How to raise and take care of shell scripts: How to quote

When should I quote, when not?

A good quoting principle is: Better too much than too little. But you have to take care on HOW to quote. Quoting prevents you from errors caused by blanks, tabs and other nasty parts of values. Unfortunally there are some situations, when quoting is not applicable or unnecessary. I don't want to explain all quoting rules here - this is done in many tutorials, books or even in the shell manuals. I want to show some examples, which use a kind of special way to work with quotes, and to describe some rules how to protect quotes against themselfes.

In some cases quotes are unnecessary - for instance if you use variables, from whom you know there are defined and numeric:

Other situations may request to use no quotings. The next example uses the leak of quotes to fill an array:

  line="field1 field2 field3 field4"
  declare -a fld_arr
  fld_arr=($line)     # do not quote here to be able to access every single field
  for (( i=0; i < ${#fld_arr[*]}; i++ )); do
    echo $i ${fld_arr[$i]}
  done

The ' quoting sign (single quote) allows you to prevent every character in your text from being interpreted by the shell. But what if your text contains this character? It can't be escaped because no escape characters are evaluated within the single quotes. It would be the end of your quoted text every time. In the next samples I use a modified version of some of my replies in a web forum's thread:

  # single quote embedded in a text
  jan@jack:~/tmp> echo '123'(456\('
  bash: syntax error near unexpected token `('
  # try to escape the single quote
  jan@jack:~/tmp> echo '123\'(456\('
  bash: syntax error near unexpected token `('
  # so it works: the single quote is placed outside the quoted text
  # and is hided from being interpreted using double quotes
  jan@jack:~/tmp> echo '123'"'"'(456\('
  123'(456\(

How one can use this method to deal with variable, unknown content? I created a directory containing some very nasty file names - in my opinion mp3 files often have such unbelievable names, some mp3 administration programs don't stop at nothing ;-)

  jan@jack:~/tmp/trash_names> ls
  123'(456\(b l a)
  123'(456\(bla)
  123 "buh" 456
  jan@jack:~/tmp/trash_names> find . -type f -printf "ls |%p|\n" |
  > sed "s/'/'\"'\"'/g;s/|/'/g" | sh
 ./123'(456\(bla)
 ./123'(456\(b l a)
 ./123 "buh" 456

If you forward these filenames simply using a ls to a loop to work with variable values (to rename the files, for instance) you'll get the problems I mentioned above. To work around it you can hide the outer single quotes using another character. The -printf option unfortunally is only available in the GNU version of the find command. I use the pipe character as replacement - of course this character may not be found within the file names.
Next the file names go through a sed laundry. Now ervery single quote will be replaced by the character sequence '"'"', at last the escape pipe sign we used in the first step is re-replaced by the single quote - now we can use the file names.

Sometimes other quote characters must be hided too - for instance when working with encapsulated commands: How to deal with commands, which use the output of other commands using some other command's output (onion-skin commands ;-)? The sample you see below calculates the next working day in the format "Monday, 21. of July" and displays it. The construct intensively uses the options available in the GNU version of the date command.

  date +"%A, %d. of %B" -d "today +`if test \`date +%w\` -gt 4; then \
    expr 8 - \`date +%w\`; else echo 1; fi` days"

Here's a step-by-step description of the monster command parts (you should've opened the date manual in another terminal):

O.K. - we got the result. But now we want to store this value in a variable. For this we pull a new "`...`" skin on the command. Which characters we have to escape from being interpreted now? It's quite simple: All characters having a special meaning to the shell, in our case: quotes, backslash, backtick. We prepend every such character with an escape character, namly the backslash - and we're ready:

  NEXT_WDAY="`date +\"%A, %d. of %B\" -d \"today +\`if test \\\`date +%w\\\` -gt 4; then \\
    expr 8 - \\\`date +%w\\\`; else echo 1; fi\` days\"`"