Различное поведение в скрипте оболочки и в оболочке?

Различное поведение в скрипте оболочки и в оболочке?

ОБНОВЛЯТЬ:

Я изменил 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в спецификации), поэтому вы не сможете выполнить его надежную постобработку для переноса.

Например, при использовании psfrom 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
}

Связанный контент