1

In the Bash Pitfalls on "Greg's Wiki" I found the following quote:

In fact, the echo command cannot be used with absolute safety here. If the variable contains -n for example, echo will consider that an option, rather than data to be printed. The only absolutely sure way to print the value of a variable is using printf:

printf "%s\n" "$foo"

But when I type:

var="-n Hello"
echo "$var"

It works properly and doesn't interpret -n as an option, so I don't understand why the source I quoted says that we need to use printf to be absolutely safe?

AdminBee
  • 22,803

1 Answers1

6

In your example, it worked correctly "by chance". The reason that is that

  • you have provided only one argument to echo, and
  • you have quoted your variable as is recommended.

That means that echo sees only one string as command-line argument, which is -n Hello, including the space in between the "words". Now, echo (well, some echos) is programmed such that it recognizes -n as an option, but not the string -n<space>Hello, so it will assume that this is an operand - i.e. a string you want to print "as is" (well, possibly with \ sequences expanded).

Had you used

echo $var

without quotes instead, and with the default value of $IFS, then $var would have been split at the space, and echo would have seen two arguments: -n - which it will interpret as an option - and Hello.

This means that in this case, correctly quoting your shell variables was a remedy against involuntarily running into a situation where echo interprets an operand as an option.

However:

  1. Remember that the example in your linked source was printing a list of files via "glob expansion" (*.zip), where the variable must be used unquoted, so it is not always possible to use that guard.

  2. Quoting the variables can only help in cases where word-splitting would lead to echo seeing an option-argument. If the argument to be printed is a single -n to begin with, quoting cannot help you.

    Consider e.g. a case where you have collected an array of strings that you want echo to print, and one of the strings happens to also be an option argument to echo:

    arguments=( "-n" "Hello" )
    

    Then, even if you correctly quote the variable, as in

    echo "${arguments[@]}"
    

    the first token echo sees will still be -n which it interprets as option, rather than an operand. The same is true if you simply have several variables as arguments to pass to echo, and the first one turns out to be or start with -n. Here, too, quoting the variables cannot help you.

  3. In the general case, even quoting will not help you if a program accepts arguments in the form -oValue.

    As a hypothetical case, take a program my_formula_renderer that typesets mathematical formulae and that accepts an option parameter -sfontsize. Then, imagine you want to typeset the formula -sqrt(a^2 + b^2), as in

    ./my_formula_renderer "-sqrt(a^2+b^2)"
    

    If your program doesn't have a -- specifier that ends options parsing, it will think that you tried to typeset "nothing" with a font size of qrt(a^2+b^2), rather than the above formula with the default font size.

    It wouldn't even help in cases where you have explicit space between the -s and the further formula elements, as in "-s +2t +u^2"; it might simply mis-interpret this as a "font size" of +2t +u^2 with leading space (see e.g. this question where this became a problem).

  4. The same is true if a program accepts "clustering" of single-letter options.

    As noted by @Stéphane Chazelas, echo for example could interpret a string -neEenennne as a concatenation of the options -n, -e and -E, so trying to print that string via echo could fail/behave in an unexpected way.

  5. Beside option processing, echo (some echos, including bash's when the xpg_echo option is enabled which it is by default in some builds) does expand \n, \\, \b and so on into a newline, backslash, backspace character, so can't be used to output verbatim strings that may contain backslashes.

TL/DR

The key point is the term "absolutely" in the source you quoted, and the fact that it refers to echo specifically. In most cases, echo will be fine. But if you start relying on that blindly, there will (sooner rather than later) be the situation in which echo, or indeed another, more critical program you call, inadvertently mis-interprets something you intended to be an "operand" parameter as an "option" argument, usually when you pass it as a shell variable.

AdminBee
  • 22,803
  • Someone obviously found something wrong with this answer. I would be interested to learn what it is ... – AdminBee Sep 02 '22 at 12:44