(O+P)ut

アウトプット



(O+P)ut

エンジニアのアウトプット

【Cygwin】標準出力を含んだ作業ログを自動で残す

スポンサーリンク

はじめに

Cygwinで作業をすることが多いのですが scriptコマンドを使えば、標準出力である画面のログをテキスト形式で残せます。
本記事ではその作業ログの取得を逐次実施せずに自動で行えるようにした際のメモです。

scriptコマンドの説明

scriptコマンドの使い方は、ターミナルで script と打てば利用できます。。

標準出力は以下のようになります。

$ script
スクリプトを開始しました。ファイルは typescript です

ここから記録が ./typescript に始まり、終了する際は exit と打てば終了します。

$ exit
exit
スクリプトを終了しました。ファイルは typescript です

オプションや引数をつけることで、ファイルを上書きしたりファイル名を指定もできます。

自動化する際のポイント

問題はこれを起動時に自動で実行しようとする際です。

単純に .bashrc に 以下のように加筆すると

script

と書くと失敗します。

以下が私の環境で実際に上記の記述を.bashrcに行い、起動した際の出力です。

スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です

実はscriptコマンドは新たにシェルを立ち上げる挙動します。

新たなシェルが立ち上がる→scriptコマンド実行→新たなシェルが... の無限ループになるみたいですね。

scriptコマンド実行前後のプロセスを見てみます。

$ ps
      PID    PPID    TTY       STIME COMMAND
    13052       1    ?         01:08:49 /usr/bin/mintty
     6288    1936    pty0      01:08:56 /usr/bin/ps
     1936   13052    pty0      01:08:49 /usr/bin/bash
$ script
スクリプトを開始しました。ファイルは typescript です
$ ps
      PID    PPID   TTY      STIME COMMAND
     1288    5712  pty0      01:09:01 /usr/bin/script
    11180    4176  pty1      01:09:04 /usr/bin/ps
     5712    1936  pty0      01:09:01 /usr/bin/script
     4176    1288  pty1      01:09:02 /usr/bin/bash
    13052       1  ?         01:08:49 /usr/bin/mintty
     1936   13052  pty0      01:08:49 /usr/bin/bash

scriptコマンドに伴い、pty1が立ち上がってカレントシェルが変更になっています。

対策方針

今回は簡易的に、現在のシェルの親プロセスが /usr/bin/mintty な場合にのみ script を動かすようにします。

autoscript.shというスクリプトを記述しました。実行できるように適切な権限を付与ください。

※シェルスクリプトの権限の違いについて実証した別記事
www.mtioutput.com

実装

直接.bashrcに記述してもいいですが、テストしやすかったのでそのままshファイルを起動するという構成にしています。

#!/bin/sh

ppcheck=`ps | grep $PPID | grep -v pty | awk '{print $8}'`
ttynum=`echo \`tty\` | awk '{print substr($0,(length($0)+1)-1,1)}'`
nowtime=`date +"%Y%m%d%H%M%S"`
filename="${nowtime}_${ttynum}"

if [[ ${ppcheck} = "/usr/bin/mintty" ]]; then
        script -f script_log/$filename
fi

ppcheckという変数に現在のシェルの親プロセスのIDを取り込み、そこで現在のttyの最後の数字と現在の時刻とするファイル名でログをとる、という流れですね。

こうしておけばscriptで立ち上がったシェルの親プロセスは/usr/bin/minttyではなくなるので、ループは起きないといった流れです。
ファイル名は、時刻だけだと同時にCygwinの窓を複数立ち上げると被ってしまう可能性があると思い、一意にするべくttyから値を持ってきてます。

これを起動時に実行するために .bashrc に以下のように記載します。

. ./autoscript.sh

明示的にカレントシェルで実行するよう記述しているのは、親プロセスが変わってしまうのを防ぐためです。

以下を記載した上でCygwinを立ち上げると

スクリプトを開始しました。ファイルは script_log/20170129011831_0 です

$ ls script_log/
20170129011831_0

と、しっかり機能しています。

とりあえずこれで運用してみて、挙動が怪しい場合にはまたその都度修正していこうと思います。
Cygwinをお使いの方で、同じようなことに興味がある方はご参考ください。