
업데이트:
대본에서 (의미하려고 하다가 ) 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사양에서), 따라서 실제로 이식 가능하게 안정적으로 후처리할 수는 없습니다.
예를 들어, Linux에서는 ps
from을 사용하면 열(여기서 명령 args가 아닌 프로세스 이름이 있음) 을 procps
출력하는 반면 FreeBSD에서는 args를 사용하여 출력 합니다.PID TTY TIME CMD
CMD
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는 POSIX 유틸리티의 경로 sh
나 she-bang 메커니즘을 지정하지 않으므로 /bin/sh
POSIX 쉘이 아닐 수도 있습니다. 그러나 실제로 she-bang은 대부분의 POSIX 시스템에서 지원되며 /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
}