tcsh環境では、コマンドハッシュがデフォルトで無効になっているように見え、全体的に有効にすることはできません。代わりに、すべてのwhileループを含む個々のスクリプトでコマンドハッシュを有効にして、最初の反復で$ PATHで定義されたすべてのパスを繰り返すことができ、その後の反復で内部スクリプトの正確なパスに到達できるようにします。ハッシュテーブル。目的は、監査サービスによってキャプチャされた失敗したexecve呼び出しの数を減らすことです。
最初の質問は、内部ハッシュテーブルを出力するためにtcshに "hash"に似たコマンドがありますか? Hashstatがうまくいかないようです。プロンプトに何も出力しません。おそらくハッシュが無効になっているのでしょうか?何かを印刷すると、特定のコマンドではなくハッシュバケットの数とサイズだけが印刷されます。
主な問題は、スクリプトの先頭に「rehash」を追加してみました。これは、コマンドごとのexecve呼び出しの数を〜5から〜2(最初の繰り返しでも)に減らすのに役立ちました。何らかの理由で常に「/sbin」でコマンドを最初に実行しようとします。 rehashを実行した後でも、間違ったパスでコマンドを実行しようとしている理由を確認するために何を確認する必要があるかについての提案はありますか?それとも、スクリプト内でコマンドハッシュを有効にする別の方法はありますか?
一方、サイドの問題は、ハッシュテーブルが無効になっていても、bashがまだ正しいパスを見つけることができることです。コマンドハッシュなしでこれを行う方法を知っていますか?
最後に、straceは監査でキャプチャされた失敗したexecve呼び出しをキャプチャしませんでした。私はsimplestrace sleep
とを試しましたが、strace -f -e trace=execve sleep
どちらもデフォルトで正しい項目だけを表示しますが、失敗した項目は表示しません。
execve("/bin/sleep", ["sleep"], 0x7ffe0d773ff8 /* 32 vars */) = 0
。
答え1
かなり複雑な質問なのに説明します。
コマンドがハッシュされる方法tcsh
コマンドハッシュはtcsh
のコマンドハッシュとは大きく異なりますbash
。では、bash
初めてコマンドを実行するたびに、PATH
環境変数からその場所を検索してキャッシュします。次に同じコマンドを実行すると、ハッシュされたフルパスが使用されます。
関連部分ですtcsh
マニュアルページ:
rehash
path
変数内のディレクトリの内容の内部ハッシュテーブルが再計算されます。ログイン中にディレクトリに新しいコマンドが追加された場合はpath
必須です。これは、自分のディレクトリの1つにコマンドを追加した場合、またはシステムプログラマがシステムディレクトリの1つの内容を変更した場合にのみ必要です。また、チルダ拡張で作成されたホームディレクトリのキャッシュもフラッシュします。
path
実行可能なコマンドを検索するディレクトリのリストです。 [...] オプション
-c
も-t
オプションも提供されないシェルパスのディレクトリ内容は読み取られ、ハッシュされ、~/.tcshrc
毎回path
リセットされます。。path
シェルが有効になっている間にディレクトリに新しいコマンドを追加した場合は、rehash
そのコマンドを見つけるためにシェルでaを実行する必要があります。
tcsh
つまり、変数ディレクトリ内のすべてのコマンドは、次の場合にのみハッシュされますpath
。
- シェルが起動しました。
- 改訂中
path
。 - コマンドを手動で実行します
rehash
。
したがって、シェルを起動すると、その中のすべてのコマンドがハッシュされ、path
変更または実行しない限り、他のコマンドはハッシュされません。path
rehash
ハッシュが無効になったのはなぜですか?
マニュアルページでは、このような状況が発生する条件を指定します。
このハッシュメカニズムは使用されません。
- ハッシュが明示的に解放された場合
unhash
。- シェルに
-f
パラメータを提供する場合。
ここに例があります。そのフラグでシェルを起動すると、ハッシュ-f
がhashstat
無効になり空になります。
$ tcsh -f
> hashstat
どこかでコマンドを実行しても同じことが起こりますunhash
。
ただし、どちらの場合も「rehash」を実行して有効にすることができます。
> rehash
> hashstat
512 hash buckets of 8 bits each
tcsh
ハッシュが有効になっていても、一部のコマンドがまだ別のパスで実行されるのはなぜですか?
理解するhashstat
出力を見てみましょうhashstat
。
$ hashstat
512 hash buckets of 8 bits each
512個のハッシュバケットがあることがわかります。tcsh
ハッシュ関数を使用して各コマンドのハッシュIDを計算します。この場合、値は0から511の間です。これは、いくつかのコマンドが同じハッシュを持つことができることを意味します。バケットとビットの初期数はによって異なりますPATH
。
2つのコマンドAとBが同じハッシュ値を持っているとします(両方のコマンドのハッシュ値が123であると仮定します)。コマンドAがあり、/bin
コマンドBがあるとします/usr/bin
。それではどうなりますか?
これは、ハッシュIDが123のバケットに両方のパス(両方とも/bin
)があることを意味します/usr/bin
。 AまたはBを実行すると、成功するまで両方のディレクトリexec
(にある順序で)からそのコマンドを実行しようとします。PATH
この回答の下には例が表示されます。しかし、今話しましょうrehash
。
rehash
「秘密」パラメータ
rehash
組み込み関数には、tcsh
ソースコードでのみ表示される文書化されていないいくつかの「秘密」オプションのパラメータがあります。オプションは次のとおりです。
rehash [hashlength [hashwidth [debug]]]
たとえば、次の方法でハッシュバケットの数を変更できます。
$ hashstat
512 hash buckets of 8 bits each # At the beginning, 512 buckets
$ rehash 4096 # increasing the bucket number
$ hashstat
4096 hash buckets of 8 bits each # Now there are 4096 buckets instead of 512
ハッシュ幅は長さが0に設定されている場合にのみ機能するため、説明しません(幅はハッシュ長を計算するために内部的に使用されます)。それ以外には何の意味もありません。
重要なヒント
テーブルの長さを長くすると、異なるコマンド間で発生する可能性がある競合の数を最小限に抑えるのに役立ちます。したがって、いくつかのテストを実行している場合は、試してみていくつかの改善があるかどうかを確認できます。
それでも、本物面白い部分はまだ来ていません。
内部ハッシュテーブルの表示
最後のパラメータを1または3に設定して、ハッシュジョブのデバッグを増やすことができます。それがどのように機能するか見てみましょう。
$ rehash 0 0 3
hash=19 dir=0 prog=addgnupghome
hash=0 dir=0 prog=addpart
hash=206 dir=0 prog=agetty
hash=498 dir=0 prog=alternatives
hash=463 dir=0 prog=applygnupgdefaults
hash=323 dir=0 prog=blkdiscard
hash=342 dir=0 prog=blkid
hash=277 dir=0 prog=blkzone
hash=410 dir=0 prog=blockdev
hash=500 dir=0 prog=cfdisk
[...]
すべてのコマンドのハッシュが何であるか、ハッシュがどこにあるかを正確に表示します。
たとえば、このコマンドのaddgnupghome
ハッシュ値は19で、ディレクトリ番号0にあります。カタログ番号0は何ですか?
ディレクトリは、環境変数(のシェル変数PATH
に関連付けられている)の順序に従って番号が付けられます。path
tcsh
$ echo $PATH
/usr/sbin:/usr/bin:/sbin:/bin
私のディレクトリのPATH
番号は次のとおりです。
/usr/sbin
/usr/bin
/sbin
/bin
したがって、コマンドがディレクトリ番号0にある場合は、addgnupghome
そのコマンドがにあることを意味します/usr/sbin
。
はい
rehashコマンドの出力(デバッグを3に設定した後)をファイルに保存しましょう。
$ rehash > rehash.log
それでは、特定のハッシュを見てみましょう。
$ grep hash=498 rehash.log
hash=498 dir=0 prog=alternatives
hash=498 dir=1 prog=passwd
同じハッシュ値(498)にマップされた2つのコマンドがあります。
alternatives
- 発見中ディレクトリ0(
/usr/sbin
)
- 発見中ディレクトリ0(
passwd
- 発見中ディレクトリ1(
/usr/bin
)。
- 発見中ディレクトリ1(
デバッグレベルを上げることで得られるもう1つの利点は組み込みですwhere
(「エイリアス、組み込み、実行可能ファイルなど、既知のすべてのコマンドインスタンスを報告します。path
」)。通常、実行可能ファイルが見つかった場所のみが表示されますが、デバッグを増やすと見逃した場所も表示されます。
$ where alternatives
/usr/sbin/alternatives
hash miss: /usr/bin/alternatives
$ where passwd
hash miss: /usr/sbin/passwd
/usr/bin/passwd
/usr/sbin
これらの2つのコマンドに対して2つのディレクトリを検索することがわかります/usr/bin
。
で確認してくださいstrace
。
あなたは次のように書きました:
最後に、監査でキャプチャされた失敗した呼び出しは
strace
キャプチャされません。execve
私はsimplestrace sleep
とを試しましたが、strace -f -e trace=execve sleep
どちらもデフォルトで正しい項目だけを表示しますが、失敗した項目は表示しません。execve("/bin/sleep", ["sleep"], 0x7ffe0d773ff8 /* 32 vars */) = 0
を実行すると、strace sleep
もはや場所を検索するシェルではなく、コマンド用sleep
ですstrace
。シェルは実際にコマンドを実行した場所、つまり行の最初の単語(この場合strace
)のみを検索します。残りの単語は、コマンドに渡された引数として処理されます。したがって、コマンドをstrace
実行する前に、コマンド自体は 。sleep
strace
PATH
シェルがコマンドの場所をどのように見つけるかを実際に表示するには、他のstrace
端末からシェルに接続してコマンドを実行する必要があります。
tcsh
たとえば、PID 21033のプロセスがあります。他の端末から対応するstrace
pidに接続しています。
$ strace -f -qq -e trace=execve -p 21033
alternatives
リンクシェルで実行すると、このハッシュリストの最初のディレクトリですぐにそのシェルを見つけます。
[pid 19134] execve("/usr/sbin/alternatives", ["alternatives", "--help"], [/* 125 vars */]) = 0
ただし、passwd
コマンドは2番目のディレクトリにあり、execve
最初のディレクトリでは失敗します。
[pid 20919] execve("/usr/sbin/passwd", ["passwd", "-h"], [/* 125 vars */]) = -1 ENOENT (No such file or directory)
[pid 20919] execve("/usr/bin/passwd", ["passwd", "-h"], [/* 125 vars */]) = 0
以下は、ハッシュを1つのコマンドにのみマッピングする反対の例です。
$ grep hash=102 rehash.log
hash=102 dir=1 prog=curl # Only curl has hash 102
$ where curl
/usr/bin/curl # And indeed "where" only tries one dir
婦人声明
私がここに書いたすべての内容は私の経験に基づいて書かれています。tcsh
ソースコード私の環境でテストしました(私のバージョンは6.20.00です)。tcsh
これは、バージョンとディストリビューションでコンパイルされた方法によって異なります。