5

My bash script looks like this:

#!/bin/bash
set -x 

./test FLAGS=${@:2}

And when I run ./script 1 2 3 the command that is executed is ./test FLAGS=2 3. I want FLAGS="2 3".

When I change the fourth line to ./test FLAGS="${@:2}" there's no change.

When I change the fourth line to ./test FLAGS=\"{@:2}\" I get ./test 'FLAGS="2' '3"' which really isn't what I want.

How do I simply wrap my variables in quotes?

Derek
  • 51
  • This sounds like the XY problem (asking about your concept of the solution rather than your original problem you're trying to solve with that solution). Since you're using the variable name FLAGS, is it possible that what you're actually trying to do here is parse options in your script? – Wildcard Dec 24 '15 at 09:23
  • No, my script needs to execute another script using the parameters passed to it. Replace FLAGS with any other name, I chose it arbitrarily. – Derek Dec 24 '15 at 10:35
  • test is both a shell builtin and a standard utility (try type -a test): you should choose a different name for your program. – glenn jackman Dec 24 '15 at 13:51

3 Answers3

8

Use

"${*:2}"

instead of

"${@:2}"

$@, and ${x[@]}, and their variants, expand to one "word" per element in the array when quoted. $* (and ${x[*]}) expand to a single word with all the elements joined together using the first character of $IFS.

If they're not quoted at all, both versions undergo word-splitting and create multiple words split wherever any of the characters of IFS occur.

Michael Homer
  • 76,565
  • When I do FLAGS="${*:2}", it results in 'FLAGS="1 2"' how do I get rid of the wrapping single quotes? – Derek Dec 24 '15 at 10:34
  • 1
    @derek, that's just how set -x outputs things. The actual ./test program will receive a single argument but it won't have to deal with those single quotes. – glenn jackman Dec 24 '15 at 13:49
2

Based on comments, my understanding is that you want to discard (or store somewhere) the first argument, and then use the rest of the arguments as arguments to another script or command that you are calling from within your script.

If that's so, you can do it pretty easily, and you don't need to set a variable (FLAGS or anything else) to pass the script's parameters to the command within your script. See the following example:

#!/bin/bash
set -x

original_first_arg="$1"     # Use this line if you need to save the value to use later.

shift
mycommand "$@"

The shift command is a bash builtin. By itself (without giving it any number), it just throws away the script's first argument (positional parameter). From the manpage:

   shift [n]
          The  positional  parameters  from n+1 ... are renamed to $1 ....
          Parameters represented by the numbers  $#  down  to  $#-n+1  are
          unset.   n  must  be a non-negative number less than or equal to
          $#.  If n is 0, no parameters are changed.  If n is  not  given,
          it  is assumed to be 1.  If n is greater than $#, the positional
          parameters are not changed.  The return status is  greater  than
          zero if n is greater than $# or less than zero; otherwise 0.

Then "$@" expands to exact parameters you gave the script, minus the first one $1 since that was thrown away by the shift command.

Further reading:

Wildcard
  • 36,499
  • Please don't downvote without a comment. I put work into this answer. – Wildcard Dec 24 '15 at 20:19
  • The addendum just isn't correct. The documentation is a bit screwy here, but you can try it: bash -c 'set abc def ghi ; echo "${*:2}"'. – Michael Homer Dec 24 '15 at 20:21
  • You are correct, and the documentation is indeed screwy. I've removed the incorrect addendum. Mind reversing the vote? :) – Wildcard Dec 24 '15 at 20:26
2

If you want different sets of parameters based on your script's args, then you can use functions for that:

other_script(){ shift; command other_script "$@"; }
other_script "$@"

It's how I typically do it. A function can call another function, or itself, or define a new function - or redefine itself. Every function gets an array.

mikeserv
  • 58,310