如何使用变量格式存储 printf 的输出?
How to store output from printf with formatting in a variable?
我想将 printf
的输出与格式一起存储在一个变量中,但由于某种原因它剥离了格式。
这是正确的输出
$ printf "%-40s %8s %9s %7s" "File system" "Free" "Refquota" "Free %"
File system Free Refquota Free
现在格式消失了
$ A=$(printf "%-40s %8s %9s %7s" "File system" "Free" "Refquota" "Free %")
$ echo $A
File system Free Refquota Free %
echo
将按顺序打印它的每个参数,以 space 分隔。您正在将一堆不同的参数传递给 echo
.
简单的解决方法是引用$A
:
A=$(printf "%-40s %8s %9s %7s" "File system" "Free" "Refquota" "Free %")
echo "$A"
这是因为您没有引用变量。如果这样做,格式将完美显示:
echo "$A" #although $a would be best, uppercase vars are not good practise
也就是说,你的var=$(printf )
方法完全没问题,你只是没能正确echo
。
你可能想知道为什么。在 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 "" "" ""
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.
我想将 printf
的输出与格式一起存储在一个变量中,但由于某种原因它剥离了格式。
这是正确的输出
$ printf "%-40s %8s %9s %7s" "File system" "Free" "Refquota" "Free %"
File system Free Refquota Free
现在格式消失了
$ A=$(printf "%-40s %8s %9s %7s" "File system" "Free" "Refquota" "Free %")
$ echo $A
File system Free Refquota Free %
echo
将按顺序打印它的每个参数,以 space 分隔。您正在将一堆不同的参数传递给 echo
.
简单的解决方法是引用$A
:
A=$(printf "%-40s %8s %9s %7s" "File system" "Free" "Refquota" "Free %")
echo "$A"
这是因为您没有引用变量。如果这样做,格式将完美显示:
echo "$A" #although $a would be best, uppercase vars are not good practise
也就是说,你的var=$(printf )
方法完全没问题,你只是没能正确echo
。
你可能想知道为什么。在 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 variablefoo
”. 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 listfoo
,*
,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 finallybar
. If the current directory is empty, the result isfoo
,*
,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"" "" ""
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.