Background:
I'm working on quickly calling bash command line expressions inside of SGE's job submission program qSub in parallel. While doing so, I was attempting to submit an expression (as an argument) to be ran inside of another script like so:
./runArguments.sh grep foo bar.txt > output.txt
runArguments.sh looks like this:
#!/bin/bash
${1} ${2} ${3} etc....to 12
The idea is that I want "grep foo bar.txt > output.txt" to be evaluated in the script...NOT ON THE COMMAND LINE. In the example above, "grep foo bar.txt" will evaluate during runArguments.sh execution, but the output redirection would be evaluated on the command line. I eventually found a working solution using "eval" that is shown below, but I do not understand why it works.
Question(s)
1) Why does
./runArguments.sh eval "grep foo bar.txt > output.txt"
allow the eval and the expression to be taken as arguments, but
./runArguments.sh $(grep foo bar.txt > output.txt)
evaluates on the command line before the script is called? (the output of $(grep...) is taken as the arguments instead)
2) Is there a better way of doing this?
Thanks in advance!
Your first question is a bit hard to answer, because you've already answered it yourself. As you've seen, command substitution (the
$(...)
or`...`
notation) substitutes the output of the command, and then processes the result. For example, this:gets converted to this:
So in your case, this:
runs
grep foo bar.txt > output.txt
, grabs its output — which will be nothing, since you've redirected any output tooutput.txt
— and substitutes it, yielding:(so your script is run with no arguments).
By contrast, this:
does not perform any command substitution, so your script is run with two arguments:
eval
, andgrep foo bar.txt > output.txt
. This command inside your script:is therefore equivalent to this:
which invokes the
eval
built-in with five arguments:grep
,foo
,bar.txt
,>
, andoutput.txt
. Theeval
built-in assembles its arguments into a command, and runs them, and even translates the>
andoutput.txt
arguments into an output-redirection, so the above is equivalent to this:. . . and you already know what that does. :-)
As for your second question — no, there's not really a better way to do this. You need to pass the
>
in as an argument, and that means that you need to useeval ...
orbash -c "..."
or the like in order to "translate" it back into meaning output-redirection. If you're O.K. with modifying the script, then you might want to change this line:to this:
so that you don't need to handle this in the parameters. Or, actually, you might as well change it to this:
which will let you pass in more than twelve parameters; or, better yet, this:
which will give you slightly more control over word-splitting and fileglobbing and whatnot.