※当サイトの記事には、広告・プロモーションが含まれます。

Linuxでシェルスクリプトの稼働状態を監視したい

gigazine.net

⇧ IoTの分野も日進月歩ですな。

Linuxシェルスクリプトの稼働状態を監視したい

前回、

ts0818.hatenablog.com

⇧ ファイルの変更を検知という発想で検討していたのですが、よくよく考えてみたら、

  • 監視したいのはシェルスクリプトの処理が終了したかどうか
  • ファイルに対して何も処理されないことも有り得る

という要件だったので、実現したいことは

になるということで、「.sh」ファイルの処理が完了したかどうかという情報ってどうやって取得できるのかを知りたいですと。

で、調べていた限りでは、

linuxhint.com

⇧ 上記サイト様によりますと、実行された「.sh」ファイルは、Linuxのプロセスの1つと認識されているので、psコマンドでプロセス自体は特定できるらしい。

なので、後は、

  • 「.sh」ファイルの処理が終了した時に、プロセスの状態がどうなるのか

についてが分かれば、「.sh」ファイルの処理が完了したかどうかということを判定できるということになると思うんだけど、

qiita.com

yohei-a.hatenablog.jp

⇧ 上記サイト様にありますように、プロセスの状態についてが分かり辛い...

「WSL 2(Windows Subsystem for Linux 2)」の「Ubuntu 22.04.2 LTS (GNU/Linux 5.15.90.1-microsoft-standard-WSL2 x86_64)」でpsコマンドのマニュアルを確認した感じでは、

PROCESS STATE CODES
       Here are the different values that the s, stat and state output specifiers (header "STAT" or
       "S") will display to describe the state of a process:

               D    uninterruptible sleep (usually IO)
               I    Idle kernel thread
               R    running or runnable (on run queue)
               S    interruptible sleep (waiting for an event to complete)
               T    stopped by job control signal
               t    stopped by debugger during the tracing
               W    paging (not valid since the 2.6.xx kernel)
               X    dead (should never be seen)
               Z    defunct ("zombie") process, terminated but not reaped by its parent

       For BSD formats and when the stat keyword is used, additional characters may be displayed:

               <    high-priority (not nice to other users)
               N    low-priority (nice to other users)
               L    has pages locked into memory (for real-time and custom IO)
               s    is a session leader
               l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
               +    is in the foreground process group    

⇧ 上記がpsコマンドの「PROCESS STATE CODES」の説明。

う~む、サッパリ分からん...

ちなみに、「ps auxw」っていう「-w」のオプションを使ったことがほとんど無かったので調べてみたんだけど、

OUTPUT MODIFIERS
       c      Show the true command name.  This is derived from the name of the executable file, rather
              than from the argv value.  Command arguments and any modifications to them are thus not
              shown.  This option effectively turns the args format keyword into the comm format
              keyword; it is useful with the -f format option and with the various BSD-style format
              options, which all normally display the command arguments.  See the -f option, the format
              keyword args, and the format keyword comm.

       --cols n
              Set screen width.

       --columns n
              Set screen width.

       --cumulative
              Include some dead child process data (as a sum with the parent).

       e      Show the environment after the command.

       f      ASCII art process hierarchy (forest).

       --forest
              ASCII art process tree.

       h      No header.  (or, one header per screen in the BSD personality).  The h option is
              problematic.  Standard BSD ps uses this option to print a header on each page of output,
              but older Linux ps uses this option to totally disable the header.  This version of ps
              follows the Linux usage of not printing the header unless the BSD personality has been
              selected, in which case it prints a header on each page of output.  Regardless of the
              current personality, you can use the long options --headers and --no-headers to enable
              printing headers each page or disable headers entirely, respectively.

       -H     Show process hierarchy (forest).

       --headers
              Repeat header lines, one per page of output.

       k spec Specify sorting order.  Sorting syntax is [+|-]key[,[+|-]key[,...]].  Choose a
              multi-letter key from the STANDARD FORMAT SPECIFIERS section.  The "+" is optional since
              default direction is increasing numerical or lexicographic order.  Identical to --sort.

                      Examples:
                      ps jaxkuid,-ppid,+pid
                      ps axk comm o comm,args
                      ps kstart_time -ef

       --lines n
              Set screen height.

       n      Numeric output for WCHAN and USER (including all types of UID and GID).

       --no-headers
              Print no header line at all.  --no-heading is an alias for this option.

       O order
              Sorting order (overloaded).  The BSD O option can act like -O (user-defined output format
              with some common fields predefined) or can be used to specify sort order.  Heuristics are
              used to determine the behavior of this option.  To ensure that the desired behavior is
              obtained (sorting or formatting), specify the option in some other way (e.g.  with -O or
              --sort).

              For sorting, obsolete BSD O option syntax is O[+|-]k1[,[+|-]k2[,...]].  It orders the
              processes listing according to the multilevel sort specified by the sequence of
              one-letter short keys k1,k2, ... described in the OBSOLETE SORT KEYS section below.
              The "+" is currently optional, merely re-iterating the default direction on a key, but
              may help to distinguish an O sort from an O format.  The "-" reverses direction only on
              the key it precedes.

       --rows n
              Set screen height.

       S      Sum up some information, such as CPU usage, from dead child processes into their parent.
              This is useful for examining a system where a parent process repeatedly forks off
              short-lived children to do work.

       --sort spec
              Specify sorting order.  Sorting syntax is [+|-]key[,[+|-]key[,...]].  Choose a
              multi-letter key from the STANDARD FORMAT SPECIFIERS section.  The "+" is optional since
              default direction is increasing numerical or lexicographic order.  Identical to k.  For
              example: ps jax --sort=uid,-ppid,+pid

       w      Wide output.  Use this option twice for unlimited width.

       -w     Wide output.  Use this option twice for unlimited width.

       --width n
              Set screen width.

⇧ 出力のオプションらしく、「ps auxww」とかwを2個付けると出力の際に横幅が無制限になるってことらしい。

Linuxの「.sh」ファイルの処理が終わると、そのプロセスも無くなるっぽい

で、試しに、「.sh」ファイルの処理が終わると、「.sh」ファイルのプロセスがどうなるのか確認してみました。

qiita.com

⇧ 上記サイト様を参考に、以下のようなコードを用意して、

■プロセスを確認する処理の対象

#!/bin/bash

echo "shell script done."

# 開始時刻を取得
date_start=$(date +%Y/%m/%d-%H:%M:%S)
# 経過時間計算時使用するスタート時刻を取得
start_time=$(date "+%s")
# 経過時刻変数
elapsed_time=0

# 経過時間が10秒を経過したらwhile文を抜ける
while :; do
    if [ ${elapsed_time} -ge 10 ]; then
        echo "10 seconds. elapsed"
        break
    else
        echo "process doing. ${elapsed_time} seconds."
        # 1秒停止
        sleep 1
        # 現在時間を算出
        current_time=$(date "+%s")
        # 経過時間を計算
        elapsed_time=$((${current_time} - ${start_time}))
    fi
done

# 終了時間を取得
date_end=$(date +%Y/%m/%d-%H:%M:%S)

echo "loop finished."
echo -e "start datetime. "'\t'"${date_start}"
echo -e "finish datetime."'\t'"${date_end}"

#exit 0
    

■プロセスを確認する処理

#!/bin/bash

echo "shell script done."

# 開始時刻を取得
date_start=$(date +%Y/%m/%d-%H:%M:%S)
# 経過時間計算時使用するスタート時刻を取得
start_time=$(date "+%s")
# 経過時刻変数
elapsed_time=0

# 経過時間が13秒を経過したらwhile文を抜ける
while :; do
    if [ ${elapsed_time} -ge 13 ]; then
        echo "10 seconds. elapsed"
        break
    else
        echo "process doing. ${elapsed_time} seconds."
        result_ps=$(ps aux | grep test_ps.sh | grep -v grep)
        echo 'result_ps'"${result_ps}"
        # 1秒停止
        sleep 1
        # 現在時間を算出
        current_time=$(date "+%s")
        # 経過時間を計算
        elapsed_time=$((${current_time} - ${start_time}))
    fi
done

# 終了時間を取得
date_end=$(date +%Y/%m/%d-%H:%M:%S)

echo "loop finished."
echo -e "start datetime. "'\t'"${date_start}"
echo -e "finish datetime."'\t'"${date_end}"

#exit 0
    

コマンドプロンプトを2つ起ち上げておいて、それぞれのコマンドを実行。

どちらも、「WSL 2(Windows Subsystem for Linux 2)」のUbuntu環境にSSH接続でログインしています、つまり、同一マシン内にいる状態。

bash ./test_ps.sh    

bash ./check_ps.sh  

⇧ そうすると、「.sh」ファイルの処理で、exitとかしていなくても、処理が終わったと認識されるのか、プロセスが取得できなくなったところを見ると、

「.sh」ファイルの処理の終了 = プロセスが取得できなくなる

ということで良さそうなので、psコマンドの結果を変数に格納するようにして、変数が空であるかどうかを判定することで、「.sh」ファイルの処理の終了を判定できそうですと。

プロセスのライフサイクルがよく分からんので、推測での話になってしまいますが、

1. シェルスクリプトファイルの実行開始

2. シェルスクリプトファイルの実行プロセス生成

3. シェルスクリプトファイル内の処理開始

4. シェルスクリプトファイル内の処理終了

5. シェルスクリプトファイルの実行終了

6. シェルスクリプトファイルの実行プロセス消滅

って感じで、プロセスが無くなるってことですかね。

シェルスクリプトの処理のプロセス監視用のシェルスクリプトを実装してみた

というわけで、「.sh」ファイルのプロセスを監視するシェルスクリプトを実装してみました。

#!/bin/bash

# このシェルスクリプトのファイルがある位置に移動
cd `dirname $0`

# 今いる場所を表示
WORK_DIR=$(pwd)
echo -e "WORK_DIR"'\t'"${WORK_DIR}"

echo "watch shell script done."

# 開始時刻を取得
date_start=$(date +%Y/%m/%d-%H:%M:%S)
# 経過時間計算時使用するスタート時刻を取得
start_time=$(date "+%s")
# 経過時刻変数
elapsed_time=0

TEST_SHELL_SCRIPT=test_ps.sh

# テスト回数
test_count=0

# 実施するテスト回数
expect_test_count=3

# タイムアウト(処理時間の上限)
time_out=90

# 初期値はダミーのプロセス
result_ps="Not test done"

# プロセスを確認するタイミング(10秒毎)
TRIGGER_TIME_CHECK_PROCESS=10

# test_ps.sh が実施するテスト回数だけ実行されたか、タイムアウトになるまで繰り返し処理
while :; do

  # タイムアウトの場合
  if [ ${elapsed_time} -ge ${time_out} ]; then
    echo "timeout."
    break
  fi

  # テストの処理が完了した場合
  if [ -z "${result_ps}" ]; then

    if [ ${test_count} -ne 0 ]; then
      # テスト完了
      echo "${test_count} time test_ps.sh finished."

    fi

    # 実施するテスト回数が完了した場合
    if [ ${test_count} -ge ${expect_test_count} ]; then
      echo "all test done."
      break
    fi
    
    # テスト回数をインクリメント
    test_count=$((test_count +1))

    # 開始時間(経過時間を算出用)を初期化
    start_time=$(date "+%s")
    # 経過時間を初期化
    elapsed_time=0

    # テスト開始
    echo "${test_count} time test_ps.sh start."

    # テストを実施
    bash ./test_ps.sh
      
  fi
  
  # 経過時間を算出する
  echo "watch process doing. ${elapsed_time} seconds."
  echo '###result_ps###'
  echo "${result_ps}"
  # 1秒停止
  sleep 1
  # 現在時間を算出
  current_time=$(date "+%s")
  # 経過時間を計算
  elapsed_time=$((${current_time} - ${start_time}))
  
  # 10秒毎に実行
  if [ ${elapsed_time} -ge ${TRIGGER_TIME_CHECK_PROCESS} ]; then
    # プロセスを取得
    result_ps=$(ps aux | grep ${TEST_SHELL_SCRIPT} | grep -v grep)
  
  fi

done

# 終了時間を取得
date_end=$(date +%Y/%m/%d-%H:%M:%S)

echo "watch loop finished."
echo -e "watch start datetime. "'\t'"${date_start}"
echo -e "watch finish datetime."'\t'"${date_end}"

#exit 0
    

で、それぞれ、実行。

bash ./test_ps.sh    

bash ./watch_process.sh   

⇧ 「test_ps.sh」ファイルの処理のプロセスの終了を検知できてるようです。

実際は、プロセスの終了を検知して、テスト結果を格納用のディレクトリを作成して、テスト結果をコピーして、次のテストのインプットに差し替えたりするなどの処理をシェルスクリプトに追記する感じになるとは思いますが、監視っぽいことは実現できたということで良しとしますかね。

ちなみに、今回は同一マシン上でプロセスが取れるかどうかを試したわけなんですが、

qiita.com

⇧ 別マシンに対してリモート接続でコマンドを実行させたいような場合は注意が必要なようです。

う~む、マシンが増えるとハマりどころが多そうですな...

そう言えば、前回、「cron」使ってみるみたいなこと言っときながら、使ってないや...

「cron」は、別の機会に学習することにいたします...

毎度モヤモヤ感が半端ない...

今回はこのへんで。