简体   繁体   English

将3条输出行读入bash中的3个变量

[英]Read 3 output lines into 3 variables in bash

I have a command that generates 3 lines of output such as 我有一个生成3行输出的命令,例如

$ ./mycommand
1
asdf
qwer zxcv

I'd like to assign those 3 lines to 3 different variables ($a, $b, $c) such that 我想将这3行分配给3个不同的变量($ a,$ b,$ c),这样

$ echo $a
1
$ echo $b
asdf
$ echo $c
qwer zxcv

I'm familiar with the while loop method that would normally be used for reading 3 lines at a time from output that contains sets of 3 lines. 我熟悉while循环方法,该方法通常用于一次从包含3行的输出中读取3行。 But that seems less than elegant considering my command will only ever output 3 lines. 但是,考虑到我的命令只会输出3行,这似乎还不够优雅。

I tried playing around with various combinations of values for IFS= and options for read -rabc , sending the command output as stdin, but I could only ever get it to set the first line to the first variable. 我尝试使用IFS=的值和read -rabc选项的各种组合,将命令输出作为stdin发送,但是我只能将其设置为第一行到第一个变量。 Some examples: 一些例子:

IFS= read -r a b c < <(./mycommand)
IFS=$'\n' read -r a b c < <(./mycommand)
IFS= read -r -d $'\n' < <(./mycommand)

If I modify my command so that the 3 lines are separated by spaces instead of newlines, I can successfully just use this variation as long as each former line is properly quoted: 如果我修改命令以使三行用空格而不是换行符隔开,只要正确地引用每行的前面,我就可以成功使用此变体:

read -r a b c < <(./mycommand)

And while that is working for the purposes of my current project, it's still bugging me that I couldn't get it to work the other way. 尽管这对于我当前的项目有用,但仍然令我感到困惑,我无法使它以其他方式工作。 So I'm wondering if anyone can see and explain what I was missing in my original attempt with the 3 lines of output. 因此,我想知道是否有人可以通过3行输出来查看和解释我最初尝试中缺少的内容。

If you want to read data from three lines, use three reads: 如果要从三行读取数据,请使用三读:

{ read -r a; read -r b; read -r c; } < <(./mycommand)

read reads a chunk of data and then splits it up. read读取大量数据,然后将其拆分。 You couldn't get it to work because your chunks were always single lines. 您无法使它正常工作,因为您的块始终都是单行。

Newer BASH versions support mapfile command. 较新的BASH版本支持mapfile命令。 Using that you can read all the lines into an array: 使用它,您可以将所有行读入数组:

mapfile -t ary < <(./command)

Check the array content: 检查数组内容:

declare -p ary
declare -a ary='([0]="1" [1]="asdf" [2]="qwer zxcv")'

Perhaps this explanation will be useful to you. 也许这种解释对您有用。

... it's still bugging me that I couldn't get it to work the other way. ...仍然令我烦恼,我无法使它以其他方式工作。 So I'm wondering if anyone can see and explain what I was missing in my original attempt with the 3 lines of output. 因此,我想知道是否有人可以通过3行输出来查看和解释我最初尝试中缺少的内容。

Simple: read works only with one line (by default). 简单: read仅适用于一行(默认情况下)。 This: 这个:

#!/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"

Will print: 将打印:

first  : 1
second : 
third  : 

However, using a null character will capture the whole string in (replace this line above): 但是,使用字符将捕获整个字符串(在上面替换此行):

    read -d '' a b c < <(mycommand)

Will print: 将打印:

first  : 1
second : asdf
third  : qwer zxcv

The read command absorbed the whole output of the command and was broken into parts with the default value of IFS: Space Tab Enter . read命令吸收了命令的全部输出,并被分解为具有IFS默认值的部分: Space Tab Enter

In this specific example, that worked correctly because the last value is the one with more than one "part". 在此特定示例中,该方法正确运行,因为最后一个值是具有多个“部分”的值。
But this kind of processing is very brittle. 但是这种处理非常脆弱。 For example: this other possible output of the command, the assignment to variables will break: 例如:该命令的其他可能输出,对变量的分配将中断:

mycommand(){ echo -e "1 and 2\nasdf and dfgh\nqwer zxcv"; }

Will output (incorrectly): 将输出(不正确):

first  : 1
second : and
third  : 2
asdf and dfgh
qwer zxcv

The processing is brittle. 处理很脆。 To make it robust we need to use a loop. 为了使其健壮,我们需要使用循环。 But you say that that is something you already know: 但是您说那是您已经知道的:

#!/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]}"

Which will (correctly) print: 哪个将(正确)打印:

first  : 1 and 2
second : asdf and dfgh
third  : qwer zxcv

However, the loop could be made simpler using bash command readarray: 但是,可以使用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]}"

And using a printf "loop" will make the structure work for any count of input lines: 并且使用printf“ loop”将使该结构适用于任何数量的输入行:

#!/bin/bash
mycommand(){ echo -e "1 and 2\nasdf and dfgh\n*\nqwer zxcv"; }

readarray -t arr < <(mycommand)
printf 'value : %s\n' "${arr[@]}"

Hope that this helped. 希望这会有所帮助。

EDIT 编辑

About nulls (in simple read): 关于空值(简单阅读):

In bash, the use of nulls is almost never practical. 在bash中,几乎不使用null。 In specific, nulls are erased silently in most condidions. 具体来说,在大多数情况下,零值都会被静默删除。 This solution does suffer of that limitation. 该解决方案确实具有该限制。

Including a null in the input: 在输入中包含null:

mycommand(){ echo -e "1 and 2\nasdf and dfgh\n\000\n*\nqwer zxcv"; }

will make a simple read -r -d '' get the input up to the first null (understanding such null as the character with octal 000). 将进行简单的read -r -d ''以使输入达到第一个空值(理解为空值,例如具有八进制000的字符)。

echo "test one:"; echo
echo "input"; echo
mycommand | od -tcx1

echo "output"; echo
read -r -d '' arr < <(mycommand)
echo "$arr" | od -tcx1

Gives this as output: 将此作为输出:

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

It is clear that the value captured by read stops at the first octal 000. 显然,读取捕获的值停止在第一个八进制000。
Which, frankly, is to be expected. 坦率地说,这是可以预期的。


About nulls (in readarray): 关于空值(在readarray中):

I have to report, however, that readarray does not stop at the octal 000 but just silently removes it (an usual shell trait). 我必须报告,然而,readarray 不会在八进制000停止,但只是默默地删除它(通常一个外壳性状)。
Running this code: 运行此代码:

#!/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

Renders this output: 呈现此输出:

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

That is, the null 000 or just \\0 gets silently removed. 也就是说,零000或仅\\ 0被静默删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM