如何避免bash命令替换删除换行符?

Lau*_*ent 70 bash newline command-substitution

为了加速一些bash脚本执行,我想使用命令替换将命令的结果保存在变量中,但是命令替换用0x0A空格替换换行符.例如:

a=`df -H`
Run Code Online (Sandbox Code Playgroud)

要么

a=$( df -H )
Run Code Online (Sandbox Code Playgroud)

当我想进一步处理时$a,换行符被空格替换,所有行现在都在一行上,这更难以grep:

echo $a
Run Code Online (Sandbox Code Playgroud)

什么是避免命令替换删除换行符的简单技巧?

use*_*001 107

不删除非尾随换行符

您正在寻找的换行符就在那里,您只是看不到它们,因为您在echo不引用变量的情况下使用它们.

验证:

$ a=$( df -H )
$ echo $a
Filesystem Size Used Avail Use% Mounted on /dev/sda3 276G 50G 213G 19% / udev 2.1G 4.1k 2.1G 1% /dev tmpfs 832M 820k 832M 1% /run none 5.3M 0 5.3M 0% /run/lock none 2.1G 320k 2.1G 1% /run/shm
$ echo "$a"
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda3       276G   50G  213G  19% /
udev            2.1G  4.1k  2.1G   1% /dev
tmpfs           832M  820k  832M   1% /run
none            5.3M     0  5.3M   0% /run/lock
none            2.1G  320k  2.1G   1% /run/shm
$ 
Run Code Online (Sandbox Code Playgroud)

尾随换行符已删除

正如@ user4815162342正确指出的那样,尽管未删除输出中的换行符,但是使用命令替换将删除尾随换行符.见下面的实验:

$ a=$'test\n\n'
$ echo "$a"
test


$ b=$(echo "$a")
$ echo "$b"
test
$
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这并不重要,因为echo将添加删除的换行符(除非使用该-n选项调用),但是有一些边缘情况,在程序的输出中有更多的一个尾随换行符,并且它们对于某些原因.

解决方法

1.添加虚拟角色

在这些情况下,正如@Scrutinizer所提到的,您可以使用以下解决方法:

$ a=$(printf 'test\n\n'; printf x); a=${a%x}
$ echo "$a"
test


$ 
Run Code Online (Sandbox Code Playgroud)

说明:在换行符之后将字符x添加到输出(使用printf x).由于换行符不再跟踪,因此命令替换不会删除它们.下一步是x使用%运算符删除我们添加的${a%x}.现在我们有了原始输出,所有换行都出现了!!!

2.使用流程替换进行读取

我们可以使用进程替换 来将程序的输出提供给read内置命令(归功于@ormaaj),而不是使用命令替换将程序的输出分配给变量.流程替换保留所有换行符.将输出读取到变量有点棘手,但您可以这样做:

$ IFS= read -rd '' var < <( printf 'test\n\n' ) 
$ echo "$var"
test


$ 
Run Code Online (Sandbox Code Playgroud)

说明:

  • 我们将read命令的内部字段分隔符设置为null IFS=.否则read不会将整个输出分配给var,而只分配第一个标记.
  • 我们read用选项调用-rd ''.本r是为了防止反斜杠作为一个特殊字符,并与d ''设置分隔符不了了之,使读取读取整个输出,而不只是第一道防线.

3.从管道中读取

我们可以将程序的输出管道传递给read命令(归功于@ormaaj),而不是使用命令或进程替换来将程序的输出分配给变量.管道也保留所有换行符.但是请注意,这时候我们设置的lastpipe外壳可选行为,使用shopt内置.这是必需的,因此read命令在当前shell环境中执行.否则,变量将在子shell中分配,并且无法从脚本的其余部分访问.

$ cat test.sh 
#!/bin/bash
shopt -s lastpipe
printf "test\n\n" | IFS= read -rd '' var
echo "$var"
$ ./test.sh 
test


$
Run Code Online (Sandbox Code Playgroud)

  • @ user4815162342:有趣的是,一个糟糕的工作可能是使用`a = $(printf'test \n \n'; printf x); echo"$ {a%x}"` (5认同)
  • 注意:`read`在击中EOF时退出非零.这导致方法2和3以非零方式退出,因此屏蔽主命令的退出状态.在方法1中使用`main_cmd; printf x`还会覆盖主命令退出代码并干扰错误处理.请尝试使用`main_cmd && printf x`(如果尾部线仅在成功退出时很重要)或`main_cmd; EC = $ ?; printf x; 退出$ ec`以在成功和错误情况下保留尾随空格. (4认同)
  • *尾随*换行仍然被删除,我恐怕无法避免.(OP可能并不关心那些,但一般来说这很好.) (3认同)