Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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
up vote 5 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 '14 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

Ok, more explanashons:

$ cat /tmp/test.sh
#! /bin/bash

echo '$@='"$@"

set -v # "debug"

sh -c 'echo $@' "$@" # gremlins steal the first option

sh -c 'echo $@' -- "$@" # We defend the option with two knifes

$ bash -e /tmp/test.sh first second third
$@=first second third

sh -c 'echo $@' "$@" # gremlins steal the first option
second third

sh -c 'echo $@' -- "$@" # We defend the option with two knifes
first second third
share|improve this answer
#! /bin/bash
sh -c 'other_tool -a -b "$@"' -- "$@"
share|improve this answer
2  
Welcome to Super User!  We’re looking for long answers that provide some explanation and context.  Don’t just give a one-line answer; explain why your answer is right, ideally with citations.  Answers that don’t include explanations may be removed. – G-Man May 9 '15 at 6:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .