簡単に言えば、いくつかのシステムの動作を追跡するために特定の実行可能ファイルが呼び出される方法を追跡したいと思います。実行可能ファイルがあるとしましょう。
/usr/bin/do_stuff
実際、シンボリックリンクを介してさまざまな名前で呼び出されます。
/usr/bin/make_tea -> /usr/bin/do_stuff
/usr/bin/make_coffee -> /usr/bin/do_stuff
など。明らかにdo_stuff
受け取った最初の引数は、取るべき実際のアクションを決定するために使用され、残りの引数はそれに応じて処理されます。
行われた呼び出し/usr/bin/do_stuff
(および完全なパラメータのリスト)を記録したいと思います。シンボリックリンクがない場合は、単に移動してdo_stuff
スクリプトdo_stuff_real
を作成するだけです。
#!/bin/sh
echo "$0 $@" >> logfile
/usr/bin/do_stuff_real "$@"
しかし、私が知っている限り、呼び出された名前を解決するのはうまくいきません。同じ目標を達成しながら、正しいdo_stuff
「実行ファイルで使用される名前」を渡すスクリプトをどのように書くことができますか?
記録のために次の質問に答えないようにするには:
- 私はCで(execveを使用して)それを行うことができることを知っていますが、この場合、シェルスクリプトを使用できる場合ははるかに簡単になります。
do_stuff
単にロギングプログラムに置き換えることはできません。
答え1
busybox
ほとんどの一般的なUNIXユーティリティを単一の実行可能ファイルとして提供するなどのユーティリティでこれをよく見ることができます。このユーティリティの動作は、呼び出されるかbusybox
渡されるacpid
項目によって異なりますzcat
。
argv[0]
通常、パラメータを見て何をすべきかを決定しますmain()
。単純な比較にしてはいけません。なぜなら、argv[0]
似たようなことがあるかもしれないしsleep
、そうかもしれないし/bin/sleep
、同じことをすることに決めなければならないからです。つまり、このパスは状況をより複雑にします。
したがって、ワーカーが正しく実行すると、ロギングラッパーが同じもので実行される可能性があります/bin/realstuff/make_tea
。ワーカーがデフォルト名だけを見ると、argv[0]
正しい関数を実行する必要があります。
#!/bin/sh -
myexec=/tmp/MYEXEC$$
mybase=`basename -- "$0"`
echo "$0 $@" >> logfile
mkdir "$myexec" || exit
ln -fs /usr/bin/real/do_stuff "$myexec/$mybase" || exit
"$myexec/$mybase" "$@"
ret=$?
rm -rf "$myexec"
exit "$ret"
上記の例では、argv[0]
次のように読み取る必要があります/tmp/MYEXEC4321/make_tea
(4321が実行されているPIDの場合/bin/sh
)。これはbasenamemake_tea
アクションをトリガーする必要があります。
argv[0]
ラッパーなしで状況を正確に複製したい場合は、より困難な問題に直面するでしょう。絶対ファイルパスが/
.で始まるため、新しいパスを作成できません。/bin/sleep
(私がいなければchroot
そこに行きたくないと思います。)ご存知のように、これを行うためにいくつかの味を使うことができますが、exec()
シェルで包まれません。
エイリアスを使用してロガーにアクセスし、スクリプトラッパーの代わりに基本プログラムを起動することを検討しましたか?限られた一連のイベントだけをキャプチャしますが、おそらくそのイベントにのみ興味があるかもしれません。
答え2
exec -a
(bash
、、、、にはありますがksh93
、zsh
まだPOSIXでは見つかりません)を使用して、実行中のコマンドを指定できます。mksh
yash
argv[0]
#! /bin/bash -
printf '%s\n' "$0 $*" >> /some/log
exec -a "$0" /usr/bin/do_stuff_real "$@"
$0
これはいいえ注文argv[0]
が届きました。これはに渡され、execve()
引数として渡されるスクリプトへのパスですが、bash
これはあなたの目的には十分です。
たとえば、ifは次make_tea
のように呼び出されます。
execv("/usr/bin/make_tea", ["make_tea", "--sugar=2"])
名前でコマンドを呼び出すと(で実行可能ファイルを見つける$PATH
)、シェルが通常行うように、ラッパーは次のことを行います。
execv("/usr/bin/do_stuff_real", ["/usr/bin/make_tea", "--sugar=2"])
それではありません:
execv("/usr/bin/do_stuff_real", ["make_tea", "--sugar=2"])
do_stuff_real
しかし、それが車のためのものであることを知るだけで十分です。
次のように呼び出される場合do_stuff
:
execv("/usr/bin/do_stuff", ["/usr/bin/make_tea", "--sugar=2"])
なぜなら、これは次のように翻訳されるからです:
execv("/usr/bin/do_stuff_real", ["/usr/bin/do_stuff", "--sugar=2"])
これは通常の作業中は発生しませんが、ラッパーが同様の操作を実行することに注意してください。
ほとんどのシステムでは、argv[0]
インタプリタが実行されると(ここでは)スクリプトに渡された内容が失われます(/bin/bash
ほとんどのシステムでは、通訳者のルートはargv[0]
シェバン行に提供されるルートです。) したがって、シェルスクリプトはこれについて何もできません。
を渡すには、argv[0]
実行可能ファイルをコンパイルする必要があります。それは次のとおりです。
#include <stdio.h>
int main(int argc, char *argv[], char *envp[])
{
/* add logging */
execve("/usr/bin/do_stuff_real", argv, envp);
perror("execve");
return 127;
}