Here's a challenge for the shell script experts, something which I have not found easy to find via Google: Is there a quote/escaping aware version of bash's $@ syntax?
I have a simple bash wrapper script where I use the $@ variable to get all the command line variables passed to the script. I'm actually using this to run BLAST on our cluster but first automatically cache databases on the local node's hard drive, and update the paths in the command line arguments to match. However, the following reduced script demonstrates the problem:
#!/bin/bash echo Timing this: $@ time $@
Save that as time_it and make it executable, and it can be used as follows:
$ ./time_it /xxx/blastp -outfmt 6 -query x.faa -db nr -task blastp -out x.tsv Timing this: /xxx/blastp -outfmt 6 -query x.faa -db nr -task blastp -out x.tsv ... real 0m0.021s user 0m0.012s sys 0m0.005s
However, a command like this fails:
$ ./time_it /xxx/blastp -outfmt "6 std score" -query x.faa -db nr -task blastp -out x.tsv Timing this: /xxx/blastp -outfmt 6 std score -query x.faa -db nr -task blastp -out x.tsv USAGE ... Too many positional arguments (1), the offending value: std ... real 0m0.026s user 0m0.012s sys 0m0.005s
Here the third argument is "6 std score", quoted to keep it as one rather than three arguments, but using $@ in the bash script lost the quote marks (as shown via the echo time, and the error message from BLAST).
I can avoid this by switching from bash to something else like Python, but I'm hoping there is a neat bash (or sh) solution instead.
can you use use quote, e.g. "$@" ?
import sys print "|".join(sys.argv[1:])
then run as:
bash time.sh python t.py a b "c and d"
timing this python t.py a b c and d a|b|c and d real 0m0.043s user 0m0.032s sys 0m0.010s
I think the problem is not with the content of $@ but rather the behavior of the last line that transforms it into a command, this below works for me:
#!/bin/bash echo Timing this: $@ time exec "$@"
invoking it as:
$ ./time_it python test.py "ONE TWO THREE" "FOUR FIVE" Timing this: python test.py ONE TWO THREE FOUR FIVE ONE TWO THREE FOUR FIVE
$ more test.py import sys print sys.argv print sys.argv
Could you not call it in the following manner? This way you "escape" the quotes (this is probably the Google search term you wanted).
$ ./time_it /xxx/blastp -outfmt \"6 std score\" -query x.faa -db nr -task blastp -out x.tsv
Thereby you program is correct, just the way you use bash to call a bash script changes.