When you leave a variable substitution
$var or a command substitution
$(cmd) unquoted, the result undergoes the following transformations:
- Split the resulting string into words. The split happens at whitespace (sequences of space, tabs and newlines); this can be configured by setting
IFS (see 1, 2).
- Each of the resulting word is treated as a glob pattern, and if it matches some files, the word is replaced by the list of file names.
Note that the result is not a string but a list of strings. Furthermore, note that quote characters like
" are not involved here; they are part of the shell source syntax, not of string expansion.
A general rule of shell programming is to always put double quotes around variable and command substitutions, unless you know why you have to leave them off. So in
echo "Argument 1: $1". To pass arguments to
test.sh, you're in trouble: you need to pass a list of words from
test.sh, but your chosen method involves a command substitution and that only provides the way for a simple string.
If you can guarantee that the arguments to be passed do not contain any newlines, and it is acceptable to change the invocation process slightly, you can set
IFS to contain only a newline. Make sure that
args.sh outputs exactly one file name per line with no stray quotes.
If the arguments may contain arbitrary characters (presumably except null bytes, which you cannot pass as arguments), you'll need to perform some encoding. Any encoding will do; of course, it won't be the same as passing the arguments directly: that's not possible. For example, in
\t by an actual tab character if your shell doesn't support it):
for x; do
printf '%s_\n' "$x" |
sed -e 's/q/qq/g' -e 's/ /qs/g' -e 's/\t/qt/g' -e 's/$/qn/'
for arg; do
decoded=$(printf '%s\n' "$arg" |
sed -e 's/qs/ /g' -e 's/qt/\t/g' -e 's/qn/\n/g' -e 's/qq/q/g')
# process "$decoded"
You may prefer to change
test.sh to accept a list of strings as input. If the strings don't contain newlines, invoke
args.sh | test.sh and use this in
while IFS= read -r line; do
# process "$line"
Another method that prevents the need for quoting altogether is to invoke the second script from the first.