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

问题描述

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

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

提取 PID 需要 sed 步骤)。

但是,可能存在没有 xpdf-process 正在运行的情况。然后将行嵌入到脚本中会很困难,因为它会立即中止并带有来自 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,\}.*$/\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,\}.*$/\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" 作为输入),我该怎么办? (我希望我的脚本不会被中止)

提前致谢。

解决方法

您可以使用 pkill(1)killall(1) 代替解析 ps 输出。 (不建议为编写脚本而解析人类可读的输出。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.
,

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

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

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 一起使用(如 this answer 中建议的那样),但它在BSD 或 POSIX 版本。例如我在 macOS 上的情况...

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

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...