I have a shell script, the arguments of which are collected in quote as a single argument and passed to a perl script.

/usr/local/API/check_api.sh "-D x.x.x.x -C Test_Internal_Cluster -u user -p pass  -i 300 -l runtime -s list"

which result to

/usr/bin/perl /usr/local/check_api.pl -D x.x.x.x -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list

I want to give an argument like this:

"Test Internal Cluster"

to the perl script.

When I am using

"Test\ Internal\ Cluster"

as the argument to the shell script its parsing as

Test Internal Cluster

to the perl script, but I want that to be in double quotes.

If I am using this:

""\"Test Internal Cluster\"

It converting to:

'"Test' Internal 'Cluster"'

This seems simple but not able to use proper quoting and escaping to achieve.

The shell script is:

#!/bin/bash -xv


if [ $# -gt 0 ]; then
  if [ -x $Api ]
        output=$($Perl $Api $1)
        F1=$(echo $output | awk -F':' '{print $1}')
        F2=$(echo $output | awk -F':' '{print $2}' | awk -F'|' '{print $1}')
        F3=$(echo $output | awk -F':' '{print $2}' | awk -F'|' '{print $2}')
        echo " $F1: $F3 | $F2"
        echo "check_api.pl not found"
 exit $exitstatus
    echo "Usage : $0 '-D <DC> -C <Cluster> -u <userbame> -p <password> -i <interval> -l <command>  -s <subcommand>'"

Here you can see the whole argument passed to the shell script is collected to $1 and passed to the check_api perl script.

The issue is happening with cluster name with space (passed to -C).

And the whole argument passed to the shell script should be in single/double quotes.

I haven't quoted the variable $1 because the perlscript is not accepting the whole argument as a single as below which I found in debug mode - where the whole argument is quoted if $1 is quoted.

/usr/bin/perl /usr/local/check_api.pl '-D x.x.x.x -C Test_Internal_Cluster -u user -p pass  -i 300 -l runtime -s list'

and throws error as incorrect usage

But without quoting the $1 the argument passed to perl script works like this and which gives me the result:

/usr/bin/perl /usr/local/check_api.pl -D x.x.x.x -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list

But I am failed with a cluster with space in its name.

In case of a space in cluster name if I am running in the command line directly(not from shell script) it works fine without any issue:

/usr/bin/perl /usr/local/check_api.pl -D x.x.x.x -C "Test Internal Cluster" -u user -p pass -i 300 -l runtime -s list

which I wanted to achieve from shell script.

  • 1
    Wrap it in single quotes? '"Test Internal Cluster"'
    – jasonwryan
    Feb 25, 2014 at 7:00

5 Answers 5


You should use like this:

"\"Test Internal Cluster\""

or wrap it in single quote:

'"Test Internal Cluster"'



echo $1


% bash test.sh "\"Test Internal Cluster\""
"Test Internal Cluster"


The problem here is becasue you call perl with $1, $1 contains white space, so perl will see it as three separate arguments.

To prevent this, you should wrap $1 in double quotes "$1".

Another note is you should handle $1 argument in perl script, because if $1 contains any special characters, it will cause some bugs in perl program. you can use quotemeta($ARGV[0]).


I think you shoul not pass all argument as a string to shell script. You should pass them as normal, except cluster name, you still have to wrap it as string. Then pass them to perl script by "$@" instead of $1.

  • Hi its ok with echo echo. But when I am using it as argument to shell script : in debug mode I found up to '"Test' and rest are broken .
    – kumarprd
    Feb 25, 2014 at 7:16
  • Please give your shell script.
    – cuonglm
    Feb 25, 2014 at 7:17
  • shell script updated with question.
    – kumarprd
    Feb 25, 2014 at 7:25
  • Update answer for this problem.
    – cuonglm
    Feb 25, 2014 at 7:43
  • 1
    How you pass arguments to shell script?
    – cuonglm
    Feb 25, 2014 at 9:16

I'm having a little trouble following your question, and I think that both your concrete problem and the odd phrasing of your question stem from the same root, which is an incorrect model of how command argument parsing works.

A command does not receive a string as an argument, but a list of strings. All the quoting is performed by the shell.

If you run any of the following shell commands

mycommand "foo bar" wibble
mycommand 'foo bar' 'wibble'
mycommand ''''foo" "bar \wib\ble
mycommand       foo\ bar \

(or a myriad variants) then the command is executed with exactly the same arguments in all cases:

  • argument 0 is mycommand;
  • argument 1 is the 7-character string foo bar;
  • argument 2 is the 6-character string wibble.

The shell construct $1 does not mean “take the value of parameter 1”. It means “take the value of parameter 1, split it into whitespace-separated pieces, and interpret each piece as a wildcard pattern and replace it by the names of matching files”. Yes, it's quite a mouthful. As a general shell programming principle, always put double quotes around variable substitutions and command susbtitutions: "$1", "$some_variable", "$(some_command)".

Here, two wrongs make a right, but only in a narrow case: you pass a single argument to the shell script, and split is into whitespace-separated pieces to turn it into a list of strings to pass as arguments to the Perl script. This is an unintuitive and limited interface. It doesn't allow you to have spaces in the arguments to the Perl script, since spaces are already used to separate arguments. You can't have it both ways. Quotes are of no help here: you can pass a quote character in the argument to the shell script, and it'll become part of an argument to the Perl script. For example, if you run one of

check_api.sh "-C \"Test Internal Cluster\""
check_api.sh '-C "Test Internal Cluster"'
check_api.sh -C\ \"Test Internal Cluster\"

then the shell script is called with a single argument -C "Test Internal Cluster", and the Perl script is called with the 4 arguments -C, "Test, Internal and Cluster".

The fix is simple, but it does require changing the calling convention for the shell script. What you have now is a bug, you need to fix it. Call the script the normal way:

/usr/local/API/check_api.sh -D x.x.x.x -C Test_Internal_Cluster -u user -p pass  -i 300 -l runtime -s list
/usr/local/API/check_api.sh -D x.x.x.x -C 'Test Internal Cluster' -u user -p pass  -i 300 -l runtime -s list
/usr/local/API/check_api.sh -D x.x.x.x -C "Test Internal Cluster" -u user -p pass  -i 300 -l runtime -s list

(the last two are equivalent, since single and double quotes only make a difference when \$` appear inside7), and fix the shell script to use double quotes around command substitutions.

if [ -x "$Api" ]
    output=$("$Perl" "$Api" "$1")
    F1="${output%%:*}"; output="${output#*:}"
    F2="${output%%\|*}"; output="${output#*\|}"

I've replaced the awk code by equivalent shell constructs, assuming that the output is a single line.


The problem is in the following line:

output=$($Perl $Api $1)

You need to quote the argument:

output=$($Perl $Api "$1")

When you don't quote the variable, word-splitting causes perl to see multiple arguments.

  • 4
    You need to quote all the variables (output=$("$Perl" "$Api" "$1") and [ -x "$Api" ]...). Leaving a variable unquoted is the split+glob operator in shells. It makes no sense to use that operator here. Feb 25, 2014 at 8:14

The problem, really, is that you should be doing proper option parsing. It looks like maybe you don't know how, but there's no shame in that.

Using bash's built in option parsing, and properly quoting them along the way will make it all work like you want it to, meaning that you can quote it normally on the command line. No need to re-quote or escape anything.



usage() {
    echo "Usage : $0 -D <DC> -C <Cluster> -u <userbame> -p <password> -i <interval> -l <command>  -s <subcommand>"
    exit 1

while getopts "C:D:u:p:i:l:s:" options; do
    case $options in
        C ) CLUSTER="${OPTARG}";;
        D ) DC="${OPTARG}";;
        u ) USER="${OPTARG}";;
        p ) PASS="${OPTARG}";;
        i ) INTERVAL="${OPTARG}";;
        l ) COMMAND="${OPTARG}";;
        s ) SUBCOMM="${OPTARG}";;
        * ) usage ;;

$PERL $API -C "$CLUSTER" -D "$DC" -u "$USER" -p "$PASS" -i "$INTERVAL" -l "$COMMAND" -s "$SUBCOMM"

It looks like you're doing some other stuff with the output, which I'll leave to you to append to this example.

  • Initially I started with getopts and its there already with a lot of options. I know very well the use of getopts. But I was advised to make it short with this approach. And now its already in production and issue arised when someone created a cluster with space. So instead of changing inside the code, if with passing argument with quotes and escapes it can be corrected, it will better, thats why I asked it here.
    – kumarprd
    Feb 25, 2014 at 9:37
  • Well, I can't account for someone else giving you bad advice. What you're doing is both difficult to understand and prone to error. You're directly experiencing both. You don't seem to be saving any time or effort in trying to hack it. I urge you to push back against whoever gave you that advice.
    – bahamat
    Feb 25, 2014 at 9:44

The problem is that you try to use a single variable to store a list of strings:

I have a shell script, the arguments of which are collected in quote as a single argument and passed to a perl script.

That doesn't work. Once you concatenate several arguments and get a single string, the distinction between spaces within arguments and spaces between arguments is lost.There is no way to fix this by including quoting characters within the string, because, simply said, quoting characters lose their magic as soon as they are part of a string. A backslash or " within a string is an ordinary character; it doesn't quote anything.

Solution: Use an array variable to collect the arguments:

A+=(-D "$address")
A+=(-C "$clustername")
A+=(-u "$username")
A+=(-p "$password")
output=$("$Perl" "$Api" "${A[@]}")

(note quoting.)

Of course, if the arguments that you want to pass to the perl program are just all the command line arguments that are passed to your script, then they are already contained in an array (namely the list of command line arguments "$@"), so you can simply use that one:

output=$("$Perl" "$Api" "$@")

You must log in to answer this question.

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