引数に与えられたコマンドを実行して、その証跡を残す関数

引数に与えられたコマンドを実行して、その証跡を残す関数の作成。

まず、やりたいことができるかどうかの確認。
引数に与えられたコマンドを実行できるか。

$ aaa="echo sample message"
$ eval $aaa
sample message

基本は、これでやれそう。

次に、証跡を残すこと。
実行結果を変数に入れて、適当に出力できるようにしてみる。

$ type eval
eval はシェル組み込み関数です
$
$ aaa="echo sample message"
$ bbb=$(eval $aaa)
$ echo $bbb
sample message

やりたいことは、ほぼ完成。
エラー処理各種を試す。

$ aaa="echo sample message 1>&2"
$ bbb=$(eval $aaa)
sample message

標準エラー出力は xxx=$() という形式では含んでくれないので、含めておこうか。
ついでに、正常な場合の Exit Status も確認。

$ aaa="echo sample message 1>&2"
$ bbb=$(eval $aaa 2>&1)
$ ccc=$?
$ logger -t test $bbb
$ tail -1 /var/log/messages
May 25 08:55:37 tsrw01 test: sample message
$ echo $ccc
0

コマンドが異常終了した場合の Exit Status も確認

$ ls /etc/dummy
ls: '/etc/dummy' にアクセスできません: そのようなファイルやディレクトリはありません
$ echo $?
2
$ 
$ aaa="ls /etc/dummy"
$ bbb=$(eval $aaa 2>&1)
$ ccc=$?
$ echo "$(date +%Y/%m/%d %H:%M:%S) $bbb" > /tmp/ddd
$ cat /tmp/ddd
2019/05/25 09:04:53 ls: '/etc/dummy' にアクセスできません: そのようなファイルやディレクトリはありません
$ echo $ccc
2

ということで、内容を見やすく整理しつつ関数にまとめる。

#!/bin/bash

pLogFile=cmd.log

fnLog() {
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

fnCmd() {
    fnLog "Command line: $*"

    # Command exec
    pOutput=$(eval "$*" 2>&1)
    pExitStatus=$?

    # Log output
    fnLog "=== standard output / standard error output ========="
    echo "${pOutput}" >> ${pLogFile}
    fnLog "====================================================="
    fnLog "Exit status: ${pExitStatus}"

    return ${pExitStatus}
}

こうしておくと、実行したコマンドの出力結果を自由に出力しつつ、戻り値も確認できる。

ログの中身は各行に必ず日時がないとヤダ……という場合は、こんな感じ。

#!/bin/bash

pLogFile=cmd.log

fnLog() {
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

fnCmd() {
    fnLog "Command line: $*"

    # Command exec
    pOutput=$(eval "$*" 2>&1)
    pExitStatus=$?

    # Log output
    fnLog "=== standard output / standard error output ========="
    echo "${pOutput}" | while read line
        do
            fnLog "$line"
        done
    fnLog "====================================================="
    fnLog "Exit status: ${pExitStatus}"

    return ${pExitStatus}
}


個人的には、スクリプトログなんて実行が失敗した時しか見ないんだから、コマンドの出力結果に日時情報を入れるだけムダだし、紛らわしくて困ると思うんだけど……。
パッと見のキレイさの方が重要みたい。
どうせなら、4桁くらいで固定で行番号を入れてみる?

#!/bin/bash

pLogFile=cmd.log

fnLog() {
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

fnCmd() {
    fnLog "Command line: $*"

    # Command exec
    pOutput=$(eval "$*" 2>&1)
    pExitStatus=$?

    # Log output
    fnLog "=== standard output / standard error output ========="
    i=1
    echo "${pOutput}" | while read line
        do
            fnLog $(printf "%04d: %s" $i "$line")
            i=$(($i + 1))
        done
    fnLog "====================================================="
    fnLog "Exit status: ${pExitStatus}"

    return ${pExitStatus}
}

fnCmd "ls /etc/hosts*"

これを chk.sh に書き込んで実行してみた結果。

$ bash ./chk.sh
$ cat cmd.log
2019/05/25 09:39:11 Command line: ls /etc/hosts*
2019/05/25 09:39:12 === standard output / standard error output =========
2019/05/25 09:39:12 0001: /etc/hosts
2019/05/25 09:39:12 0002: /etc/hosts.allow
2019/05/25 09:39:12 0003: /etc/hosts.deny
2019/05/25 09:39:12 =====================================================
2019/05/25 09:39:12 Exit status: 0
$ 

うーん……
他の分も、同じコマンドを実行させるように sample1.sh / sample2.sh として書き込んで実行してみる。

sample1.sh は、実行結果に日時情報のないもの。

$ [ -f cmd.log ] && rm cmd.log
$ bash ./sample1.sh
$ cat cmd.log
2019/05/25 09:43:56 Command line: ls /etc/hosts*
2019/05/25 09:43:56 === standard output / standard error output =========
/etc/hosts
/etc/hosts.allow
/etc/hosts.deny
2019/05/25 09:43:56 =====================================================
2019/05/25 09:43:56 Exit status: 0


sample2.sh は、実行結果に日時情報を入れたもの。

$ [ -f cmd.log ] && rm cmd.log
$ bash ./sample2.sh
$ cat cmd.log
2019/05/25 09:47:00 Command line: ls /etc/hosts*
2019/05/25 09:47:00 === standard output / standard error output =========
2019/05/25 09:47:00 /etc/hosts
2019/05/25 09:47:00 /etc/hosts.allow
2019/05/25 09:47:00 /etc/hosts.deny
2019/05/25 09:47:00 =====================================================
2019/05/25 09:47:00 Exit status: 0

実行結果に、日時情報と行番号を入れたものを再度。

$ bash ./chk.sh
$ cat cmd.log
2019/05/25 09:39:11 Command line: ls /etc/hosts*
2019/05/25 09:39:12 === standard output / standard error output =========
2019/05/25 09:39:12 0001: /etc/hosts
2019/05/25 09:39:12 0002: /etc/hosts.allow
2019/05/25 09:39:12 0003: /etc/hosts.deny
2019/05/25 09:39:12 =====================================================
2019/05/25 09:39:12 Exit status: 0
$ 


……こうして並べてみると、印象がすごく違うね。