如何让 bash 反引号运算符在输出中保留换行符?

slo*_*son 40 linux bash

有没有办法让 bash 在反引号替换中不吃换行符?

例如:

var=`echo line one && echo line two`
echo $var

line one line two
Run Code Online (Sandbox Code Playgroud)

我想要的是

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two
Run Code Online (Sandbox Code Playgroud)

cYr*_*rus 61

这不是反引号替换的问题,而是echo; 您必须引用变量才能使控制字符正常工作:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two
Run Code Online (Sandbox Code Playgroud)

  • `bash` 在执行命令之前执行参数扩展,需要引用来通知 `bash` 你要打印单个字符串而不是 4 个单词(`line`、`one`、`line` 和 `two`)。尝试:`echo a <许多空格> b` 和`echo "a <许多空格> b"`。也看看[这个答案](http://stackoverflow.com/a/2414157/477168)。 (7认同)

Iwa*_*amp 11

简而言之,需要的是在捕获时在输出的末尾添加后缀,在使用变量时将其删除,并始终引用变量:

# add a suffix to the end of the output when capturing it
var=$( cat /no/file/here 2>&1; echo extra)
# when using the variable, remove the suffix, and quote the variable
echo -n "${var%extra}"
Run Code Online (Sandbox Code Playgroud)

将其与以下内容进行比较,其中第二个命令的输出不会以换行符结尾。

var=$( cat /no/file/here 2>&1)
echo -n "${var}"
Run Code Online (Sandbox Code Playgroud)

对正在发生的事情的解释

字符串中的换行符

首先确定预期的字节序列:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.
Run Code Online (Sandbox Code Playgroud)

现在,使用命令替换(第 3.5.4 节)尝试捕获此字节序列:

$ x=$( echo a; echo b; )
Run Code Online (Sandbox Code Playgroud)

然后,做一个简单的回显来验证字节序列:

$ echo $x | xxd
0000000: 6120 620a                                a b.
Run Code Online (Sandbox Code Playgroud)

所以看起来第一个换行符被一个空格替换了,第二个换行符保持不变。但为什么 ?

让我们看看这里实际发生了什么:

首先bash会做Shell参数扩展(3.5.3节)

'$' 字符引入了参数扩展、命令替换或算术扩展。要扩展的参数名称或符号可以用大括号括起来,这是可选的,但用于保护要扩展的变量免受紧跟其后的字符的影响,这些字符可以解释为名称的一部分。

然后 bash 将进行分词(第 3.5.7 节)

shell 扫描没有出现在双引号内的参数扩展、命令替换和算术扩展的结果以进行分词。

Shell 将 $IFS 的每个字符视为分隔符,并将其他扩展的结果拆分为这些字符上的单词。如果 IFS 未设置,或者它的值正好是 ,...

接下来,bash 会将其视为简单命令(第 3.2.1 节)

一个简单的命令是最常遇到的那种命令。它只是由空格分隔的一系列单词,由 shell 的一个控制运算符终止(请参阅定义)。第一个单词通常指定要执行的命令,其余单词是该命令的参数。

空白的定义(第 2 部分 - 定义)

空白 空格或制表符。

最后,bash 调用echo(第 4.2 节 - Bash 内置命令)内部命令

... 输出 args,以空格分隔,以换行符结尾。...

所以总结一下,换行符被分词删除,然后echo得到2个args,“a”和“b”,然后输出它们,用空格分隔并以换行符结尾。

执行@cyrus 建议的操作(并使用 -n 抑制 echo 中的换行符),结果会更好:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b
Run Code Online (Sandbox Code Playgroud)

字符串末尾的换行符

尽管如此,它仍然不完美,尾随的换行符不见了。仔细查看命令替换(第 3.5.4 节)

Bash 通过执行命令并用命令的标准输出替换命令替换来执行扩展,并删除任何尾随的换行符。

现在已经清楚为什么换行符消失了,bash 可能会被愚弄保留它。为此,请在末尾添加一个附加字符串,并在使用变量时将其删除:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.
Run Code Online (Sandbox Code Playgroud)

另一个例子

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46
Run Code Online (Sandbox Code Playgroud)