Search this blog ...

Tuesday, April 19, 2011

dircolors – color ls unix directory listings – danger of overriding shell built-ins

I ran in to a crazy issue today that stemmed from a built-in function override configuration setting that I have leveraged for the best part of a decade. I’m amazed that this is the first time I actually noticed it!

I have a environment setup script for one of my installations like the following ...

export MW_HOME=/u01/app/oracle/product/Middleware
export JAVA_HOME="`ls -d ${MW_HOME}/jdk16*`"
export PATH=$JAVA_HOME/bin:$PATH

The JDK path is not hardcoded as this is a pre-release environment, and the JDK is often patched and the directory name changes as a result.

A script I was invoking had a simple test like the following:

#!/bin/sh
if [ x${PATH} != x ]; then
    PATH=$ORACLE_HOME/bin:$PATH
fi

When I ran the shell script from plain vt100 terminal, everything worked perfectly.  When I fired the script up from an xterm running inside VNC, I hit an error like the following:

./test.sh: line 2: [: too many arguments

The reason for the error, the string JAVA_HOME ended up containing ANSI characters when run from xterm.

For example, from an XTERM

echo `ls -d /tmp` > file.txt

file.txt contained ... ^[[00m^[[00;34m/tmp^[[00m ^[[m

, whereas from the VT100 terminal:

echo `ls -d /tmp` > file.txt

file.txt contained ... /tmp

It turns out, I had a zsh function setup-for/overriding the ls command so as to use color listings when invoked from an xterm by passing the --color option.  The function was as follows:

ls ()
{
  [[ $TERM = (*xterm*|ansi|linux|dtterm) ]] && set -- --color "$@"
  command ls -v "$@"
}

The fix was to change the --color option to be --color=auto so that the ANSI escape sequences were only output if the standard output is a terminal (but not for things like scripts etc).

ls ()
{
  [[ $TERM = (*xterm*|ansi|linux|dtterm) ]] && set -- --color=auto "$@"
  command ls -v "$@"
}

Moral of the story, be extremely careful using shell functions to override standard built-in commands, and make sure you understand the impact of switches!

Update

So my good mate and colleague Dragos states:

You should not rely on 'ls' (or any other command), as it can be an alias or function to anything else.

The fix is in your environment script to use /bin/ls or =ls, yes "equal sign ls"

There's a way to force filename expansion too:

JAVA_HOME=(${MW_HOME}/jdk16*)

No comments:

Post a Comment