Reputation: 11936
I want to pass parts of commands around in bash so that:
commandpart=( --flags "filename" >/dev/null )
command "${commandpart[@]}"
Will evaluate to:
command --flags "filename" >/dev/null
This has previously worked great for me, unfortunately putting a >
inside a bash array causes a syntax error.
syntax error near unexpected token `>'
`commandpart=( --flags "filename" >/dev/null )'
Other things I've tried are sticking it all into a string and eval
ing it:
commandpart="--flags filename >/dev/null"
eval "command $commandpart"
But this presents problems with escaping, spaces, etc. (And eval is not exactly a best practice)
Preferably, how do I fix the syntax error on the first example without escaping characters like <>&
? I'd like to continue using this array syntax as it seems to be far more robust than eval and doesn't need escaping.
If not, is there another method for moving parts of commands around that would fit my needs?
Bonus points: If I pass arguments to my script to be executed like this:
#!/bin/bash
echo "script running"
command "${@}"
I have a new problem, adding the >/dev/null
to the script call applies it to the script, not the command:
$ script >/dev/null
While escaping it applies it to the command, but as strings which don't have the intended effect. (eg if the command was firefox it would open firefox at a page called '>/dev/null' and promptly 404)
Upvotes: 2
Views: 221
Reputation: 5275
From bash(1):
A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections, and terminated by a control operator. The first word specifies the command to be executed, and is passed as argument zero. The remaining words are passed as arguments to the invoked command.
This means that redirections are not part of command line. They are interpreted by bash as part of description for "environment" the command will run in, on par with assigning environment variables that precede it.
From the same bash(1):
Expansion is performed on the command line after it has been split into words.
This hints at the fact that by the time parameter expansion ("${commandpart[@]}" in your case) happens, words have already been separated from redirections. As this unlikely to happen twice, that means that results of parameter expansions are not interpreted as redirections.
You can make this work by making a function that would quote array elements which are not redirections, following bash quoting rules, and then pass them to eval as arguments. However, that function would have to be able to distinguish redirections from "words" the same way bash does, and there are many kinds. This also might require quoting parts of some kinds of redirections.
I would suggest not to store/pass redirections, but instead apply them in place of invocation, as necessary. If you always need some redirections with some commands, make wrapper functions or scripts.
Upvotes: 4
Reputation: 45536
You will want to read I'm trying to put a command in a variable, but the complex cases always fail! on why this is a problem and will not really work the way you want it to.
Upvotes: 2