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

単にを使用する代わりに独自の「終了ループ」を作成することを決定した場合は、少なくとも次pkillpgrep出力を分割し、分割は代わりに可能な先行/末尾のスペースによって妨げられないPIDのリストを取得するために使用しますps

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

pgrepまたは、出力を直接繰り返します。

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

pgrep1行に1つずつ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配列が作成され、先行スペースと末尾のスペースが削除されます。

エラー出力によると、明らかにそうではないという事実は、次の2つの理由の1つを示唆しています。

  1. シェルのIFS値を修正しました。

  2. 使用中のシェルは、引用符のない変数拡張をトークン化しません。zsh、出力形式は、デフォルトのxtrace動作としてファイルを使用していることを示します。つまり、コマンドの置き換え時にIFS分割を実行しても、上記のコードはそのシェルで動作し続けます。から改行に分割するには、zsh次を使用することをお勧めします。

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

    ただし、これは現在の値への依存関係をbash排除します。readarray$IFS


1実際に改行文字を削除するには、以下を使用できます。${PID//$'\n'/}

答え2

避けるべきもう1つのオプションはpgrep/pkillカスタム出力ですps

psリストプロセスと印刷フォーマット/フィールドでカスタマイズを実行できます。

多くのフィールドを持つすべてのプロセスが一覧表示されるため、ps -efpidを取得するのは面倒です。

whileはps -e -o pid,comm2つの出力列を提供します

 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

選ぶ

一部のオプションpsman 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

関連情報