2013-09-04

Passing command line options that have white spaces to a bash script: $* and $@

I always try to simplify a command line parser implementation regardless any programming language: C++, bash, ... To do that, I restrict the command line option to a regular form only. All my arguments should be

    '-arg_key value'

form. Even I want to specify a file name, my program needs an argument key, e.g., '-in_file input_filename. If you do in this way, each command line argument has the key, so I can put all the command line options to a map. This makes the command line parser simple. I usually don't need getopt library. Also I try to simplify the command line option support, means smaller number of command line options, and use a config file, which contains 'key = value' lines. This has two advantages: you can support negative values without confusing the command line option, easy to reproduce the test case.

However, this method still has a problem when the command line option includes white space. If I could, I will only try to use config files, but, in practice, it is not always the solution.

Let me show you such command line options. For instance, I want to pass a vector to my command as the following.

   command -eye_position '0 0 -10' -up_vector '0 1 0'
If I run a executable by hand from a shell, this is fine, but I usually want to run a command from a shell script like for automated tests. Here is an implementation example. test_1.sh:
  
-- test_1.sh --
echo "call with two args, but the second one has spaces."
echo "./test_2.sh args0 'args1_1 args1_2 args1_3'"
./test_2.sh args0 'args1_1 args1_2 args1_3'
-- test_1.sh --
The script test_2.sh just shows how the commend line options are passed.
-- test_2.sh --
echo "show the args with \$*."

for i in $*
do
    echo " $i"
done

echo "show the args with quoted \"\$*\"."

for i in "$*"
do
    echo " $i"
done

echo "show the args with \$@."

for i in $@
do
    echo " $i"
done

echo "show the args with quoted \"\$@\"."

for i in "$@"
do
    echo " $i"
done
-- test_2.sh --
What I want to get in the \verb|test_2.sh| is two arguments as the following:
  • $1: args0
  • $2: args1_1 args1_2 args1_3
Please note, in the second argument has while spaces. The execution result of the script is as following.
-- result --
call with two args, but the second one has spaces.
./test_2.sh args0 'args1_1 args1_2 args1_3'
show the args with $*.
 args0
 args1_1
 args1_2
 args1_3
show the args with quoted "$*".
 args0 args1_1 args1_2 args1_3
show the args with $@.
 args0
 args1_1
 args1_2
 args1_3
show the args with quoted "$@".
 args0
 args1_1 args1_2 args1_3
-- result --
If I use $* or $@ only, the white space separates the second argument, and made four arguments. To prevent this, I quote the arguments in the script,but "$*" becomes now only one argument. The "$@" is the one I wanted. This is the difference between $* and $@ of bash. This is a detail, and not necessary to know about it I Think. However, if you know it, you are a bit happier than before, I wish.

No comments: