1. ホーム
  2. スクリプト・コラム
  3. リナックスシェル

teeコマンドでシェルスクリプトのパイプラインをデバッグする方法

2022-01-09 05:36:38

インスタンス

以下は、processid関数が指定されたプロセス名のプロセスIDを問い合わせる簡単なスクリプトで、Linuxサーバーの管理では非常によく使われる関数です。

#! /bin/sh

processid()
{
    ipid=$(ps -ef | grep -w $1 | grep -v grep | awk '{print $2}')
    echo $ipid
}

case "$1" in
    i)
       processid $2
      ;;
    *)
        echo "parameter error... $1"
      ;;
esac

スクリプトを実行する

このスクリプトを実行して、zone9_log1 のプロセス ID を問い合わせると、次のような結果が得られます。

[wanng@localhost ~]$ . /a.sh i zone9_log1
130530 144391 144392

実際のzone9_log1プロセスのプロセスIDと比較するために、ps -ef | grep -w zone9_log1 | grep -v grep | awk '{print $2}' コマンドを別途実行すると、以下のような結果になります。

[wanng@localhost ~]$ ps -ef | grep -w zone9_log1 | grep -v grep | awk '{print $2}'
130530

質問事項

パイプラインの中間結果を出力するために、スクリプトにteeコマンドを追加し、調整したスクリプトは次のようになります。

processid()
{
    ipid=$(ps -ef | grep -w $1 | tee out1 | grep -v grep | tee out2 | awk '{print $2}') | tee out3
    echo $ipid
}

case "$1" in
    i)
       processid $2
      ;;
    *)
        echo "parameter error... $1"
      ;;
esac

再度スクリプトを実行すると、このパイプラインコマンドの中間結果を記録した out1 out2 out3 という3つのファイルがローカルに生成されます。スクリプトの実行結果と out1 out2 out3 ファイルの内容は次のとおりです。

[wang@localhost ~]$ . /a.sh i zone9_log1
130530 144885 144886

[wang@localhost ~]$ cat out1
wang 130530 1 0 Apr 24 pts/10 00:07:47 . /zone9_log1 . /zone9_log1.lua
wang 144885 109338 0 20:45 pts/8 00:00:00 /bin/sh . /a.sh i zone9_log1
wang 144886 144885 0 20:45 pts/8 00:00:00 /bin/sh . /a.sh i zone9_log1
wang 144888 144886 0 20:45 pts/8 00:00:00 grep -w zone9_log1
[wang@localhost ~]$ cat out2
wang 130530 1 0 Apr 24 pts/10 00:07:47 . /zone9_log1 . /zone9_log1.lua
wang 144885 109338 0 20:45 pts/8 00:00:00 /bin/sh . /a.sh i zone9_log1
wang 144886 144885 0 20:45 pts/8 00:00:00 /bin/sh . /a.sh i zone9_log1
[wang@localhost ~]$ cat out3
130530
144885
144886
[wang@localhost ~]$ 

理由

スクリプトを実行すると、デフォルトで新しいシェル(=新しいプロセス)が作成され、上記のスクリプトa.shは新しいシェル環境で実行されます。上記のテスト結果からわかるように、ps -ef | grep -w zone9_log1 コマンドの結果には、実行フット自身が起動したプロセスと問い合わせたい対象プロセスが含まれており、正確なプロセスIDを得るためにはスクリプト自身のプロセスをフィルタリングすればよく、調整後のスクリプトは以下のようになります(とりあえずteeコマンドの中間結果を出力しておきます)。

processid()
{
    ipid=$(ps -ef | grep -w $1 | grep -v $0 | tee out1 | grep -v grep | tee out2 | awk '{print $2}') | tee out3
    echo $ipid
}

case "$1" in
    i)
       processid $2
      ;;
    *)
        echo "parameter error... $1"
      ;;
esac

上記のprocessid関数 grep -v $0 は、スクリプトの名前 ( a.sh ) をフィルタリングする役割を果たします。

バリデート

再度スクリプトを実行すると、以下のようになります。

[wanng@localhost ~]$ . /a.sh i zone9_log1
130530

[wanng@localhost ~]$ cat out1
wanng 130530 1 0 Apr 24 pts/10 00:07:51 . /zone9_log1 . /zone9_log1.lua
wanng 146170 146168 0 21:11 pts/8 00:00:00 grep -w zone9_log1
[wanng@localhost ~]$ cat out2
wanng 130530 1 0 Apr 24 pts/10 00:07:51 . /zone9_log1 . /zone9_log1.lua
[wanng@localhost ~]$ cat out3
130530

上のテストからわかるように、最終的な出力は正しいです

概要

多層パイプラインはシェルスクリプトでは非常に一般的で、非常に簡単かつ効率的に使用できます。しかし、スクリプトに問題が発生した場合、デバッグが困難になります。

シェルスクリプトのパイプラインをデバッグするteeコマンドの詳細については、スクリプトハウスの他の関連記事に注目してください!上記は、シェルスクリプトのパイプラインをデバッグするteeコマンドの使用方法です。