쉘 스크립트는 쉘과 다르게 동작합니까?

쉘 스크립트는 쉘과 다르게 동작합니까?

고쳐 쓰다:

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 -APOSIX가 지정되지 않았습니다.Unix와 호환되지 않는 시스템(예: FreeBSD)의 경우(출력 형식 섹션과 옵션 설명이 -f표시되어 있음 을 알 수 있습니다)XSI사양에서) 실제로 안정적으로 후처리할 수 없습니다.

ps예를 들어, from Linux에서는 열(여기서 명령 매개변수가 아닌 프로세스 이름) 을 procps인쇄하는 반면, FreeBSD에서는 인쇄 (여기서 매개변수)를 인쇄합니다.PID TTY TIME CMDCMDPID TT STAT TIME COMMANDCOMMAND

귀하의 사용 사례를 고려하면 , 프로세스 이름(보통 마지막으로 실행된 명령이나 첫 번째(0번째 ) 인수에서 파생됨) 뿐만 아니라 grep -v grep후자를 기대하거나 최소한 ps -A프로세스에서 실행한 명령의 인수를 출력할 것으로 예상됩니다.

grep명령 매개변수 만 사용 하려면 grep다음을 사용해야 합니다.

ps -A -o pid= -o args=

출력은 POSIX에 의해 지정됩니다.

이제, 당신의 문제는 그것이 일치 mykill때문에 스스로를 죽이고 있다는 것입니다 .mykill foofoo

또 다른 문제는 아무것도 죽이지 않는다는 것입니다 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 쉘이 아닐 수도 있습니다. 그러나 실제로 대부분의 POSIX 시스템은 she-bang을 지원하며 /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하세요 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
}

관련 정보