英文:
Kill bash command when line is found
问题
我希望在输出中找到某个字符串时杀死一个bash命令。
为了澄清,我希望解决方案类似于timeout
命令:
timeout 10s looping_program.sh
这将执行脚本:looping_program.sh
并在执行10秒后终止脚本。
相反,我想要类似于:
regexout "^Success$" looping_program.sh
这将执行脚本,直到在程序的stdout
中匹配到只有Success
的一行。请注意,我假设这个looping_program.sh
不会因为某种原因在输出Success
的同时退出,所以如果我不关心之后发生的事情,等待程序退出将浪费时间。
因此,类似于:
bash -e looping_program.sh > /tmp/output &
PID="$(ps aux | grep looping_program.sh | head -1 | tr -s ' ' | cut -f 2 -d ' ')"
echo $PID
while :; do
echo "$(tail -1 /tmp/output)"
if [[ "$(tail -1 /tmp/output)" == "Success" ]]; then
kill $PID
exit 0
fi
sleep 1
done
其中looping_program.sh
是这样的:
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Success"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
但这不够健壮(使用了一个临时文件...可能会杀死其他程序...),我希望它只是一个命令。是否存在类似的东西?如果没有,我可能会编写一个C程序来完成它。
附言:我提供了我的代码作为我希望程序执行的示例。它并没有采用良好的编程实践。其他评论者的注意事项:
@KamilCuk 不要使用临时文件。使用一个fifo。
@pjh 请注意,任何涉及在shell代码中使用PID和kill的方法都有可能杀死错误的进程。只在绝对必要的情况下在shell程序中使用kill。
其他用户还提出了更多建议,我只是想确保没有人看到这个问题,然后认为模仿他们的代码是个好主意。
英文:
I want to kill a bash command when I found some string in the output.
To clarify, I want the solution to be similar to a timeout
command:
timeout 10s looping_program.sh
Which will execute the script: looping_program.sh
and kill the script after 10 seconds of execute.
Instead I want something like:
regexout "^Success$" looping_program.sh
Which will execute the script until it matches a line that just says Success
in the stdout
of the program.
Note that I'm assuming that this looping_program.sh
does not exit at the same time it outputs Success
for whatever reason, so simply waiting for the program to exit would waste time if I don't care about what happens after that.
So something like:
bash -e looping_program.sh > /tmp/output &
PID="$(ps aux | grep looping_program.sh | head -1 | tr -s ' ' | cut -f 2 -d ' ')"
echo $PID
while :; do
echo "$(tail -1 /tmp/output)"
if [[ "$(tail -1 /tmp/output)" == "Success" ]]; then
kill $PID
exit 0
fi
sleep 1
done
Where looping_program.sh
is something like:
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Success"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
But that is not very robust (uses a single tmp file... might kill other programs...) and I want it to just be one command. Does something like this exist? I may just write a c program to do it if not.
P.S.: I provided my code as an example of what I wanted the program to do. It does not use good programming practices. Notes from other commenters:
@KamilCuk Do not use temporary file. Use a fifo.
@pjh Note that any approach that involves using kill with a PID in shell code runs the risk of killing the wrong process. Use kill in shell programs only when it is absolutely necessary.
There are more suggestions below from other users, I just wanted to make sure no one came across this and thought it would be good to model their code after.
答案1
得分: 2
looping_program() {
for i in 1 2 3; do echo $i; sleep 1; done
echo Success
yes
}
coproc looping_program
while IFS= read -r line; do
if [[ "$line" =~ Success ]]; then
break
fi
done <&${COPROC[0]}
exec {COPROC[0]}>&- {COPROC[1]}>&-
kill ${COPROC_PID}
wait ${COPROC_PID}
英文:
looping_program() {
for i in 1 2 3; do echo $i; sleep 1; done
echo Success
yes
}
coproc looping_program
while IFS= read -r line; do
if [[ "$line" =~ Success ]]; then
break
fi
done <&${COPROC[0]}
exec {COPROC[0]}>&- {COPROC[1]}>&-
kill ${COPROC_PID}
wait ${COPROC_PID}
Notes:
- Do not use temporary file. Use a fifo.
- Do not use
tail -n1
to read last line. Read from the stream in a loop. - Do not repeat
tail -1
twice. Cache the result. - Wait for pid after killing to synchronize.
- When you're using a coprocess, use
COPROC_PID
to get the PID - When you're not using a coprocess, use
$!
to get the PID of a background process started from the current shell. - When you can't use
$!
(because the process you're trying to get a PID of was not spawned in the background as a direct child of the current shell), do not useps aux | grep
to get the pid. Usepgrep
. - Do not use
echo $(stuff)
. Just run thestuff
, no echo.
答案2
得分: 1
#!/usr/bin/env -S expect -f
set timeout -1
spawn ./looping_program.sh
expect "Success"
send -- "\x03"
expect eof
Call it looping_killer
:
$ ./looping_killer
spawn ./looping_program.sh
Fail
Fail
Fail
Success
^C
To pass the program and pattern:
./looping_killer some_program "some pattern"
You'd change the expect script to
#!/usr/bin/env -S expect -f
set timeout -1
spawn [lindex $argv 0]
expect -- [lindex $argv 1]
send -- "\x03"
expect eof
英文:
With [tag:expect]
#!/usr/bin/env -S expect -f
set timeout -1
spawn ./looping_program.sh
expect "Success"
send -- "\x03"
expect eof
Call it looping_killer
:
$ ./looping_killer
spawn ./looping_program.sh
Fail
Fail
Fail
Success
^C
To pass the program and pattern:
./looping_killer some_program "some pattern"
You'd change the expect script to
#!/usr/bin/env -S expect -f
set timeout -1
spawn [lindex $argv 0]
expect -- [lindex $argv 1]
send -- "\x03"
expect eof
答案3
得分: 0
Assuming that your looping program exists when it tries to write to a broken pipe, this will print all output up to and including the 'Success' line and then exit:
./looping_program | sed '/^Success$/q';
You may need to disable buffering of the looping program output. See Force line-buffering of stdout in a pipeline and How to make output of any shell command unbuffered? for ways to do it.
See Should I save my scripts with the .sh extension? and Erlkonig: Commandname Extensions Considered Harmful for reasons why I dropped the '.sh' suffix.
Note that any approach that involves using kill
with a PID in shell code runs the risk of killing the wrong process. Use kill
in shell programs only when it is absolutely necessary.
英文:
Assuming that your looping program exists when it tries to write to a broken pipe, this will print all output up to and including the 'Success' line and then exit:
./looping_program | sed '/^Success$/q'
- You may need to disable buffering of the looping program output. See Force line-buffering of stdout in a pipeline and How to make output of any shell command unbuffered? for ways to do it.
- See Should I save my scripts with the .sh extension? and Erlkonig: Commandname Extensions Considered Harmful for reasons why I dropped the '.sh' suffix.
Note that any approach that involves using kill
with a PID in shell code runs the risk of killing the wrong process. Use kill
in shell programs only when it is absolutely necessary.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论