Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have a wrapper script that does some work and then passes the original parameters on to another tool:

#!/bin/bash
# ...
other_tool -a -b "$@"

This works fine, unless the "other tool" is run in a subshell:

#!/bin/bash
# ...
bash -c "other_tool -a -b $@"

If I call my wrapper script like this:

wrapper.sh -x "blah blup"

then, only the first orginal argument (-x) is handed to "other_tool". In reality, I do not create a subshell, but pass the original arguments to a shell on an Android phone, which shouldn't make any difference:

#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"
share|improve this question

4 Answers 4

up vote 4 down vote accepted

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:

#!/bin/bash

quoted_args="$(printf " %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 " %q" "$@")"

share|improve this answer

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
share|improve this answer
    
Unfortunately, this doesn't work either. If you call the wrapper like this: wrapper.sh -x "blah blup" then the subshell gets THREE parameters (-x, blah, blup) instead of TWO (-x, "blah blup") –  Ralf Holly Mar 21 '12 at 15:17
    
You need to "double-escape" the argument if you want the space separator to be preserved, i.e. "Blah\ blup". Try it and see if it works. –  Daniel Andersson Mar 21 '12 at 15:22
    
It looks like it works, however, this would require the caller of wrapper.sh to add escapes, and I'm looking for a transparent solution. –  Ralf Holly Mar 21 '12 at 15:29

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[*]}
share|improve this answer
    
also useful if you're trying to concatenate $@ as part of an eventual quoted string argument like '/bin/foo '"$@"' --opt' and discovering that it just doesn't come out as you'd expect. –  jrg Jun 17 at 11:22

It's failing because you're coercing an array (the positional parameters) into a string. "$@" is magical because it gives you each separate paramter as a properly quoted string. Adding additional text breaks the magic: "blah $@" is just a single string.

This may get you closer:

cmd="other_tool -a -b"
for parm in "$@"; do cmd+=" '$parm'"; done
adb sh -c "$cmd"

Of course, any parameter that contains a single quote will cause trouble.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.