6

What needs to be done to ensure that a parameter containing embedded spaces is handled correctly when used within a command substitution? Perhaps an example will illustrate

$ file="/home/1_cr/xy z"
$ basename $file
xy

$ #need to pass basename a quoted $file to prevent the z in 'xy z' from being eaten
$ basename "$file"
xy z

$ #Now using $file inside a command substitution without quoting.....
$ #....the command substitution causes 'xy z' to split
$ set -- $(basename "$file")
$ printf "%s\n" "$@"
xy
z

$ #But quoting the command substitution does not prevent 'xy z'...... 
$ #....from being split before being passed to basename
$ set -- "$(basename $file)"
$ printf "%s\n" "$@"
xy

What do i need to do so that

$ set -- $(basename $file)
$ printf "%s\n" "$@"

yields

xy z   
4
  • 1
    set -- "$(basename "$file")"? At least this works on my system.
    – Joseph R.
    Nov 12, 2013 at 22:40
  • @JosephR. lol...and I thought i would have to do something a lot more complicated. Do you know how the word-splitting plays out with your solution?
    – iruvar
    Nov 12, 2013 at 22:43
  • The internal quotes simply protect $file from word expansion when passed to basename as in your own example. Just because you quoted the command substitution doesn't mean everything inside it is immune to word expansion.
    – Joseph R.
    Nov 12, 2013 at 22:47
  • @JosephR. thanks! See my comment against Gilles answer
    – iruvar
    Nov 13, 2013 at 0:48

2 Answers 2

5

You need to put double quotes around all variable and command substitutions.

set -- "$(basename -- "$file")"

If you allow the value of the variable to be split into words and these words to be treated as glob patterns, there's no going back. You can no longer tell how much whitespace was there or whether the file names were the result of a wildcard expansion. You can't turn the hamburger back into a cow, so you must make sure not to send the cow to the abattoir in the first place.

2
  • Thanks. What threw me off was the difference in the way bash handles nested double-quotes in set -- "xy "$file"" versus set -- "$(basename -- "$file")". So when command substitution comes into play, bash is able to interpret nested quotes "naturally"?
    – iruvar
    Nov 13, 2013 at 0:44
  • 2
    @1_CR Yes (provided you use the $(…) form of command substitution, and not the obsolescent backquotes). Nov 13, 2013 at 0:51
3

You have several options,

  1. Use "$"* instead "$@"

     set -- $(basename "$file")
     printf "%s\n" "$*"
    
  2. Quote the command substitution and the variable,

     set -- "$(basename "$file")"
     printf "%s\n" "$@"
    
1
  • 4
    Really shouldn't use #1 as it can still produce unreliable results. #2 is only way :-)
    – phemmer
    Nov 12, 2013 at 23:00

You must log in to answer this question.

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