
ОБНОВЛЯТЬ:
Я изменил grep $1
часть на grep '$1'
(хотя я пытался иметь в виду grep "$1"
) в сценарии и на этот раз у меня получилось
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
сообщение (вместо Terminated: 15
сообщения). Я не понимаю, что происходит.
ВОПРОС:
Я написал простой скрипт оболочки под названием mykill
.
mykill
:
#!/bin/sh
kill `ps -A | grep $1 | grep -v 'grep' | grep -Eom 1 '^[0-9]+'`
Однако есть странное поведение. Когда я пишу строку:
kill `ps -A | grep process_name | grep -v 'grep' | grep -Eom 1 '^[0-9]+'`
вручную на bash, если ничего не выводится ps -A | grep process_name
, я получаю следующее:
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
Если что-то происходит, команда выполняется правильно и завершается без вывода сообщений.
Теперь, если я запускаю скрипт, выполняя файл mykill
, и что-то появляется в качестве вывода ps -A | grep process_name
, скрипт выполняется правильно и завершается без вывода сообщений, что равносильно выполнению команды вручную.
Но если ничего не выводится ps -A | grep process_name
, я не получаю сообщение об использовании команды kill. Вместо этого я получаю:
Terminated: 15
Я также проверил коды возврата. После того, как я пытаюсь завершить несуществующий процесс, вручную написав команду в оболочке, echo $?
выдает 1
. Однако, после того, как я пытаюсь завершить несуществующий процесс, вызвав скрипт, echo $?
выдает 143
.
Что здесь происходит? Почему я наблюдаю разное поведение при выполнении одной и той же команды, вручную прописав ее в оболочке, по сравнению с выполнением ее в скрипте оболочки?
ПРИМЕЧАНИЕ: Обе sh
мои рабочие оболочки — bash
.
БОНУС: Можно ли написать мой скрипт оболочки более эффективно и/или элегантно, используя толькоУтилиты POSIX? Если да, то как?
решение1
Примечание о переносимости. Формат вывода ps -A
дляне указано в POSIXдля систем, несовместимых с Unix (например, FreeBSD) (вы заметите, что разделы формата вывода и описание опции -f
помечены тегамиXSIв спецификации), поэтому вы не сможете выполнить его надежную постобработку для переноса.
Например, при использовании ps
from procps
в Linux будет выведено, PID TTY TIME CMD
столбцов (где CMD
— имя процесса, а не аргументы команды), тогда как во FreeBSD будет выведено PID TT STAT TIME COMMAND
( COMMAND
где — аргументы).
Учитывая ваше использование grep -v grep
, я предполагаю, что вы ожидаете последнего или, по крайней мере, ps -A
вывода аргументов команды, которую выполнил процесс, а не просто имени процесса (обычно выведенного из имени файла последней выполненной команды или первого (0-го ) аргумента).
Если вы grep
хотите работать grep
только с аргументами команды, то следует использовать:
ps -A -o pid= -o args=
вывод которого определен POSIX.
Теперь ваша проблема в том, что mykill
он убивает себя, потому что mykill foo
спички foo
...
Другая проблема в том, что это mykill grep
никого не убьет.
Здесь вы можете сделать следующее:
#! /bin/sh -
PATTERN=${1?} export PATTERN
trap '' TERM # ignore SIGTERM for the shell and its children
ps -A -o pid= -o args= | awk '$0 ~ ENVIRON["PATTERN"] {
system("kill " $1); exit}'
(обратите внимание, что POSIX не определяет путь к sh
утилите POSIX или механизму she-bang, поэтому /bin/sh
это может быть не оболочка POSIX. Однако на практике she-bang поддерживается большинством систем POSIX и /bin/sh
является либо POSIX sh
, либо Bourne sh
, и приведенный выше код должен работать в обеих системах).
Хотя это не идеально, так как всегда возвращает истинный (0) статус выхода, даже если процесс не найден. Лучшим подходом будет:
#! /bin/sh -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
ps -A -o pid= -o args= | grep -e "$pattern" | {
read pid args && kill "$pid"
}
В обоих случаях мы завершаем только первый процесс сопоставления, как и grep -m 1
предполагает ваш подход.
Теперь, trap '' SIGTERM
убедившись, что наши процессы не будут завершены, что было бы нормально, если бы мы их завершили.всесоответствующие процессы, но поскольку здесь мы убиваем только первый соответствующий процесс, проблема в том, что этот первый процесс вполне может быть запущенным mykill pattern
или grep pattern
.
Вместо того чтобы добавлять некоторые процессы grep -ve grep -e mykill
(что не будет надежным решением, поскольку может исключить больше процессов, чем предполагалось), вы можете попробовать сравнить идентификаторы соответствующих процессов.
#! /bin/sh -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
# just in case
psoutput=$(exec ps -A -o pid= -o ppid= -o args=)
printf '%s\n' "$psoutput" | grep -e "$pattern" | {
while read -r pid ppid args; do
if [ "$pid" -ne "$$" ] && [ "$ppid" -ne "$$" ]; then
kill "$pid"
exit # with the exit status of kill above
fi
done
exit 1 # not found
}
(обратите внимание, что $(...)
и read -r
являются POSIX, но не Bourne).
Или с помощью ksh93
, bash
, zsh
или yash
(ни одна из которых не является командой POSIX), то есть оболочки со встроенным сопоставлением регулярных выражений:
#! /bin/bash -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
# just in case
psoutput=$(exec ps -A -o pid= -o ppid= -o args=)
printf '%s\n' "$psoutput" | {
while read -r pid ppid args; do
if ((pid != $$ && ppid != $$)) && [[ $args =~ $pattern ]]; then
kill "$pid"
exit # with the exit status of kill above
fi
done
exit 1 # not found
}