How to pass bash script arguments to a subshell
Bash's printf
command has a feature that'll quote/escape/whatever a string, so as long as both the parent and subshell are actually bash, this should work:
[Edit: as siegi pointed out in a comment, if you do this the obvious way there's a problem when no arguments are supplied, where it acts like there actually was a single empty argument. I've added a workaround below, wrapping the format string with ${1+}
, which only includes the format string if the first argument is defined. It's a bit of a kluge, but it does work.]
#!/bin/bash
quoted_args="$(printf "${1+ %q}" "$@")" # Note: this will have a leading space before the first arg
# echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing
bash -c "other_tool -a -b$quoted_args"
Note that you can also do it in a single line: bash -c "other_tool -a -b$(printf "${1+ %q}" "$@")"
None of the solutions work well. Just pass x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\" as parameter and they fail.
Here is a simple wrapper that handles every case. Note how it escapes each argument twice.
#!/usr/bin/env bash
declare -a ARGS
COUNT=$#
for ((INDEX=0; INDEX<COUNT; ++INDEX))
do
ARG="$(printf "%q" "$1")"
ARGS[INDEX]="$(printf "%q" "$ARG")"
shift
done
ls -l ${ARGS[*]}
Change $@
to $*
. I did a small local test and it works in my case.
#!/bin/sh
bash -c "echo $*"
bash -c "echo $@"
Saving as test.sh
and making it executable gives
$ ./test.sh foo bar
foo bar
foo
There is a subtle difference between $*
and $@
, as you can see. See e.g. http://ss64.com/bash/syntax-parameters.html
For the follow-up question in the comments: you need to escape e.g. white-space "twice" to pass a string with a separator as a combined argument, e.g. with test.sh
modified to a wc
wrapper:
#!/bin/sh
bash -c "wc $*"
This works:
$ touch test\ file
$ ./test.sh -l "test\ file"
0 test file
but:
$ ./test.sh -l "test file"
wc: test: No such file or directory
wc: file: No such file or directory
0 total