我有一个生成3行输出的命令,例如
$ ./mycommand
1
asdf
qwer zxcv
我想将这3行分配给3个不同的变量($ a,$ b,$ c),以便
$ echo $a
1
$ echo $b
asdf
$ echo $c
qwer zxcv
我熟悉while循环方法,它通常用于从包含3行组的输出一次读取3行。但考虑到我的命令只输出3行,这似乎不太优雅。
我尝试使用IFS=
的各种值组合和read -r a b c
的选项,将命令输出作为stdin发送,但我只能得到它将第一行设置为第一个变量。一些例子:
IFS= read -r a b c < <(./mycommand)
IFS=$'\n' read -r a b c < <(./mycommand)
IFS= read -r -d $'\n' < <(./mycommand)
如果我修改我的命令以便3行用空格而不是换行符分隔,只要每个前一行被正确引用,我就可以成功地使用这个变体:
read -r a b c < <(./mycommand)
虽然这对我目前的项目来说是有效的,但它仍然让我烦恼,因为我不能让它以另一种方式运作。因此,我想知道是否有人能够通过3行输出看到并解释我在原始尝试中缺少的内容。
答案 0 :(得分:6)
如果您想从三行读取数据,请使用三个读数:
{ read -r a; read -r b; read -r c; } < <(./mycommand)
read
读取一大块数据然后将其拆分。你无法让它发挥作用,因为你的块总是单行。
答案 1 :(得分:3)
较新的BASH版本支持mapfile
命令。使用它,您可以将所有行读入数组:
mapfile -t ary < <(./command)
检查阵列内容:
declare -p ary
declare -a ary='([0]="1" [1]="asdf" [2]="qwer zxcv")'
答案 2 :(得分:3)
也许这个解释对你有用。
......它仍然困扰着我,我无法让它以另一种方式运作。因此,我想知道是否有人能够通过3行输出看到并解释我在原始尝试中缺少的内容。
简单:read
仅适用于一行(默认情况下)。这样:
#!/bin/bash
mycommand(){ echo -e "1\nasdf\nqwer zxcv"; }
read a b c < <(mycommand)
printf 'first : %s\nsecond : %s\nthird : %s\n' "$a" "$b" "$c"
将打印:
first : 1
second :
third :
但是,使用 null 字符将捕获整个字符串(替换上面的这一行):
read -d '' a b c < <(mycommand)
将打印:
first : 1
second : asdf
third : qwer zxcv
read
命令吸收了命令的整个输出,并被分成了具有默认值IFS的部分: Space Tab Enter < / KBD>
在这个具体的例子中,它正常工作,因为最后一个值是一个多于一个&#34;部分&#34;。
但这种处理非常脆弱。例如:该命令的其他可能输出,对变量的赋值将中断:
mycommand(){ echo -e "1 and 2\nasdf and dfgh\nqwer zxcv"; }
输出(错误):
first : 1
second : and
third : 2
asdf and dfgh
qwer zxcv
处理很脆弱。为了使其健壮,我们需要使用循环。但是你说这是你已经知道的事情:
#!/bin/bash
mycommand(){ echo -e "1 and 2\nasdf and dfgh\nqwer zxcv"; }
i=0; while read arr[i]; do ((i++)); done < <(mycommand)
printf 'first : %s\nsecond : %s\nthird : %s\n' "${arr[0]}" "${arr[1]}" "${arr[2]}"
将(正确)打印:
first : 1 and 2
second : asdf and dfgh
third : qwer zxcv
但是,使用bash命令readarray可以使循环更简单:
#!/bin/bash
mycommand(){ echo -e "1 and 2\nasdf and dfgh\nqwer zxcv"; }
readarray -t arr < <(mycommand)
printf 'first : %s\nsecond : %s\nthird : %s\n' "${arr[0]}" "${arr[1]}" "${arr[2]}"
使用printf&#34;循环&#34;将使结构适用于任何输入行数:
#!/bin/bash
mycommand(){ echo -e "1 and 2\nasdf and dfgh\n*\nqwer zxcv"; }
readarray -t arr < <(mycommand)
printf 'value : %s\n' "${arr[@]}"
希望这有所帮助。
修改强>
在bash中,使用null几乎不可行。具体而言,在大多数条件下,空值会被默默地删除。这种解决方案确实受到这种限制。
在输入中包含null:
mycommand(){ echo -e "1 and 2\nasdf and dfgh\n\000\n*\nqwer zxcv"; }
将使一个简单的read -r -d ''
获得第一个null的输入(理解为带有八进制000的字符的null)。
echo "test one:"; echo
echo "input"; echo
mycommand | od -tcx1
echo "output"; echo
read -r -d '' arr < <(mycommand)
echo "$arr" | od -tcx1
将此作为输出:
test one:
input
0000000 1 a n d 2 \n a s d f a n d
31 20 61 6e 64 20 32 0a 61 73 64 66 20 61 6e 64
0000020 d f g h \n \0 \n * \n q w e r z
20 64 66 67 68 0a 00 0a 2a 0a 71 77 65 72 20 7a
0000040 x c v \n
78 63 76 0a
0000044
output
0000000 1 a n d 2 \n a s d f a n d
31 20 61 6e 64 20 32 0a 61 73 64 66 20 61 6e 64
0000020 d f g h \n
20 64 66 67 68 0a
0000026
很明显,读取的值在第一个八进制000处停止 坦率地说,这是可以预期的。
然而,我必须报告,readarray 不停在八进制000但只是默默地删除它(通常的shell特性)。
运行此代码:
#!/bin/bash
mycommand(){ echo -e "1 and 2\nasdf and dfgh\n\000\n*\nqwer zxcv"; }
echo "test two:"; echo
echo "input"; echo
mycommand | od -tcx1
echo "output"; echo
readarray -t arr < <(mycommand)
printf 'value : %s\n' "${arr[@]}"
echo
printf 'value : %s\n' "${arr[@]}"|od -tcx1
呈现此输出:
test two:
input
0000000 1 a n d 2 \n a s d f a n d
31 20 61 6e 64 20 32 0a 61 73 64 66 20 61 6e 64
0000020 d f g h \n \0 \n * \n q w e r z
20 64 66 67 68 0a 00 0a 2a 0a 71 77 65 72 20 7a
0000040 x c v \n
78 63 76 0a
0000044
output
value : 1 and 2
value : asdf and dfgh
value :
value : *
value : qwer zxcv
0000000 v a l u e : 1 a n d 2 \n
76 61 6c 75 65 20 3a 20 31 20 61 6e 64 20 32 0a
0000020 v a l u e : a s d f a n d
76 61 6c 75 65 20 3a 20 61 73 64 66 20 61 6e 64
0000040 d f g h \n v a l u e : \n v
20 64 66 67 68 0a 76 61 6c 75 65 20 3a 20 0a 76
0000060 a l u e : * \n v a l u e :
61 6c 75 65 20 3a 20 2a 0a 76 61 6c 75 65 20 3a
0000100 q w e r z x c v \n
20 71 77 65 72 20 7a 78 63 76 0a
0000113
也就是说,空的000或只是\ 0被静默删除。