고쳐 쓰다:
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 Linux에서는 열(여기서 명령 매개변수가 아닌 프로세스 이름) 을 procps
인쇄하는 반면, FreeBSD에서는 인쇄 (여기서 매개변수)를 인쇄합니다.PID TTY TIME CMD
CMD
PID TT STAT TIME COMMAND
COMMAND
귀하의 사용 사례를 고려하면 , 프로세스 이름(보통 마지막으로 실행된 명령이나 첫 번째(0번째 ) 인수에서 파생됨) 뿐만 아니라 grep -v grep
후자를 기대하거나 최소한 ps -A
프로세스에서 실행한 명령의 인수를 출력할 것으로 예상됩니다.
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는 POSIX 유틸리티에 대한 경로 sh
나 she-bang 메커니즘을 지정하지 않으므로 /bin/sh
POSIX 쉘이 아닐 수도 있습니다. 그러나 실제로 대부분의 POSIX 시스템은 she-bang을 지원하며 /bin/sh
POSIX sh
또는 Bourne이 될 수 있습니다 sh
. 위의 코드는 두 가지 모두에 대해 작동해야 합니다.
그러나 이는 프로세스를 찾을 수 없더라도 항상 true(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
일부 프로세스를 추가하는 대신 일치하는 프로세스의 프로세스 ID를 비교해 볼 수 있습니다(예상보다 더 많은 프로세스를 제외할 수 있으므로 완벽한 방법은 아닙니다).
#! /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은 아닙니다).
또는 정규식 일치 기능이 내장된 셸인 , 또는 (POSIX 명령도 아님)을 ksh93
사용 bash
하세요 zsh
.yash
#! /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
}