How to store output from printf with formatting in

2019-05-19 16:22发布

问题:

This question already has an answer here:

  • I just assigned a variable, but echo $variable shows something else 6 answers

I would like to store the output of printf with formatting in a variable, but it strips off the formatting for some reason.

This is the correct output

$ printf "%-40s %8s %9s  %7s" "File system" "Free" "Refquota" "Free %"
File system                                  Free  Refquota   Free 

And now the formatting is gone

$ A=$(printf "%-40s %8s %9s  %7s" "File system" "Free" "Refquota" "Free %")
$ echo $A
File system Free Refquota Free %

回答1:

echo will print each of it's arguments in order, separated by one space. You are passing a bunch of different arguments to echo.

The simple solution is to quote $A:

A=$(printf "%-40s %8s %9s  %7s" "File system" "Free" "Refquota" "Free %")
echo "$A"


回答2:

This is because you are not quoting the variable. If you do, the format will show perfectly:

echo "$A"   #although $a would be best, uppercase vars are not good practise

That is, your var=$(printf ) approach is completely fine, you just fail to echo properly.

You may want to know why. Find it in Why does my shell script choke on whitespace or other special characters?

Why do I need to write "$foo"? What happens without the quotes?

$foo does not mean “take the value of the variable foo”. It means something much more complex:

  • First, take the value of the variable. * Field splitting: treat that value as a whitespace-separated list of fields, and build the resulting list. For example, if the variable contains foo * bar ​ then the result of this step is the 3-element list foo, *, bar.
    • Filename generation: treat each field as a glob, i.e. as a wildcard pattern, and replace it by the list of file names that match this pattern. If the pattern doesn't match any files, it is left unmodified. In our example, this results in the list containing foo, following by the list of files in the current directory, and finally bar. If the current directory is empty, the result is foo, *, bar.

Note that the result is a list of strings. There are two contexts in shell syntax: list context and string context. Field splitting and filename generation only happen in list context, but that's most of the time. Double quotes delimit a string context: the whole double-quoted string is a single string, not to be split. (Exception: "$@" to expand to the list of positional parameters, e.g. "$@ is equivalent to "$1" "$2" "$3" if there are three positional parameters. See What is the difference between $* and $@?)

The same happens to command substitution with $(foo) or with `foo`. On a side note, don't use `foo`: its quoting rules are weird and non-portable, and all modern shells support $(foo) which is absolutely equivalent except for having intuitive quoting rules.

The output of arithmetic substitution also undergoes the same expansions, but that isn't normally a concern as it only contains non-expandable characters (assuming IFS doesn't contain digits or -).

See When is double-quoting necessary? for more details about the cases when you can leave out the quotes.

Unless you mean for all this rigmarole to happen, just remember to always use double quotes around variable and command substitutions. Do take care: leaving out the quotes can lead not just to errors but to security holes.