记住当使用多个参数时,
grep
的作用 - 第一个参数是要查找的单词,其余参数是要扫描的文件。
还要记住,
$*
,
"$*"
和
$@
在参数中丢失空格,而神奇的
"$@"
符号则不会。
因此,为了处理您的情况,您需要修改调用
grep
的方式。您可以使用每个参数的选项和
grep -F
(也称为
fgrep
),或者您可以使用交替的
grep -E
(也称为
egrep
)。在某种程度上,这取决于您是否必须处理包含管道符号的参数。
使用单个
grep
调用可靠地完成这项工作非常棘手;您最好容忍多次运行管道的开销:
for process in "$@"
do
kill $(ps -A | grep -w "$process" | awk '{print $1}')
done
如果多次运行ps
的开销太大(这让我很痛苦,但我没有测量成本),那么你可能会做以下操作:
case $# in
(0) echo "Usage: $(basename $0 .sh) procname [...]" >&2; exit 1;;
(1) kill $(ps -A | grep -w "$1" | awk '{print $1}');;
(*) tmp=${TMPDIR:-/tmp}/end.$$
trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15
ps -A > $tmp.1
for process in "$@"
do
grep "$process" $tmp.1
done |
awk '{print $1}' |
sort -u |
xargs kill
rm -f $tmp.1
trap 0
;;
esac
使用普通的
xargs
是可以的,因为它处理的是进程ID列表,而进程ID不包含空格或换行符。这保持了简单情况下的简单代码;复杂情况下使用临时文件来保存
ps
命令的输出,然后针对命令行中的每个进程名称扫描一次该文件。
sort -u
确保如果某个进程恰好与您的所有关键字匹配(例如,
grep -E '(firefox|chrome)'
将匹配两者),则只发送一个信号。
陷阱行等确保临时文件被清除,除非有人对命令过度粗暴(捕获的信号是HUP、INT、QUIT、PIPE和TERM,即1、2、3、13和15;零捕获任何原因导致shell退出)。任何时候,如果脚本创建临时文件,您应该在使用该文件时周围设置类似的陷阱,以便在进程终止时清理它。
如果您感到谨慎,并且您有GNU Grep,则可以添加
-w
选项,以便命令行提供的名称仅匹配整个单词。
以上所有内容都适用于Bourne/Korn/POSIX/Bash系列中的几乎所有shell(在Bourne shell中严格使用反引号代替
$(...)
,并且在
case
条件中的前导括号也不允许使用Bourne shell)。但是,您可以使用数组来正确处理事情。
n=0
unset args
for i in "$@"
do
args[$((n++))]="-e"
args[$((n++))]="$i"
done
kill $(ps -A | fgrep "${args[@]}" | awk '{print $1}')
这个方法会仔细保留参数间的空格,并使用精确匹配进程名称。它避免了使用临时文件。这段代码没有对零个参数进行验证,需要事先处理。或者你可以添加一行
args[0]='/collywobbles/'
或类似的内容来提供一个默认的、不存在的命令来搜索。
killall
而不是使用 grep 呢?例如killall chrome; killall firefox
或者for pname in $*; do killall "$pname"; done
? - khachikkillall "$@"
就足够了。顺便提一下,永远不要使用$@
,很少使用$*
,当你想要使用"$@"
时,请不要使用"$*"
。 - Mark Edgar