printf 只打印一行

printf 只打印一行

env我正在尝试获取shell 变量中的输出并打印它。

#!/bin/sh
ENV_BEFORE=$(env)
printf $ENV_BEFORE

结果,env输出中的单个变量被打印。

当使用echo而不是printf打印所有输出时,但没有换行符。

我在这里缺少什么?

答案1

问题是您没有引用该$ENV变量。正如中所解释的man bash

将字符括在双引号中会保留引号内所有字符的字面值,但 $、`、\ 以及启用历史扩展时的 ! 除外。字符 $ 和 ` 在双引号内保留其特殊含义。 仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:$、`、"、\ 或

因此,将序列括\n在双引号中可以保留其含义。这就是为什么,当没有引用时,\n它只是一个正常的n

$ printf \n
n$

而当引用时:

$ printf "\n"

$

bash 中未加引号的变量会调用 split+glob 运算符。这意味着该变量在空格(或特殊变量$IFS设置的任何内容)上分割,并且每个结果单词都用作 glob(它将扩展以匹配任何匹配的文件名)。您的问题在于其中的“拆分”部分。

为了说明这一点,我们采用一个更简单的多行变量:

$ var=$(printf "foo\nbar\n")

现在,使用 shell 的set -x调试功能,您可以准确地看到发生了什么:

$ echo $var
+ echo foo bar
foo bar

$ echo "$var"
+ echo 'foo
bar'
foo
bar

正如您在上面看到的,echo $var(未加引号)服从$varsplit+glob,因此它会产生两个单独的字符串,foobar。换行符被 split+glob 吃掉了。当变量被引用时,它不会受到 split+glob 的影响,换行符被保留,并且因为它被引用,所以也被正确解释并打印出来。

下一个问题是printf不喜欢echo。它不只是打印您提供的任何内容,它还需要一个格式字符串。例如printf "color:%s" "green"将打印,color:green因为%s将被替换为green

它还忽略任何不适合给定格式字符串的输入。因此,如果您运行printf foo bar,printf将视为foo其格式字符串以及bar应使用其格式化的变量。由于没有%s或等价物可以被替换barbar因此被忽略并foo单独打印:

$ printf $var
+ printf foo bar
foo

这就是你跑步时发生的事情printf $ENV_BEFORE。因为变量没有被引用,所以分割的 glob 有效地用空格替换了换行符,并且printf只打印了它看到的第一个“单词”。

要正确执行此操作,请使用格式字符串,并始终引用变量:

printf '%s\n' "$ENV_BEFORE"

答案2

printf( 与echo) 默认情况下不打印换行符,您必须明确告诉它:

printf "${ENV_BEFORE}\n"

或更好:

printf '%s\n' "$ENV_BEFORE"

@don_crissti在评论中正确指出,在我再次阅读问题后,我认为您的情况与上面不同。

就您而言,问题是您没有引用变量$ENV_BEFORE

如果不引用变量,以下是printf和的行为:echo

  • printf:

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

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

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

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

  • echo:

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

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

相关内容