コマンドがハッシュされる方法tcsh

コマンドがハッシュされる方法tcsh

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

  1. シェルが起動しました。
  2. 改訂中path
  3. コマンドを手動で実行しますrehash

したがって、シェルを起動すると、その中のすべてのコマンドがハッシュされ、path変更または実行しない限り、他のコマンドはハッシュされません。pathrehash

ハッシュが無効になったのはなぜですか?

マニュアルページでは、このような状況が発生する条件を指定します。

このハッシュメカニズムは使用されません。

  1. ハッシュが明示的に解放された場合unhash
  2. シェルに-fパラメータを提供する場合。

ここに例があります。そのフラグでシェルを起動すると、ハッシュ-fhashstat無効になり空になります。

$ 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に関連付けられている)の順序に従って番号が付けられます。pathtcsh

$ echo $PATH
/usr/sbin:/usr/bin:/sbin:/bin

私のディレクトリのPATH番号は次のとおりです。

  1. /usr/sbin
  2. /usr/bin
  3. /sbin
  4. /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)
  • passwd
    • 発見中ディレクトリ1/usr/bin)。

デバッグレベルを上げることで得られるもう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実行する前に、コマンド自体は 。sleepstracePATH

シェルがコマンドの場所をどのように見つけるかを実際に表示するには、他のstrace端末からシェルに接続してコマンドを実行する必要があります。

tcshたとえば、PID 21033のプロセスがあります。他の端末から対応するstracepidに接続しています。

$ 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これは、バージョンとディストリビューションでコンパイルされた方法によって異なります。

おすすめ記事