简体   繁体   English

将多行 output 捕获到 Bash 变量中

[英]Capturing multiple line output into a Bash variable

I've got a script 'myscript' that outputs the following:我有一个脚本“myscript”,它输出以下内容:

abc
def
ghi

in another script, I call:在另一个脚本中,我调用:

declare RESULT=$(./myscript)

and $RESULT gets the value并且$RESULT获取值

abc def ghi

Is there a way to store the result either with the newlines, or with '\n' character so I can output it with ' echo -e '?有没有办法用换行符或'\n'字符存储结果,这样我就可以用' echo -e ' output 它?

Actually, RESULT contains what you want — to demonstrate:实际上,RESULT 包含您想要的内容 - 以演示:

echo "$RESULT"

What you show is what you get from:你所展示的就是你从中得到的:

echo $RESULT

As noted in the comments, the difference is that (1) the double-quoted version of the variable ( echo "$RESULT" ) preserves internal spacing of the value exactly as it is represented in the variable — newlines, tabs, multiple blanks and all — whereas (2) the unquoted version ( echo $RESULT ) replaces each sequence of one or more blanks, tabs and newlines with a single space.如评论中所述,不同之处在于(1)变量的双引号版本( echo "$RESULT" )保留了值的内部间距,与变量中表示的值完全相同 - 换行符、制表符、多个空格和all — 而 (2) 不带引号的版本 ( echo $RESULT ) 用一个空格替换一个或多个空格、制表符和换行符的每个序列。 Thus (1) preserves the shape of the input variable, whereas (2) creates a potentially very long single line of output with 'words' separated by single spaces (where a 'word' is a sequence of non-whitespace characters; there needn't be any alphanumerics in any of the words).因此(1)保留输入变量的形状,而(2)创建一个可能非常长的 output 单行,其中“单词”由单个空格分隔(其中“单词”是非空白字符的序列;不需要'不是任何单词中的任何字母数字)。

Another pitfall with this is that command substitution$() — strips trailing newlines.另一个缺陷是命令替换—— $() ——去掉了尾随的换行符。 Probably not always important, but if you really want to preserve exactly what was output, you'll have to use another line and some quoting:可能并不总是很重要,但如果你真的想保留 output 的确切内容,你将不得不使用另一行和一些引用:

RESULTX="$(./myscript; echo x)"
RESULT="${RESULTX%x}"

This is especially important if you want tohandle all possible filenames (to avoid undefined behavior like operating on the wrong file).如果您想处理所有可能的文件名(以避免未定义的行为,例如操作错误的文件),这一点尤其重要。

In case that you're interested in specific lines, use a result-array:如果您对特定行感兴趣,请使用结果数组:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"

In addition to the answer given by @l0b0 I just had the situation where I needed to both keep any trailing newlines output by the script and check the script's return code.除了@l0b0 给出的答案之外,我还遇到了一种情况,即我需要通过脚本保留任何尾随换行符 output检查脚本的返回码。 And the problem with l0b0's answer is that the 'echo x' was resetting $? l0b0 的答案的问题是“echo x”正在重置 $? back to zero... so I managed to come up with this very cunning solution:回到零......所以我设法想出了这个非常狡猾的解决方案:

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"

Parsing multiple output解析多个output

Introduction介绍

So your myscript output 3 lines, could look like:所以你的myscript output 3 行,可能看起来像:

myscript() { echo $'abc\ndef\nghi'; }

or或者

myscript() { local i; for i in abc def ghi ;do echo $i; done ;}

Ok this is a function, not a script (no need of path ./ ), but output is same好的,这是 function,不是脚本(不需要路径./ ),但 output 是一样的

myscript
abc
def
ghi

Considering result code考虑结果代码

To check for result code, test function will become:要检查结果代码,测试 function 将变为:

myscript() { local i;for i in abc def ghi ;do echo $i;done;return $((RANDOM%128));}

1. Storing multiple output in one single variable, showing newlines 1.在一个变量中存储多个output,显示换行符

Your operation is correct:你的操作是正确的:

RESULT=$(myscript)

About result code , you could add:关于结果代码,您可以添加:

RCODE=$?

even in same line:即使在同一行:

RESULT=$(myscript) RCODE=$?

Then然后

echo $RESULT 
abc def ghi

echo "$RESULT"
abc
def
ghi

echo ${RESULT@Q}
$'abc\ndef\nghi'

printf "%q\n" "$RESULT"
$'abc\ndef\nghi'

but for showing variable definition, use declare -p :但为了显示变量定义,请使用declare -p

declare -p RESULT
declare -- RESULT="abc
def
ghi"

2. Parsing multiple output in array, using mapfile 2.解析数组中多个output,使用mapfile

Storing answer into myvar variable:将答案存储到myvar变量中:

mapfile -t myvar < <(myscript)
echo ${myvar[2]}
ghi

Showing $myvar :显示$myvar

declare -p myvar
declare -a myvar=([0]="abc" [1]="def" [2]="ghi")

Considering result code考虑结果代码

In case you have to check for result code, you could:如果您必须检查结果代码,您可以:

RESULT=$(myscript) RCODE=$?
mapfile -t myvar <<<"$RESULT"

3. Parsing multiple output by consecutives read in command group 3.在命令组中连续read解析多个output

{ read firstline; read secondline; read thirdline;} < <(myscript)
echo $secondline
def

Showing variables:显示变量:

declare -p firstline secondline thirdline
declare -- firstline="abc"
declare -- secondline="def"
declare -- thirdline="ghi"

I often use:我经常使用:

{ read foo;read foo total use free foo ;} < <(df -k /)

Then然后

declare -p use free total
declare -- use="843476"
declare -- free="582128"
declare -- total="1515376"

Considering result code考虑结果代码

Same prepended step:相同的前置步骤:

RESULT=$(myscript) RCODE=$?
{ read firstline; read secondline; read thirdline;} <<<"$RESULT"

declare -p firstline secondline thirdline RCODE
declare -- firstline="abc"
declare -- secondline="def"
declare -- thirdline="ghi"
declare -- RCODE="50"

After trying most of the solutions here, the easiest thing I found was the obvious - using a temp file.在这里尝试了大多数解决方案之后,我发现最简单的事情是显而易见的 - 使用临时文件。 I'm not sure what you want to do with your multiple line output, but you can then deal with it line by line using read.我不确定您想对多行 output 做什么,但您可以使用 read 逐行处理它。 About the only thing you can't really do is easily stick it all in the same variable, but for most practical purposes this is way easier to deal with.关于你唯一不能真正做的事情就是轻松地将它们全部放在同一个变量中,但对于大多数实际目的来说,这更容易处理。

./myscript.sh > /tmp/foo
while read line ; do 
    echo 'whatever you want to do with $line'
done < /tmp/foo

Quick hack to make it do the requested action:快速破解以使其执行请求的操作:

result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line\n"
done < /tmp/foo
echo -e $result

Note this adds an extra line.请注意,这会增加一行。 If you work on it you can code around it, I'm just too lazy.如果你在它上面工作,你可以围绕它编写代码,我只是太懒了。


EDIT: While this case works perfectly well, people reading this should be aware that you can easily squash your stdin inside the while loop, thus giving you a script that will run one line, clear stdin, and exit.编辑:虽然这个案例运行良好,但阅读本文的人应该知道,您可以轻松地在 while 循环中压缩您的标准输入,从而为您提供一个运行一行、清除标准输入并退出的脚本。 Like ssh will do that I think?我想像 ssh 会那样做吗? I just saw it recently, other code examples here: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while我最近才看到它,这里的其他代码示例: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

One more time, This time with a different filehandle (stdin, stdout, stderr are 0-2. so we can use &3 or higher in bash).再一次,这次使用不同的文件句柄(stdin、stdout、stderr 是 0-2。所以我们可以在 bash 中使用 &3 或更高)。

result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line\n"
done 3</tmp/foo
echo -e $result

you can also use mktemp, but this is just a quick code example.您也可以使用 mktemp,但这只是一个快速的代码示例。 Usage for mktemp looks like: mktemp 的用法如下所示:

filenamevar=`mktemp /tmp/tempXXXXXX`
./test > $filenamevar

Then use $filenamevar like you would the actual name of a file.然后像使用文件的实际名称一样使用 $filenamevar。 Probably doesn't need to be explained here but someone complained in the comments.可能不需要在这里解释,但有人在评论中抱怨。

How about this, it will read each line to a variable and that can be used subsequently !怎么样,它会将每一行读取到一个变量中,然后可以使用它! say myscript output is redirected to a file called myscript_output说 myscript output 被重定向到一个名为 myscript_output 的文件

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'

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

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