The*_*eer 4 shell bash quoting printf
我正在尝试获取envshell 变量中的输出并打印它。
#!/bin/sh
ENV_BEFORE=$(env)
printf $ENV_BEFORE
Run Code Online (Sandbox Code Playgroud)
结果,env输出中的单个变量被打印出来。
当使用echo代替printf所有输出时,将打印输出,但没有换行符。
我在这里缺少什么?
问题是你没有引用$ENV变量。如中所述man bash:
双引号中的字符会保留引号内所有字符的字面值,但 $、`、\ 和启用历史扩展时的 ! 除外。字符 $ 和 ` 在双引号内保留其特殊含义。 反斜杠仅在后跟以下字符之一时保留其特殊含义:$、`、"、\ 或。
因此,将序列\n括在双引号中可以保留其含义。这就是为什么,当没有引用时,\n只是一个正常的n:
$ printf \n
n$
Run Code Online (Sandbox Code Playgroud)
同时,引用时:
$ printf "\n"
$
Run Code Online (Sandbox Code Playgroud)
bash 中未加引号的变量会调用 split+glob 运算符。这意味着该变量在空格(或任何特殊变量$IFS已设置为)上拆分,并且每个结果单词用作 glob(它将扩展以匹配任何匹配的文件名)。您的问题在于“拆分”部分。
为了说明,让我们采用一个更简单的多行变量:
$ var=$(printf "foo\nbar\n")
Run Code Online (Sandbox Code Playgroud)
现在,使用 shell 的set -x调试功能,您可以确切地看到发生了什么:
$ echo $var
+ echo foo bar
foo bar
$ echo "$var"
+ echo 'foo
bar'
foo
bar
Run Code Online (Sandbox Code Playgroud)
正如您在上面看到的,echo $var(未加引号的)受$varsplit+glob 的影响,因此它会产生两个单独的字符串,foo并且bar. 换行符被 th split+glob 吃掉了。当变量被引用时,它没有受到 split+glob 的影响,换行符被保留,因为它被引用,也被正确解释并打印出来。
下一个问题是printf不像echo. 它不只是打印你给它的任何东西,它需要一个格式字符串。例如printf "color:%s" "green"将打印,color:green因为%s将被替换为green.
它还会忽略任何无法放入给定格式字符串的输入。所以,如果你运行printf foo bar,printf将把foo它当作它的格式字符串和bar它应该用它格式化的变量。由于没有%s或等价物被替换,bar,bar被忽略并foo单独打印:
$ printf $var
+ printf foo bar
foo
Run Code Online (Sandbox Code Playgroud)
那就是你跑的时候发生的事情printf $ENV_BEFORE。因为变量没有被引用,split glob 有效地用空格替换了换行符,并且printf只打印了它看到的第一个“单词”。
要正确执行此操作,请使用格式字符串,并始终引用您的变量:
printf '%s\n' "$ENV_BEFORE"
Run Code Online (Sandbox Code Playgroud)
printf( 与echo) 默认情况下不打印换行符,您必须明确告诉它:
printf "${ENV_BEFORE}\n"
Run Code Online (Sandbox Code Playgroud)
或更好:
printf '%s\n' "$ENV_BEFORE"
Run Code Online (Sandbox Code Playgroud)
在@don_crissti在评论中正确指出之后,在我再次阅读了这个问题之后,我认为你的情况与上面不同。
就您而言,问题是您没有引用变量$ENV_BEFORE。
如果不引用变量,以下是printf和的行为:echo
printf:
没有引号和任何格式说明符,它只会打印第一个单词
使用引号并且没有任何格式说明符,它会考虑扩展中具有%格式说明符的任何内容,并且可能会阻塞
如果没有引号并带有格式说明符,它将受到分词和路径名扩展的影响,因此会导致意外结果,因为任何值IFS都会成为输出中的空格
使用引号和格式说明符,您将获得所需的结果,即在扩展上不会执行分词和路径名扩展。
echo:
如果没有引号,变量扩展将受到分词和路径名扩展的影响,因此您将无法获得所需的输出
使用quote变量扩展将不会受到分词和路径名扩展的影响,因此您将获得所需的输出