In my-script, "$1" is a string of tokens, separated by whitespace.

how can I "spread" this single argument into multiple arguments to pass to another program for example

./my-script2 ...$1  # spread the string into multiple arguments, delineating by whitepace

Hopefully you understand the question, had trouble searching for answer to this.

I tried this:

./my-script2 <<< "$1"

and this:

./my-script2 "" <<< $1

and other combinations, but that didn't seem to work. I need to support Bash v3 and above.

  • But the premise "I call a bash script like so ./my-script1 "$@" so in my-script, $1 is a string of arguments, separated by whitespace" is false, so actually I don't know what you're asking. – Michael Homer Jul 15 '17 at 2:14
  • Relevant, but essentially the opposite question: Why does my shell script choke on whitespace or other special characters? – Michael Homer Jul 15 '17 at 2:15
  • @MichaelHomer if you could put your human hat on for a minute and empathize with my struggle at the moment. I was under the impression that "$@" would take all the relevant arguments and put them into a single string, and that single string would be represented by $1 in this context. If that's not true, please ignore that, and start with the assumption that $1 is a string of tokens separated by whitespace. How can I split that up into multiple arguments that can be passed to my-script2? – Alexander Mills Jul 15 '17 at 2:21
  • Why don't you edit your post to show the actual scenario you're facing, showing where your expectation isn't met, and then someone might be able to address it. – Michael Homer Jul 15 '17 at 2:23
  • ok that I can do – Alexander Mills Jul 15 '17 at 2:27
./my-script2 $1

Unquoted parameters are subject to word splitting when expanded:

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

This is the specified POSIX behaviour, though some shells (notably zsh) disable it by default. If you've changed the value of IFS from the default, that's on you.

  • thanks, that works - but I don't understand, you said that when I quote "$@" before calling a script that it won't be all one argument stored in $1 or $2, but it should be, tmk. – Alexander Mills Jul 15 '17 at 2:57
  • Wouldn't you want to ensure set -f? – Jeff Schaller Jul 15 '17 at 3:25
  • @JeffSchaller Honestly, who even knows here. – Michael Homer Jul 15 '17 at 3:44

Abstract: A "filename expansion" safe solution (for my-script2):

IFS=' ' read -a arr <<<"$1"
printf '<%s>--' "${arr[@]}" ; echo

The naive solution of using the variable un-quoted to get it divided into words (based on IFS value) also expose the contents to filename expansion on *, ? and [ (at least).

This specific string works correctly:

$ set "a string with spaces"
$ printf '<%s>--' $1; echo

But this string clearly fails:

$ set "a bad * string with spaces"
$ printf '<%s>--' $1; echo

The asterisk got expanded into the files in PWD.


One way (very often overlooked but also very useful) to get variable expansion without "pathname expansion" is to use a here-document.

IFS=' ' read -ra arr <<-_EOF_
 printf '<%s>--' "${arr[@]}" ; echo;

As explained in the manual:

If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion, … …

Please note that "pathname expansion" is not applied.

The cousin of a here-document is the here-string, which makes the code shorter:

IFS=' ' read -ra arr <<<"$1"
printf '<%s>--' "${arr[@]}" ; echo;

Just remember that (as inside double quotes) the characters \, $, and ` are special.
From the manual:

the character sequence \ is ignored, and \ must be used to quote the characters \, $, and `

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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