Bash의 PID가 배열로 분할되지 않는 이유

Bash의 PID가 배열로 분할되지 않는 이유

셀러리에는 프로세스가 많기 때문에 서버에서 셀러리를 다시 시작하고 싶습니다. 모든 프로세스 ID를 쿼리하고 종료하기 위해 다음 스크립트를 작성했습니다.

#
# stop celery process
#
PID=`ps -ef|grep -w ${CELERY_PROGRAM_NAME}|grep -v grep|cut -c 9-15`
if [ -z "${PID}" ]; then
  echo "Process aready down..."
else
    array=(${PID//\n/})
    for var in "${array[@]}"
    do
      single_pid=`echo ${var} | awk 'gsub(/^ *| *$/,"")' `
      if [[ ${single_pid} -gt 1 ]]; then
          kill -15 "${single_pid}"
      else
          echo "Process ${PROGRAM_NAME} not found"
      fi
    done
fi

로그에서 pid가 배열로 변환되지 않았고 다음 단계가 올바르게 분할되지 않은 것을 발견했습니다. GitHub Actions에서 원격으로 이 스크립트를 실행합니다. 다음은 GitHub Actions의 로그 출력입니다.

======CMD======
cd /opt/apps/pydolphin
. /opt/apps/pydolphin/restart.sh

======END======
err: +/opt/apps/pydolphin/restart.sh:16> PROGRAM_NAME=schedulespider.py 
err: +/opt/apps/pydolphin/restart.sh:17> CELERY_PROGRAM_NAME=celery 
err: +/opt/apps/pydolphin/restart.sh:18> PYTHON_BIN_PATH=/usr/bin/python3 
err: +/opt/apps/pydolphin/restart.sh:23> PID=+/opt/apps/pydolphin/restart.sh:23> ps -ef
err: +/opt/apps/pydolphin/restart.sh:23> PID=+/opt/apps/pydolphin/restart.sh:23> grep -w celery
err: +/opt/apps/pydolphin/restart.sh:23> PID=+/opt/apps/pydolphin/restart.sh:23> grep -v grep
err: +/opt/apps/pydolphin/restart.sh:23> PID=+/opt/apps/pydolphin/restart.sh:23> cut -c 9-15
err: +/opt/apps/pydolphin/restart.sh:23> PID='  9777 
err:   9778 
err:   9779 
err:   9865 
err:   9867 
err:   9868 ' 
err: +/opt/apps/pydolphin/restart.sh:24> [ -z '  9777 
err:   9778 
err:   9779 
err:   9865 
err:   9867 
err:   9868 ' ']'
err: +/opt/apps/pydolphin/restart.sh:27> array=( '  9777 
err:   9778 
err:   9779 
err:   9865 
err:   9867 
err:   9868 ' ) 
err: +/opt/apps/pydolphin/restart.sh:28> var=  9777 
err:   9778 
err:   9779 
err:   9865 
err:   9867 
err:   9868 
err: +/opt/apps/pydolphin/restart.sh:30> single_pid=+/opt/apps/pydolphin/restart.sh:30> echo '  9777 
err:   9778 
err:   9779 
err:   9865 
err:   9867 
2021/07/19 06:00:52 Process exited with status 1
err:   9868 '
err: +/opt/apps/pydolphin/restart.sh:30> single_pid=+/opt/apps/pydolphin/restart.sh:30> awk 'gsub(/^ *| *$/,"")'
err: +/opt/apps/pydolphin/restart.sh:30> single_pid='9777
err: 9778
err: 9779
err: 9865
err: 9867
err: 9868' 
err: +/opt/apps/pydolphin/restart.sh:31> [[ '9777
err: 9778
err: 9779
err: 9865
err: 9867
err: 9868' -gt 1/opt/apps/pydolphin/restart.sh:31: bad math expression: operator expected at `9778\n9779\n...'
err:  ]]

스크립트를 읽었지만 무엇이 잘못되었는지 알지 못했습니다. 스크립트를 작동시키려면 어떻게 해야 합니까?

답변1

단순히 를 사용하는 대신 자신만의 "종료 루프"를 작성하기로 결정한 경우 pkill최소한 pgrep다음의 출력을 쪼개고 쪼개는 대신 가능한 선행/후행 공백으로 인해 방해받지 않는 PID 목록을 얻는 데 사용하십시오 ps.

array=($(pgrep -- "${CELERY_PROGRAM_NAME}"))

pgrep또는 출력을 직접 반복합니다.

for single_pid in $(pgrep -- "${CELERY_PROGRAM_NAME}"); do ...

pgrep한 줄에 하나씩 PID 목록을 생성합니다. 기본 bash IFS는 연속 공백으로 분할됩니다.개행 포함예를 들어 배열에 매핑하십시오.

$ pgrep ssh
1194
3688
22642
22754

$ array=($(pgrep ssh))

$ declare -p array
declare -a array=([0]="1194" [1]="3688" [2]="22642" [3]="22754")

아니면 사용할 수 있습니다 readarray -t array < <(pgrep ssh).

요소가 없으면 루프가 실행되지 않으므로 문자열이 비어 있는지 테스트할 필요가 없습니다.

구현이 작동하지 않는 이유는 1${PID//\n/} 에서 리터럴 n문자가 제거됩니다. 숫자 값만 포함한다고 가정하면 아무 작업도 수행할 수 없습니다.PIDPIDarray=(${PID//\n/}) ~해야 한다single_pidPID를 얻기 위해 추가 처리가 필요하지 않도록 별도의 PID 배열이 생성되고 선행 및 후행 공백이 제거됩니다 .

오류 출력에 따르면 분명히 그렇지 않다는 사실은 다음 두 가지 이유 중 하나를 암시합니다.

  1. 쉘의 IFS 값을 수정했습니다.

  2. 사용 중인 쉘은 인용되지 않은 변수 확장을 토큰화하지 않습니다. zsh, 출력 형식은 xtrace기본 동작으로 파일을 사용하고 있음을 나타냅니다. 즉, 명령 대체 시 IFS 분할을 수행함에도 불구하고 위의 코드는 해당 셸에서 계속 작동합니다. 에서 개행으로 분할하려면 zsh다음을 사용하는 것이 좋습니다.

    array=(${(f)"$(pgrep -- $CELERY_PROGRAM_NAME)"})
    

    그러나 이는 현재 값에 대한 의존성을 bash제거합니다 .readarray$IFS


1 실제로 개행 문자를 제거하려면 다음을 사용할 수 있습니다.${PID//$'\n'/}

답변2

피해야 할 또 다른 옵션은 pgrep/pkill사용자 정의 출력입니다 ps.

ps목록 프로세스 및 인쇄 형식/필드에서 사용자 정의를 수행할 수 있습니다.

많은 필드가 있는 모든 프로세스가 나열 되므로 ps -efpid를 얻는 것이 번거로울 수 있습니다.

while은 ps -e -o pid,comm두 개의 출력 열을 제공합니다

 PID COMMAND
   1 systemd
   2 kthreadd
   3 rcu_gp
   4 rcu_par_gp
   9 mm_percpu_wq
  10 ksoftirqd/0
    (...)
1002 gdm3
1013 sshd
1031 php-fpm7.4
1032 php-fpm7.4
1044 nginx
1065 nmbd
     (many more lines)

(또는 -o pid= -o comm=제목을 삭제하세요).

내가 찾고 있는 프로세스가 다음 위치에 있고 ${CELERY_PROGRAM_NAME}백슬래시나 공백 문자를 포함하지 않는 경우 다음을 사용할 수 있습니다.

 ps -e -o pid= -o comm= | awk -v proc="${CELERY_PROGRAM_NAME}" '$2==proc { print $1}'

PID를 받으세요.

사용

PID=$(ps -e -o pid= -o comm= | awk -v proc="${CELERY_PROGRAM_NAME}" '$2==proc { print $1})

비어 있지 않은지 확인한 후 이것이 ${PID}개행으로 구분된 pid 목록임을 고려한 후 다음을 사용할 수 있습니다( $IFS기본적으로 개행이 포함되어 있다고 가정).

for pid2 in ${PID}
do 
   ...
done

( 출력으로 사용하도록 권장하는 zsh형식을 사용하는 경우 기본적으로 do -splitting 으로 바꾸거나 값에 관계없이 개행으로 분할 하도록 바꾸십시오 .)xtrace${PID}${=PID}$IFSbash${(f)PID}$IFS

선택하다

일부 옵션 ps( man ps모두 보기)

  • ps -p 1234 -o tty,args(pid 1234의 tty 및 args 나열)
  • ps -t pts/1,pts/3 -f(tty pts/1 및 pts/3에 모든 내용을 나열)
  • ps -u archemar(archemar에 속한 모든 프로세스 나열)

HP/UX 및 procps 구현은 이름을 기반으로 프로세스를 쿼리하는 옵션 ps도 지원합니다 .-C

관련 정보