printf 只打印一行

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所有输出时,将打印输出,但没有换行符。

我在这里缺少什么?

ter*_*don 9

问题是你没有引用$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)


hee*_*ayl 5

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:

    1. 没有引号和任何格式说明符,它只会打印第一个单词

    2. 使用引号并且没有任何格式说明符,它会考虑扩展中具有%格式说明符的任何内容,并且可能会阻塞

    3. 如果没有引号并带有格式说明符,它将受到分词和路径名扩展的影响,因此会导致意外结果,因为任何值IFS都会成为输出中的空格

    4. 使用引号和格式说明符,您将获得所需的结果,即在扩展上不会执行分词和路径名扩展。

  • echo:

    1. 如果没有引号,变量扩展将受到分词和路径名扩展的影响,因此您将无法获得所需的输出

    2. 使用quote变量扩展将不会受到分词和路径名扩展的影响,因此您将获得所需的输出