ユーザが実行したコマンドを追跡(+何か)をするには? bashではTRAPシグナル tcshならprecmdエイリアス、 伝統のsh(含Debianのdash)とcshには良い技なし(NetBSD調べ)

bash

TRAP(擬似)シグナルをつかまえれば、 ユーザがコマンドを実行するたびに「あるアクション(arg)を実行する」ことが出来ます。

trap [-lp] [[arg] sigspec …] … snip … If a sigspec is EXIT (0) the command arg is executed on exit from the shell. If a sigspec is DEBUG,the command arg is executed before every simple command, for command, case command, select command, ev‐ery arithmetic for command, and before the first command executes in a shell function (see SHELL GRAM‐MAR above). Refer to the description of the extdebug option to the shopt builtin for details of itseffect on the DEBUG trap. If a sigspec is RETURN, the command arg is executed each time a shell func‐tion or a script executed with the . or source builtins finishes executing. — qouted from “GNU bash, version 5.2.15(1)-release manual”.

設定例は、インフラ演習環境を構築するツール hands-on-basedebian-pc コンテナの起動時の設定 を参照してください。

function input_logging () {
    status=$?
    date=$(date +"%FT%T %s")
    printf "%10s %10s %s --- %s [%s]\n" "${XHOSTNAME}" "$(whoami)" "${date}" "${BASH_COMMAND}" $status >> /tmp/.msgbuf
}

trap input_logging DEBUG

この例では、 コマンドを実行するたびに、 どこ(where)でいつ(when)何を(what)?の情報をファイル(/tmp/.msgbuf)に書きこんでいます。

tcsh

precmdというエイリアスで「エイリアスとして設定」が出来ます。

alias precmd 'echo "+ \!-0"'

関数とかではなくエイリアスなので、長いアクションを書くのは一苦労です。 外部コマンドを用意して、それを呼び出す方法がよいでしょう。

[実行例]
$ date
Mon Feb 10 12:48:47 JST 2025
+ date
$

shとdash: 実行履歴の追跡は可ですが…

シェルスクリプトのデバッグの定番-vxオプションの-x

伝統のshと言っても4.4BSD由来なので、80年代に書き直されたシェルの子孫です。 NetBSDのshはKen Almquist作のashの発展版だそうです。 そしてdashはDebianデフォルトのシェルですが、これはNetBSD shのlinux移植版の直系の子孫にあたります。

-xオプションもしくは-o xtraceをつけて起動すると、 コマンド実行直前に+ コマンドという文字列をSTDERRに出力してくれます。 出力先のデフォルトはSTDERRですが、-Xオプションで変更できます。 $PS4変数で+の文字列もカスタマイズできます。

fukachan@katri:~$ sh -x
$ date
+ date
Mon Feb 10 11:39:13 JST 2025
$ 

この+表示時に何かのアクションを起こせるか?というと、 その機能は伝統的なshには無いと思います (マニュアルにもないし、ソースコードを読んでも、未記述の謎機能があるようには見えないです)。

シェル単体では難しそうですが、 出力先のファイルを見張るデーモンを作っておいて、 それが一行ずつ書きこまれるたびに何かのアクションを取るということなら出来るでしょう (そのアクションの結果をシェルに戻してシェルの動作に反映させたいとなると無理な気がしますけど)。

備考(HISTORY): そういうわけで、この-xオプションはNetBSDやDebianでも同様に存在します。 初出が「いつ」の「どのシェル」なのか?は追跡していません。 興味のある人がいれば、UNIX HERITAGEのソースコードを漁ってみてほしいです:-)

ksh

bashと似たような感じでDEBUG疑似シグナルを追うことで実現できます。 一発で「直前に実行したコマンド」を取得する方法がわからないのですが、 historyコマンドを使えば出来なくはありません…というやり方です

trap dotrace DEBUG
dotrace () { 
	cmd=$(echo $(history -0) | cut -d " " -f 2-)
	echo "+ $cmd" 
}

zsh

zshのHook Functionsの一つにprecmdという関数があります。 これがプロンプトが出力される直前に実行される関数だそうです。 また、TRAPDEBUGという関数を定義すれば、コマンドが実行される前に実行されます。

[設定例]

precmd () {
	cmd=$(echo $(history -1) | cut -d " " -f 2-)
	echo "+ $cmd" 
}

登場したシェルの中ではzshが最後発なので、 先行する様々なシェルのいろいろな特徴を取り入れている感じです。