Помните, что 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
нормально, поскольку оно имеет дело со списком идентификаторов процессов, а идентификаторы процессов не содержатпробелы или переводы строки.Это сохраняет простой код для простого случая;сложный случай использует временный файл для хранения вывода ps
, а затем сканирует его один раз для каждого имени процесса в командной строке.sort -u
гарантирует, что если какой-либо процесс будет соответствовать всем вашим ключевым словам (например, grep -E '(firefox|chrome)'
будет соответствовать обоим), будет отправлен только один сигнал.
Строки прерываний и т. Д. Гарантируют, что временный файл очищендо тех пор, пока кто-то не будет чрезмерно жесток к команде (пойманы следующие сигналы: HUP, INT, QUIT, PIPE и TERM, aka 1, 2, 3, 13 и 15; ноль ловит оболочку, выходящую по любой причине).Каждый раз, когда сценарий создает временный файл, у вас должна быть похожая ловушка вокруг использования файла, чтобы он был очищен, если процесс завершен.
Если вы чувствуете себя осторожно и у вас есть GNU Grep, вы можете добавить опцию -w
, чтобы имена, указанные в командной строке, соответствовали только целым словам.
Все вышеперечисленное будет работать практически с любой оболочкой в Bourne / Korn / POSIX / Bashсемейство (вам нужно будет использовать обратные метки со строгой оболочкой Борна вместо $(...)
, а ведущие скобки в условиях в case
также не допускаются с оболочкой Борна).Тем не менее, вы можете использовать массив для правильной обработки.
n=0
unset args # Force args to be an empty array (it could be an env var on entry)
for i in "$@"
do
args[$((n++))]="-e"
args[$((n++))]="$i"
done
kill $(ps -A | fgrep "${args[@]}" | awk '{print $1}')
Это тщательно сохраняет интервалы в аргументах и использует точные совпадения для имен процессов.Избегает временных файлов.Показанный код не проверяется на отсутствие аргументов;это должно быть сделано заранее.Или вы можете добавить строку args[0]='/collywobbles/'
или что-то подобное, чтобы предоставить команду по умолчанию - несуществующую - для поиска.