Bash:捕获在后台运行的命令的输出
我正在尝试编写一个bash脚本,它将获得在后台运行的命令的输出。不幸的是,我无法让它工作,我分配给输出的变量是空的-如果我用echo命令替换分配,那么一切都会按预期工作Bash:捕获在后台运行的命令的输出,bash,Bash,我正在尝试编写一个bash脚本,它将获得在后台运行的命令的输出。不幸的是,我无法让它工作,我分配给输出的变量是空的-如果我用echo命令替换分配,那么一切都会按预期工作 #!/bin/bash function test { echo "$1" } echo $(test "echo") & wait a=$(test "assignment") & wait echo $a echo done 此代码生成以下输出: echo done 将作业更改为 a=
#!/bin/bash
function test {
echo "$1"
}
echo $(test "echo") &
wait
a=$(test "assignment") &
wait
echo $a
echo done
此代码生成以下输出:
echo
done
将作业更改为
a=`echo $(test "assignment") &`
工作正常,但似乎应该有更好的方法来实现这一点。捕获后台命令输出的一种方法是将其输出重定向到文件中,并在后台进程结束后从文件中捕获输出:
test "assignment" > /tmp/_out &
wait
a=$(</tmp/_out)
测试“分配”>/tmp/\u out&
等待
a=$(Bash确实有一个称为进程替换的特性来实现这一点
$ echo <(yes)
/dev/fd/63
这里的问题是yes
进程同时终止,因为它收到了一个SIGPIPE(stdout上没有读卡器)
解决方案是以下构造
$ exec 3< <(yes) # Save stdout of the 'yes' job as (input) fd 3.
$exec 3 $for i in 1 2 3;do read在Bash中处理协进程的一种非常健壮的方法是使用coproc
内置
假设您有一个名为banana
的脚本或函数,您希望在后台运行,在执行一些操作时捕获其所有输出,并等待完成。我将使用以下内容进行模拟:
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
然后使用coproc
运行banana
,如下所示:
coproc bananafd { banana; }
这类似于运行banana&
,但有以下附加功能:它创建两个文件描述符,它们位于数组bananafd
中(在索引0
处用于输出,索引1
处用于输入)。您将使用read
内置命令捕获banana
的输出:
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
试试看:
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
警告:你必须在香蕉
结束之前完成东西
!如果大猩猩比你快:
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
在这种情况下,您将获得如下错误:
./banana: line 22: read: : invalid file descriptor specification
您可以检查是否太晚(即,您是否花了太长时间来处理内容),因为在coproc
完成后,bash会删除数组bananafd
中的值,这就是我们获得上一个错误的原因
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
if [[ -n ${bananafd[@]} ]]; then
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
else
echo "oh no, I took too long doing my stuff..."
fi
最后,如果你真的不想错过gorilla的任何一个动作,即使你花了太长时间在东西上,你也可以将香蕉
的文件描述符复制到另一个fd,3
例如,做你的东西,然后从3
读取:
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
# Copy file descriptor banana[0] to 3
exec 3>&${bananafd[0]}
stuff
IFS= read -d '' -u 3 output
echo "$output"
这将非常有效!最后的读取
也将扮演等待
的角色,因此输出
将包含香蕉
的完整输出
这是伟大的:没有临时文件处理(bash处理一切安静)和100%纯bash
希望这有帮助!是否有不使用文件就可以执行此操作的方法?是的,有(仅限bash)这是唯一一个对我有效的方法,谢谢!谢谢,看起来很棒!有没有任何方法可以在不循环每一行的情况下从读卡器获取所有输出?我假设调用read line也可以调用“wait”是正确的吗?警告:如果你想在现实生活脚本中使用此功能,你需要使用mktemp
创建文件backingfile
,甚至使用trap
,因为组合
/exec
/rm
不是原子的!@gniourf\gniourf:不,原子性没有问题。不,trap根本不会产生任何效果omic。对讨论的结论不完全清楚。是否缺少对wait
的调用?同样,在cat之后,感谢您的详细回复。我最终使用了Jo So的答案,因为这似乎是一种将命令输出传递给另一个fd的更简单的方法-尽管coproc似乎很有用。@user2352030 JoSo的答案并不简单呃,一点也不!它甚至更复杂,因为它迫使你创建一个文件并将其删除。如果你想以安全的方式使用他的答案,你需要使用mktemp
…甚至可能使用trap
!不要被看似简单的东西所愚弄!也许我错了,但从我的理解来看,他的便携shell方法迫使我这么做这样做,但是使用bash构造exec 3<@user2352030coproc
的另一个优点是,您可以通过检查数组bananafd
是否已设置来确定后台进程是否已完成。嗯……这看起来像是解决我遇到的问题的最佳解决方案,但事实上正式只有一个coproc
可能会立即运行,这限制了它的运行。在我的例子中,它恰好适用于四个coproc
(我在我的.bashrc
中运行了四个昂贵的命令来初始化四个不同的bash变量,我不喜欢花2.5秒的时间等待它们顺序运行,因为并行执行可能会将时间减少到0.9秒左右),但我抛出了关于在第一个coproc
之后启动的三个coproc
的警告(因为第一个还没有完成)。
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
./banana: line 22: read: : invalid file descriptor specification
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
if [[ -n ${bananafd[@]} ]]; then
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
else
echo "oh no, I took too long doing my stuff..."
fi
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
# Copy file descriptor banana[0] to 3
exec 3>&${bananafd[0]}
stuff
IFS= read -d '' -u 3 output
echo "$output"