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


580

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

abc
def
ghi

在另一个脚本中,我称:

declare RESULT=$(./myscript)

$RESULT获得价值

abc def ghi

是否可以用换行符或'\ n'字符存储结果,以便我可以用' echo -e' 输出结果?


1
这让我感到惊讶。您没有$(cat ./myscipt)吗?否则,我希望它会尝试执行abc,def和ghi命令
Johannes Schaub-litb

@litb:是的,我想是这样;您还可以使用$(<./ myscript)避免执行命令。
乔纳森·勒夫勒

2
(注意:上面的两个注释指的是该问题的修订版,开始时我有一个脚本“ myscript”,其中包含以下内容,导致了这些问题。该问题的当前修订版(我有一个脚本“的MyScript”输出以下)使评论多余然而,修改为2011-11-11,长两条评论意见后。
乔纳森·莱弗勒

Answers:


1077

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

echo "$RESULT"

您显示的是您从中得到的:

echo $RESULT

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


69
@troelskn:区别在于(1)变量的双引号版本保留值的内部间距,该值与变量,换行符,制表符,多个空格以及所有变量中所表示的值完全相同,而(2)未引用的版本替换该值一个或多个空格,制表符和换行符的每个序列,并用一个空格隔开。因此,(1)保留输入变量的形状,而(2)创建一个可能很长的单行输出,其中“单词”由单个空格分隔(其中“单词”是非空白字符的序列;需要不能是任何字母数字)。
乔纳森·莱夫勒

23
为了使答案更易于理解:答案告诉echo“ $ RESULT”保留换行符,而echo $ RESULT则不保留换行符。
Yu Shen

在某些情况下,这无法保留换行符和开头的空格。
CommaToast

3
@CommaToast:您要详细说明吗?尾随换行符丢失;周围没有简单的方法。领先的空白-我不知道在任何情况下它们会丢失。
乔纳森·勒夫勒


90

与此另一个缺陷是,命令替换 - $()-带拖尾换行符。可能并不总是很重要,但是如果您确实想准确保留输出的内容,则必须使用另一行和一些引号:

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

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


4
我不得不用一个破损的外壳工作一段时间,该外壳没有从命令替换中删除最后一个换行符(这不是 进程替换),并且它几乎破坏了所有内容。例如,如果您这样做了pwd=`pwd`; ls $pwd/$file,则在之前会有换行符/,并且用双引号将名称引起来并没有帮助。它很快修复。这是在1983-5年ICL Perq PNX上发布的;shell没有$PWD内置变量。
乔纳森·莱夫勒

19

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

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

3
如果行中有空格,则将计算字段(空格之间的内容)而不是行。
Liam 2015年

2
您将使用readarray和处理替换而不是命令替换:readarray -t RESULT < <(./myscript>
chepner '16

15

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

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

这很可爱,实际上我有一个用例,我想要一个die ()可以接受任意退出代码和可选消息的usage ()函数,并且我想使用另一个函数来提供消息,但是换行符不断被压缩,希望这可以让我解决这个问题。
Dragon788

1

在这里尝试了大多数解决方案之后,我发现最容易的事情是显而易见的-使用临时文件。我不确定要对多行输出进行处理,但是可以使用read逐行处理它。您唯一不能真正做的就是轻松地将所有内容都放在同一个变量中,但是对于大多数实际目的而言,这很容易处理。

./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循环内压缩标准输入,从而为您提供一个脚本,该脚本将运行一行,清除标准输入,然后退出。像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。可能不需要在这里解释,但是有人在评论中抱怨。


我也尝试了其他解决方案,根据您的第一个建议,我终于使脚本工​​作了
Kar.ma,2013年

1
Downvote:这过于复杂,并且无法避免多个常见的bash陷阱
三胞胎

Ya几天前有人告诉我有关stdin filehandle怪异问题的消息,我当时就像“哇”。Lemme很快就添加了一些东西。
user1279741

在bash中,你可以使用read命令扩展-u 3从文件描述符3.阅读
乔纳森·莱弗勒

为什么不趁...做...完成<<(。myscript.sh)
jtgd

0

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

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

4
好吧,那不是bash,那是awk。
vadipp
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.