捕获多行输出到Bash变量

Capturing multiple line output into a Bash variable

我有一个脚本'myscript',输出如下:

1
2
3
abc
def
ghi

在另一个脚本中,我打电话给:

1
declare RESULT=$(./myscript)

并且$RESULT获取值

1
abc def ghi

有没有办法用换行符或' n'字符存储结果,所以我可以用'echo -e'输出它?


实际上,RESULT包含你想要的 - 展示:

1
echo"$RESULT"

你展示的是你得到的:

1
echo $RESULT

正如评论中所指出的,区别在于:(1)变量的双引号版本(echo"$RESULT")保留了值的内部间距,与变量中表示的完全相同 - 换行符,制表符,多个空格和全部 - 而(2)未加引号的版本(echo $RESULT)用一个空格替换一个或多个空格,制表符和换行符的每个序列。因此(1)保留输入变量的形状,而(2)创建一个可能非常长的单行输出,其中'单词'由单个空格分隔(其中'单词'是非空白字符序列;有必要任何一个词都不是任何字母数字)。


另一个缺陷是命令替换 - $() - 剥离尾随换行符。可能并不总是重要,但如果你真的想要保留输出的确切内容,你将不得不使用另一行和一些引用:

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

如果要处理所有可能的文件名(以避免在错误的文件上操作等未定义的行为),这一点尤为重要。


如果您对特定行感兴趣,请使用结果数组:

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


除了@ l0b0给出的答案之外,我还有这样的情况:我需要保留脚本输出的任何尾随换行符并检查脚本的返回码。
而l0b0的答案问题是'echo x'正在重置$?回到零...所以我设法提出了这个非常狡猾的解决方案:

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


怎么样,它会读取变量的每一行,然后可以使用!
说myscript输出被重定向到一个名为myscript_output的文件

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


在这里尝试了大部分解决方案之后,我发现的最简单的事情是显而易见的 - 使用临时文件。我不确定你想用你的多行输出做什么,但你可以使用read逐行处理它。关于你唯一不能真正做到的事情就是把它全部放在同一个变量中,但是对于大多数实际来说,这样更容易处理。

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

快速入侵使其执行请求的操作:

1
2
3
4
5
6
7
result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line
"

done < /tmp/foo
echo -e $result

请注意,这会增加额外的一行。如果你正在使用它,你可以围绕它进行编码,我只是太懒了。

编辑:虽然这个案例非常有效,但读这篇文章的人应该知道你可以轻松地将你的stdin压缩到while循环中,从而为你提供一个运行一行,清除stdin和退出的脚本。像ssh那样我会这样做吗?我刚看到它,其他代码示例如下:https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

再一次!这次使用不同的文件句柄(stdin,stdout,stderr为0-2,所以我们可以在bash中使用&amp; 3或更高版本)。

1
2
3
4
5
6
7
result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line
"

done 3</tmp/foo
echo -e $result

你也可以使用mktemp,但这只是一个快速的代码示例。 mktemp的用法如下:

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

然后像使用文件的实际名称一样使用$ filenamevar。可能不需要在这里解释,但有人在评论中抱怨。