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

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

abc
def
ghi

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

declare RESULT=$(./myscript)

$RESULT获取值

abc def ghi

是否有一种方法存储结果与换行符,或与'\n'字符,所以我可以输出它与'echo -e'?

331501 次浏览

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

echo "$RESULT"

你所展示的就是你所得到的:

echo $RESULT

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

这样做的另一个缺陷是命令替换 - $() -去掉尾随换行符。可能并不总是重要的,但如果你真的想保留输出的完全,你必须使用另一行和一些引用:

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

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

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

的文件
awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'
除了@l0b0给出的答案之外,我只是遇到了这样的情况:我需要保留脚本而且输出的任何尾随换行符,检查脚本的返回代码。 和l0b0的答案的问题是'echo x'重置$?回到零…所以我想出了这个非常狡猾的解决方案:

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

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

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

在尝试了这里的大多数解决方案之后,我发现最简单的方法是使用临时文件。我不确定您想对多行输出做什么,但您可以使用read逐行处理它。你唯一不能做的就是把它们都放到同一个变量里,但对于大多数实际目的来说,这个更容易处理。

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

快速hack使它做请求的动作:

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。可能不需要在这里解释,但有人在评论中抱怨。

解析多个输出

简介

所以你的myscript输出3行,可以看起来像:

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

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

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

myscript
abc
def
ghi

考虑# EYZ0

为了检查结果代码,test函数将变成:

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

显示# EYZ0:

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

考虑# EYZ0

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

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"

考虑# EYZ0

同样的前置步骤:

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"