1

I wrote a script today as follows:

echo "Enter a directory path"

read dir

for file in $dir/[!.]*;
    do
        f=`echo $file | sed 's/ /_/g'`
        mv "${file}" "${f}"  
    done

Initially, the mv command was written as:

mv ${file} ${f}

But that line was throwing

usage: mv [-f | -i | -n] [-v] source target
   mv [-f | -i | -n] [-v] source ... directory

I was able to use google to figure out that the variables needed to be wrapped in double quotes, but I still don't understand why doing so resolved the problem?
Thanks!

  • Call your script with "bash -x " and you will get a lot of debug output helping you figure out what happens. – Dirk Herrmann Jan 21 '16 at 23:37
  • 1
    BTW, `echo $file | sed 's/ /_/g'` is buggy for a lot of the same reasons -- but also very, very slow. Use a parameter expansion instead: `f=${file// /_}` -- it's both faster and avoids all the bugs implicit in using `echo $file` as opposed to `echo "$file"` or (even less buggy) `printf '%s\n' "$file"`. – Charles Duffy Jan 21 '16 at 23:42
  • @CharlesDuffy +1 for the optimization. That is extremely helpful and interesting. – Digital Impermanence Jan 21 '16 at 23:48
  • As another aside, `${foo}` has no advantage over `$foo` on its own. If you want to do a parameter expansion -- `${foo%bar}` or such, sure; if you need to disambiguate for a string concatenation -- `${foo}bar` -- then sure; otherwise? Purely a stylistic preference. – Charles Duffy Jan 21 '16 at 23:49
  • @CharlesDuffy I was instructed it was good practice. Thoughts? – Digital Impermanence Jan 21 '16 at 23:51
  • I learned best practice in shell at the hands of the freenode #bash channel, consensus of which is generally recorded on the wooledge wiki -- see the BashFAQ at http://mywiki.wooledge.org/BashFAQ and the BashGuide at http://mywiki.wooledge.org/BashGuide. There's no consensus there that braces are necessary when the syntax doesn't call for them, so long as you know the corner cases (the one that trips beginners up most is `$foo_bar` when you mean to only expand `$foo`, but `_` is a valid character in variable names). – Charles Duffy Jan 21 '16 at 23:56
  • ...btw, another good resource is the bash-hackers wiki at http://wiki.bash-hackers.org/; a *bad* resource, best avoided, is TLDP's ABS, which -- despite having been around for ages and having lots of Google juice -- has a penchant for showcasing bad practices in examples and only occasionally being updated. – Charles Duffy Jan 21 '16 at 23:57
  • @CharlesDuffy I never thought to use freenode as a bash education source, so thanks for that tip. Secondly, I will save bash-hackers in my favorites and study it. Finally, that is mind-blowing about tldp because, well, it does have a lot of google juice and I just assumed it was borderline scholarly...! Thanks, again. – Digital Impermanence Jan 22 '16 at 02:12

1 Answers1

5

Quoting, in shell, prevents string-splitting and glob expansion. If you don't double-quote your variables, you have no idea how many arguments each variable may expand into after these parsing steps are run.

That is:

mv $foo $bar

...may turn into...

mv ./first word second word third word destination-file

if

foo='./first word second word third word'
bar='destination-file'

Similarly, consider the case where you have a filename containing a glob expression:

foo='hello * world'

In that case, your mv command would get a list of all files in the current directory.


...or, consider the case when an argument is empty:

foo='hello world'
bar=''

In this case, instead of (properly) getting an error that you can't have file with a 0-byte name, you would be trying to rename a file named hello to world: The $bar simply disappears.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441