将多行输出捕获到 Bash 变量中

Capturing multiple line output into a Bash variable

提问人:Parker 提问时间:3/5/2009 最后编辑:codeforesterParker 更新时间:2/6/2023 访问量:354011

问:

我有一个脚本“myscript”,它输出以下内容:

abc
def
ghi

在另一个脚本中,我调用:

declare RESULT=$(./myscript)

并获取值$RESULT

abc def ghi

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

bash 变量

评论

2赞 Johannes Schaub - litb 3/5/2009
这让我很惊讶。你没有$(cat ./myscipt)吗?否则,我本来希望它尝试执行命令 abc、def 和 ghi
0赞 Jonathan Leffler 3/5/2009
@litb:是的,我想是的;您还可以使用 $(<./myscript) 来避免执行命令。
3赞 Jonathan Leffler 12/30/2013
(注意:上面的两条评论指的是对问题的修订,该问题开始我有一个脚本“myscript”,其中包含以下内容,这导致了问题。问题的当前修订版(我有一个脚本“myscript”,输出以下内容)使评论变得多余。然而,修订是从 2011 年 11 月 11 日开始的,在两条评论发表很久之后。
0赞 Peyman Mohamadpour 4/5/2021
请参阅 IFS=$'\n' 的确切含义是什么$IFS
0赞 G-Man Says 'Reinstate Monica' 10/4/2022
相关:为什么使用命令替换时换行符会丢失?为什么我的shell脚本在空格或其他特殊字符上窒息?(在Unix和Linux上)。

答:

1292赞 Jonathan Leffler 3/5/2009 #1

实际上,RESULT 包含您想要的内容 - 演示:

echo "$RESULT"

你展示的就是你从中得到的:

echo $RESULT

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

评论

76赞 Jonathan Leffler 4/12/2011
@troelskn:区别在于 (1) 变量的双引号版本保留了值的内部间距,与变量、换行符、制表符、多个空格和所有值中表示的完全一样,而 (2) 不带引号的版本将一个或多个空格、制表符和换行符的每个序列替换为单个空格。因此,(1) 保留了输入变量的形状,而 (2) 创建了一行可能很长的输出,其中“单词”由单个空格分隔(其中“单词”是非空格字符序列;任何单词中都不需要任何字母数字)。
28赞 Yu Shen 7/14/2012
为了使答案更容易理解:答案告诉 echo “$RESULT” 保留换行符,而 echo $RESULT 则不保留换行符。
0赞 CommaToast 8/13/2016
在某些情况下,这无法保留换行符和前导空格。
4赞 Jonathan Leffler 8/13/2016
@CommaToast:你打算详细说明一下吗?尾随换行符丢失;没有一个简单的方法可以解决这个问题。前导空白 — 我不知道它们在什么情况下会丢失。
2赞 tripleee 3/13/2017
另请参阅何时将引号括在 shell 变量周围?
101赞 l0b0 3/16/2011 #2

这样做的另一个陷阱是命令替换 — — 剥离尾随换行符。可能并不总是很重要,但如果你真的想准确地保留输出的内容,你必须使用另一行和一些引号:$()

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

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

评论

6赞 Jonathan Leffler 12/30/2013
我不得不使用一个损坏的 shell 工作一段时间,该 shell 没有从命令替换中删除最后一个换行符(它不是进程替换),而且它几乎破坏了所有内容。例如,如果这样做了,则在 之前有一个换行符,并且用双引号将名称括起来没有帮助。它很快就被修复了。这是在 1983-5 年 ICL Perq PNX 的时间范围内;shell 没有内置变量。pwd=`pwd`; ls $pwd/$file/$PWD
0赞 Rahul Reddy 5/21/2012 #3

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

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

评论

5赞 vadipp 2/8/2013
好吧,那不是抨击,那是 awk。
16赞 Lurchman 7/11/2013 #4

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

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

评论

0赞 dragon788 9/26/2018
这很可爱,我实际上有一个用例,我想要一个接受任意退出代码和可选消息的函数,我想使用另一个函数来提供消息,但换行符一直被压扁,希望这能让我解决这个问题。die ()usage ()
30赞 user2574210 7/12/2013 #5

如果您对特定行感兴趣,请使用 result-array:

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

评论

5赞 Liam 4/15/2015
如果行中有空格,这将计算字段(空格之间的内容)而不是行。
4赞 chepner 1/19/2016
您将使用和处理替换,而不是命令替换:。readarrayreadarray -t RESULT < <(./myscript>
1赞 user1279741 5/4/2016 #6

在尝试了这里的大多数解决方案之后,我发现最简单的事情是显而易见的 - 使用临时文件。我不确定您想如何处理多行输出,但您可以使用读取逐行处理它。你唯一不能做的就是轻松地将它们全部粘在同一个变量中,但对于大多数实际目的来说,这更容易处理。

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

快速破解,使其执行请求的操作:

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

请注意,这会增加一行额外的行。如果你在它上面工作,你可以围绕它编写代码,我只是太懒了。


编辑:虽然这种情况运行良好,但阅读本文的人应该知道,您可以轻松地在while循环中压缩stdin,从而为您提供一个脚本,该脚本将运行一行,清除stdin并退出。我想 ssh 会这样做吗?我最近才看到它,其他代码示例在这里: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

再来一次!这次使用不同的文件句柄(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

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

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

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

评论

0赞 Kar.ma 9/9/2016
我也尝试了其他解决方案,在您的第一个建议下,我终于让我的脚本工作了
1赞 tripleee 3/13/2017
反对票:这过于复杂,无法避免多个常见的 bash 陷阱
0赞 user1279741 3/14/2017
前几天有人告诉我奇怪的 stdin 文件句柄问题,我就像“哇”。Lemme 添加一些东西非常快。
1赞 Jonathan Leffler 8/11/2018
在 Bash 中,可以使用 read 命令扩展从文件描述符 3 中读取。-u 3
0赞 F. Hauri - Give Up GitHub 10/15/2020
除了@JonathanLeffler评论之外,您还可以写:而不是创建临时文件!while read -ru $testout line;do echo "do something with $line";done {testout}< <(./test)
7赞 F. Hauri - Give Up GitHub 10/13/2020 #7

解析多个输出

介绍

因此,您的输出 3 行可能如下所示:myscript

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

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

好的,这是一个函数,而不是脚本(不需要路径),但输出是一样的./

myscript
abc
def
ghi

考虑结果代码

为了检查结果代码,测试功能将变为:

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

1.将多个输出存储在一个变量中,显示换行符

您的操作是正确的:

RESULT=$(myscript)

关于结果代码,您可以添加:

RCODE=$?

甚至在同一行:

RESULT=$(myscript) RCODE=$?

然后

echo $RESULT $RCODE
abc def ghi 66
echo "$RESULT"
abc
def
ghi
echo ${RESULT@Q}
$'abc\ndef\nghi'
printf '%q\n' "$RESULT"
$'abc\ndef\nghi'

但要显示变量定义,请使用:declare -p

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

2.解析数组中的多个输出,使用mapfile

将答案存储到变量中:myvar

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

展示:$myvar

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

考虑结果代码

如果您必须检查结果代码,您可以:

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

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

3.解析命令组中连续的多个输出read

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

显示变量:

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

我经常使用:

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

然后

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

考虑结果代码

相同的预置步骤:

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"

评论

0赞 F. Hauri - Give Up GitHub 10/15/2020
寻找有关换行符反斜杠和其他专业的选项!!help readhelp mapfile