如何评估 bash 脚本中管道的结果

How to evaluate results of pipes in a bash script

我需要帮助解决以下问题: 我想杀死一个程序的所有实例,比方说 xpdf。 在提示符下,以下内容按预期工作:

$ ps -e | grep xpdf | sed -n -e "s/^[^0-9]*\([0-9]*\)[^0-9]\{1,\}.*$//p" | xargs kill -SIGTERM

(提取 PID 需要 sed 步骤)。

但是,可能存在没有 xpdf 进程的情况 运行。这样就很难将该行嵌入到脚本中,因为它会在 kill 发出消息后立即中止。我该怎么办?

我试过(在脚本中)

#!/bin/bash
#
set -x
test=""
echo "test = < $test >"
test=`ps -e | grep xpdf | sed -n -e "s/^[^0-9]*\([0-9]*\)[^0-9]\{1,\}.*$//p"`
echo "test = < $test >"
if [ -z "$test" ]; then echo "xpdf läuft nicht";
else echo "$test" | xargs -d" " kill -SIGTERM
fi

当运行上面的脚本我得到

$ Kill_ps
+ test=
+ echo 'test = <  >'
test = <  >
++ ps -e
++ grep xpdf
++ sed -n -e 's/^[^0-9]*\([0-9]*\)[^0-9]\{1,\}.*$//p'
+ test='21538
24654
24804
24805'
+ echo 'test = < 21538
24654
24804
24805 >'
test = < 21538
24654
24804
24805 >
+ '[' -z '21538
24654
24804
24805' ']'
+ xargs '-d ' kill -SIGTERM
+ echo '21538
24654
24804
24805'
kill: failed to parse argument: '21538
24654
24804
24805

一些意想不到的事情发生了:在测试中,PID 多于进程

在提示符处:

$ ps -e | grep xpd
21538 pts/3    00:00:00 xpdf.real
24654 pts/2    00:00:00 xpdf.real

当运行再次执行脚本时,24* PID 发生变化。

所以这是我的问题:

  1. 额外的 PID 从何而来?
  2. 我能做些什么来处理这种情况,在这种情况下我想杀死的进程都不是 运行(或者为什么 xargs 不接受 echo "$test" 作为输入)? (我希望我的脚本不被中止)

提前致谢。

此答案适用于 killallpkill 中建议)对您来说不够的情况。例如,如果你真的想打印 "xpdf läuft nicht" 如果没有 pid 可以杀死或应用 kill -SIGTERM 因为你想确定你发送给你的 pids 或其他什么的信号。

您可以使用 bash 循环代替 xargssed。迭代 CSV/column 输出非常简单:

count=0
while read -r uid pid ppid trash; do
  kill -SIGTERM $pid
  (( count++ ))
done < <(ps -ef|grep xpdf)
[[ $count -le 0 ]] && echo "xpdf läuft nicht"

使用 pgrep 有一种更快的方法(前一个方法是为了说明如何使用 bash 迭代基于列的命令输出):

count=0
while read -r; do
  kill -SIGTERM $REPLY
  (( count++ ))
done < <(pgrep xpdf)
[[ $count -le 0 ]] && echo "xpdf läuft nicht"

如果您的 xargs 版本可以为您提供 --no-run-if-empty,您仍然可以将它与 pgrep 一起使用(如 中的建议),但事实并非如此在 BSD 或 POSIX 版本上可用。例如我在 macOS 上的情况...

awk 也可以做到这一点(管道后只有一个命令):

ps -ef|awk 'BEGIN {c="0"} {if([=12=] ~ "xpdf" && !([=12=] ~ "awk")){c++; system("kill -SIGTERM ")}} END {if(c <= 0){print "xpdf läuft nicht"}}'

你可以用pkill(1) or killall(1) instead of parsing ps output. (Parsing human-readable output for scripting is not recommended. This作为例子。)

用法:

 pkill xpdf
 killall xpdf # Note that on solaris, it kills all the processes that you can kill. https://unix.stackexchange.com/q/252349#comment435182_252356
 pgrep xpdf # Lists pids
 ps -C xpdf -o pid=  # Lists pids

请注意,pkillkillallpgrep 等工具的性能与 ps -e | grep 类似。因此,如果您执行 pkill sh,它会尝试杀死 shbashssh 等,因为它们都匹配模式。

但是要回答关于在输入中没有任何内容时从 运行 跳过 xargs 的问题,您可以使用 -r 选项 (GNU) xargs.

   -r, --no-run-if-empty
          If the standard input does not contain any nonblanks, do not run the command.
          Normally, the command is run once even if there is no input.
          This option is a GNU extension.