为了加快执行bash脚本的速度,我想使用命令替换将命令的结果保留在变量中,但是命令替换将0x0A
换行符替换为空格。例如:
a=`df -H`
要么
a=$( df -H )
当我想进一步处理时$a
,将换行符替换为一个空格,并且所有行现在都在一行上,这很难grep进行:
echo $a
避免换行符被命令替换删除的简单技巧是什么?
Answers:
您正在寻找的换行符在那里,只是看不到它们,因为您在使用时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
$
正如@ user4815162342正确指出的那样,尽管不删除输出中的换行符,但使用命令替换来删除结尾的换行符。请参见下面的实验:
$ a=$'test\n\n'
$ echo "$a"
test
$ b=$(echo "$a")
$ echo "$b"
test
$
在大多数情况下,这无关紧要,因为echo
将添加已删除的换行符(除非使用该-n
选项调用该换行符),但是在某些极端情况下,程序输出中的尾随换行符不止一个,它们对于一些理由。
在这种情况下,如@Scrutinizer所述,您可以使用以下解决方法:
$ a=$(printf 'test\n\n'; printf x); a=${a%x}
$ echo "$a"
test
$
说明:在换行符之后,将字符x
添加到输出中(使用printf x
)。由于换行符不再尾随,因此不会被命令替换删除。下一步是x
使用中的%
运算符删除我们添加的${a%x}
。现在我们有了原始输出,其中包含所有换行符!!!
代替使用命令替换将程序的输出分配给变量,我们可以使用进程替换 来将程序的输出提供给read
内置命令(贷记为@ormaaj)。流程替换将保留所有换行符。将输出读取到变量有点棘手,但是您可以这样做:
$ IFS= read -rd '' var < <( printf 'test\n\n' )
$ echo "$var"
test
$
说明:
IFS=
。否则read
,不会将整个输出分配给var
,而只会将第一个令牌分配给。read
用options调用-rd ''
。本r
是为了防止反斜杠作为一个特殊字符,并与d ''
设置分隔符不了了之,使读取读取,而不只是第一行的所有输出。代替使用命令或进程替换将程序的输出分配给变量,我们可以将程序的输出通过管道传递给read
命令(贷方为@ormaaj)。管道还保留所有换行符。但是请注意,这时候我们设置的lastpipe
外壳可选行为,使用了shopt
内置。这是必需的,以便read
在当前Shell环境中执行命令。否则,变量将在子外壳中分配,并且脚本的其余部分将无法访问该变量。
$ cat test.sh
#!/bin/bash
shopt -s lastpipe
printf "test\n\n" | IFS= read -rd '' var
echo "$var"
$ ./test.sh
test
$
a=$(printf 'test\n\n'; printf x); echo "${a%x}"
read
与非标准选项一起使用来进行分配。shopt -s lastpipe; printf %s "$myData" | IFS= read -rd '' var
。Bashprintf -v
在某些类似情况下也很有用。
lastpipe
(尽管我通常lastpipe
无论如何都使用)。您注意到的原因是由于作业控制。交互式外壳具有set -m
迫使管道在单独的进程组中运行的功能,这就需要最后一个元素的子外壳。由于作业控制不适用于子shell的子级,因此将所有内容都包裹起来(...)
是一种解决方法(无论如何,我在进行交互式测试时几乎总是这样做)。
read
到达EOF时以非零退出。这将导致方法2和方法3以非零值退出,从而掩盖了主命令的退出状态。在方法1中,使用main_cmd ; printf x
还会覆盖主要命令的退出代码并干扰错误处理。尝试main_cmd && printf x
改行(如果尾行仅在成功退出时才重要)或main_cmd ; ec=$?; printf x; exit $ec
在成功和错误情况下都保留尾随空格。
另一个“巧妙的窍门”是使用回车符,该字符防止换行符被剥离,但不向输出添加任何内容:
$ my_func_1 () {
> echo "This newline is squashed"
> }
$ my_func_2 () {
> echo "This newline is not squashed"
> echo -n $'\r'
> }
$ echo -n "$(my_func_1)" && echo -n "$(my_func_2)" && echo done
This newline is squashedThis newline is not squashed
done
$
但是买家要当心:正如评论中提到的那样,这对于直接进入终端的输出可能效果很好,但是如果您将其传递给另一个流程,则可能会使它感到困惑,因为它可能不会期望怪异的终止'\r'
。
echo -n "$(my_func_2)" | cat -A
您将看到$'\r'
保留在输出中,因此如果接收器进程不期望它会导致问题。