Saturday 2 June 2012

How to remove the last command line argument in a bash script

In a bash wrapper script I needed to pass a bunch of arguments to the program. No problem, I'll do that with "$@". Then I had a new requirement: if the last argument is of the form *.ppm, I want the stdout of the program to go to this file. But any previous arguments, i.e. options, should be passed to the program. So it boiled down to this:
if nth argument matches *.ppm
  program "1st arg" .. "n-1th arg" > "nth arg"
else
  program "arguments"
To get the last element of an array, you can do ${argv[-1]}. Oops, you cannot do this with ${@[-1]}. So we have to make a copy in a local variable first:
declare -a argv=("$@")
declare file=${argv[-1]}
But we still have to remove the last argument of $@. We can't set it to the empty string, it still exists and will be seen by the program as a empty string. No we can't alter $@, so we have to use unset on argv. But this doesn't work:
unset argv[-1]
Ok, so we have to get the index of the last argument. This is one less than the length of the array, which is ${#argv[@]} (the reason for [@] is the quirky bash syntax for referring to the whole array). So we have to use $(()) to do arithmetic.
declare argc=$((${#argv[@]}-1))
So, putting it all together, the code looks like this:
declare -a argv=("$@")
declare argc=$((${#argv[@]}-1))
declare file=${argv[$argc]}
unset argv[$argc]
Then in the if branch we can write:
program ... ${argv[@]} > "$file" 
Whew!


There is a similar idea in the pop operation here, where we replace the contents of argv with all but the last element, using the subarray notation.

No comments:

Post a Comment