The desired outcome
Is there a way to use a string that contains the arguments to call a script
?
str_params="this/one 'that one' and \"yet another\""
The function below prints feedback on the stdout
on how the arguments were received:
display_args () {
all_args=("${0}" "${@}")
for i in "${!all_args[@]}"; do
printf " $%d is '%q'\n" "${i}" "${!i}"
done
}
The desired result would be the below, where array expansion is use instead (see ary_params
is used, in split of str_params
):
ary_params=(this/one 'that one' and "yet another")
display_args "${ary_params[@]}"
$0 is 'bash'
$1 is 'this/one'
$2 is 'that\ one'
$3 is 'and'
$4 is 'yet\ another'
The Problem
The first attempt using str_params
shows that the string is split by blank space when transformed into arguments, regardless of the single and double quotes:
$ display_args ${str_params}
$0 is 'bash'
$1 is 'this/one'
$2 is '\'that'
$3 is 'one\''
$4 is 'and'
$5 is '\"yet'
$6 is 'another\"'
The second attempt throws an error that am still trying to understand. Does bash
really try to find a file called all that string?
$ cmd="display_args ${str_params}"
$ echo "${cmd}"
display_args this/one 'that one' and "yet another"
$ "$cmd"
bash: display_args this/one 'that one' and "yet another": No such file or directory
- And calling with just
$cmd
(with no double quotes) brings us back to the result of the first attempt (further up).
Research
There is somehow good documentation and posts around this particular problem. Just to mention some:
This FAQ suggests building an array with the arguments (as mentioned in this question) and expanding its elements, some caveats around parameter expansion, the
IFS= read -r
ramblings and some other unrelated example. Digging in a bit on passing array arguments to bash scripts, this answer clearly reflects that, but for some work around that may work in some circumstances, this is not a well supported approach (it also refers to the bash author's quote: there isn't really a good way to encode an array variable into the environment).This answer (bash splitting line with quotes into parameters), clearly reflects the difference on how the shell treats commands vs strings. The shell performs quote removal as the last step, before executing the command. In contrast, when interpreting the
str_params
variable, the shell treats the quotes as just another character and, as a consequence, they are not subject of quote removal. Most surprisingly, in another answer of the same post, there seems to be some example to re-write the arguments list (whether or not that can be a universal approach).This other answer (how to iterate over arguments in bash script) explains that the shell processes quotes before it expands variables, and to make the shell paying attention to the quotes in the variable (
str_params
andcmd
in the examples above) it is required to useeval
. However the use ofeval
seems rather a risky approach and I am still doubtful there aren't other solutions for something that, in principle, seems easier than this.
Using eval
By following the examples above, I gave a try to eval
and seems to work:
$ echo "$cmd"
display_args this/one 'that one' and "yet another"
$ eval $cmd
$0 is 'bash'
$1 is 'this/one'
$2 is 'that\ one'
$3 is 'and'
$4 is 'yet\ another'
$ eval display_args $str_params
$0 is 'bash'
$1 is 'this/one'
$2 is 'that\ one'
$3 is 'and'
$4 is 'yet\ another'
The Questions
- Is there a way to use a string containing the arguments we will use to pass them to a
script
without the use ofeval
? - Alternatively, is there a straight forward way to transform that string into an
Array
of parameters seemingly to whatbash
would do? (this would allow to just call by extending the elements of the array, as mentioned above this lines).