쉘 스크립트와 쉘에서 다른 동작이 있습니까?

쉘 스크립트와 쉘에서 다른 동작이 있습니까?

업데이트:

대본에서 (의미하려고 하다가 ) 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_namekill 명령 사용에 대한 메시지를 받지 못합니다. 대신 다음을 얻습니다.

Terminated: 15

반환 코드도 확인했습니다. 쉘에 명령을 수동으로 작성하여 존재하지 않는 프로세스를 종료하려고 echo $?하면 1. 그러나 스크립트를 호출하여 존재하지 않는 프로세스를 종료하려고 echo $?하면 143.

여기서 무슨 일이 일어나고 있는 걸까요? 동일한 명령을 셸에 수동으로 작성하여 실행할 때와 셸 스크립트 내에서 실행할 때 다른 동작이 관찰되는 이유는 무엇입니까?

참고: sh와 내 작업 쉘은 모두 bash.

보너스: 다음을 사용하여 내 쉘 스크립트를 보다 효율적이고 우아한 방식으로 작성할 수 있습니까?POSIX 유틸리티? 그렇다면 어떻게?

답변1

휴대성 노트. 출력 형식은 ps -A다음과 같습니다.POSIX에 의해 지정되지 않음Unix를 준수하지 않는 시스템(예: FreeBSD)의 경우(출력 형식 섹션과 옵션 설명이 -f모두 태그되어 있음 을 알 수 있습니다)XSI사양에서), 따라서 실제로 이식 가능하게 안정적으로 후처리할 수는 없습니다.

예를 들어, Linux에서는 psfrom을 사용하면 열(여기서 명령 args가 아닌 프로세스 이름이 있음) 을 procps출력하는 반면 FreeBSD에서는 args를 사용하여 출력 합니다.PID TTY TIME CMDCMDPID TT STAT TIME COMMANDCOMMAND

의 사용법을 고려 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/shPOSIX 쉘이 아닐 수도 있습니다. 그러나 실제로 she-bang은 대부분의 POSIX 시스템에서 지원되며 /bin/shPOSIX 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 -rPOSIX이지만 Bourne은 아닙니다).

또는 , 또는 (모두 POSIX 명령이 아님)을 사용하면 ksh93내장 정규식 일치 기능이 있는 쉘이 됩니다 bash.zshyash

#! /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
}

관련 정보