POSIXシェル標準はこのサイトで述べています
http://pubs.opengroup.org/onlinepubs/9699919799/
シェルがPATH
を使用して実行可能ファイルを検索する方法について:
「リストは最初から最後まで検索され、指定された名前と適切な実行権限を持つ実行可能ファイルが見つかるまで、各プレフィックスにファイル名が適用されます。」
まあ、これは実際のPOSIX実装でこれがどのように機能するように見えるかではありません:
man which
言う:
"は、引数が厳密にPOSIX準拠のシェルでコマンドとして指定されている場合に、現在の環境で実行されるファイル(またはリンク)のパス名を返します。これは、PATHを検索して、の名前に一致する実行可能ファイルを探します。引数。シンボリックリンクをたどりません。」
OK、この状況を見てみましょう:
$ pwd /home/mark
$ echo $PATH /home/mark/bin:
.。
$ ls -l bin/foobar
lrwxrwxrwx 1 mark mark 18 Dec 12 22:51 bin/foobar -> /home/mark/foobar1
$ touch foobar1
$ which foobar
$ chmod a+x foobar1
$ which foobar
/home/mark/bin/foobar
OK、これはPATH
に正しい名前のシンボリックリンクがあり、ls
によって実行可能であると報告されています。
which
はそれをまったく調べませんが、それが指しているものにのみ関心があります。
それにもかかわらず、両方のman which
は、シンボリックリンクをたどらないことを明示的に示しています(実際、which foobar
は印刷しませんfoobar1
)、および上記で引用したPOSIXシェルのドキュメントでは、PATH
アルゴリズムで次のシンボリックリンクについて言及することはありません。
では、which
と既存のシェルが間違っているのでしょうか、それともドキュメントを理解していないのでしょうか。
明確にするために:
私は既存の行動を知っており、説明することができます。私の質問は「これはどのように機能するのか」ではありません。私が知っています。
私の質問はドキュメンテーションについてです:私が引用したドキュメンテーションに従うことの私の間違いはどこにありますか。または、ドキュメントが間違っていますか?
動機:なぜ私は気にするのですか?
ええと、私は実装者です。実装者が異なれば、要件も異なります。私にとっての要件は、現在のPOSIX標準のWordに正確に従わなければならないことです(または、より正確には、標準自体がややバグがあるため、可能な限り最高のものです)。それが神の言葉であったように。
さて、標準的な表現はかなり明確です-以下のシンボリックリンクは言及されていませんが、他の多くの場所では、それを行う必要がある場所で言及されています。したがって、この場合はしないでください。
ただし、念のため、dash
とbash
の動作を常に再確認しています。もちろん、ここでも小さな問題があります。dash
はPOSIXとして請求されますが、POSIXに準拠した小さなバグがたくさんあります。 bash
、POSIXのバグはまだ見つかりませんが、... bashは実際にはPOSIXではなく、それだけではありません。
だからあなたはそれを持っています。それが私が気にする理由です。
シンボリックリンク自体の権限は関係ありません。試してみても変更できませんでした。
重要なのは、基になるファイルのアクセス許可です。
PATH内のディレクトリに実行可能ファイルへのシンボリックリンクを含めることは問題ありません。実際、PATH内の多くの実行可能ファイルはシンボリックリンクである可能性があります。たとえば、debian/ubuntuのようなシステムでは次のようになります。
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 23 2017 /bin/sh -> dash
man chmod
から:
chmodはシンボリックリンクのパーミッションを変更しません。 chmodシステムコールはパーミッションを変更できません。 シンボリックリンクのパーミッションは使用されないため、これは問題ではありません。ただし、コマンドラインにリストされているシンボリックリンクごとに、chmodはのパーミッションを変更します。ポイントされたファイル。対照的に、chmodは、再帰的なディレクトリトラバーサル中に検出されたシンボリックリンクを無視します。 [強調が追加されました。]
シェルには、ファイルが実行可能かどうかを判断するためのテスト-x
があります。それを試してみましょう:
$ ls -l
total 0
lrwxrwxrwx 1 john1024 john1024 7 Dec 12 23:36 foo -> foobar1
-rw-rw---- 1 john1024 john1024 0 Dec 12 23:36 foobar1
$ [ -x foo ] && echo foo is executable
$ chmod +x foobar1
$ [ -x foo ] && echo foo is executable
foo is executable
したがって、which
で見つけたように、シェルは、基になるファイルが実行可能でない限り、ソフトリンク実行可能ファイルを考慮しません。
Debianシステムでは、which
はシェルスクリプトです。コードの関連セクションは次のとおりです。
case $PROGRAM in
*/*)
if [ -f "$PROGRAM" ] && [ -x "$PROGRAM" ]; then
puts "$PROGRAM"
RET=0
fi
;;
*)
for ELEMENT in $PATH; do
if [ -z "$ELEMENT" ]; then
ELEMENT=.
fi
if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
puts "$ELEMENT/$PROGRAM"
RET=0
[ "$ALLMATCHES" -eq 1 ] || break
fi
done
;;
esac
ご覧のとおり、-x
テストを使用して、ファイルが実行可能かどうかを判断します。
POSIXは、-x
テストを次のように指定します。
-xパス名
パス名がファイルを実行する権限を持つファイルの既存のディレクトリエントリに解決される場合はTrue(または、ファイルを実行する場合は検索する)ファイルの読み取り、書き込み、および作成で定義されているように、ディレクトリ)が付与されます。パス名を解決できない場合、またはパス名がファイルの実行(または検索)のアクセス許可が付与されないファイルの既存のディレクトリエントリに解決される場合はFalse。 [強調が追加されました。]
したがって、POSIXは、パス名が何を解決するかをチェックします。つまり、シンボリックリンクを受け入れます。
POSIX exec function はシンボリックリンクに従います。 POSIX仕様は、シンボリックリンクが循環している場合や深すぎる場合に報告される可能性のあるエラー条件を指定するために詳細に説明されています。
[ELOOP]
パスまたはファイル引数の解決中に検出されたシンボリックリンクにループが存在します。
[ELOOP]
パスまたはファイル引数の解決中に、{SYMLOOP_MAX}を超えるシンボリックリンクが検出されました。
[ENAMETOOLONG]
パス引数の解決中にシンボリックリンクが検出された結果、置換されたパス名文字列の長さが{PATH_MAX}を超えました。
この場合、シンボリックリンクは透過的に、最終パスを正規化せずに追跡されます。言い換えると、which
は、/home/mark/bin
がシンボリックリンクであるかどうかを気にしません。気になるのは、ファイル/home/mark/bin/foobar
が存在するかどうかです。パスに沿ってシンボリックリンクを手動でフラット化する必要はありません– OSはそれ自体で問題なく実行できます。
実際、which
が/home/mark/bin/foobar
のファイル情報について尋ねると、OSは/home/mark/bin
がシンボリックリンクであることに気づき、それをたどり、ターゲットディレクトリでfoobar
を正常に見つけます。
プログラムがopen(…, O_NOFOLLOW)
またはfstatat(…, AT_SYMLINK_NOFOLLOW)
を使用してファイルにアクセスしない限り、これはデフォルトの動作です。
[コメントがマージされました]
シェルユーティリティはケースバイケースでそれを行うとあなたは言いますが、それはカーネルのsyscallと同じではありません:すべてのファイル関連の呼び出しdo 「nofollow」フラグが指定されていない限り、デフォルトでシンボリックリンクをたどります。 (lstatでさえ、最後のコンポーネントを除くすべてのパスコンポーネントでシンボリックリンクをたどります。)
仕様にシンボリックリンクの処理方法が明示的に記載されていない場合は、デフォルトの動作が使用されることを意味します。つまり、パスアルゴリズムに従うシェルは、シンボリックリンクを手動で解決することも、OSから明示的にオプトアウトすることもありません。 (各$ PATHコンポーネントを実行可能ファイル名と連結するだけです。)
Which(1)のマニュアルページにシンボリックリンクに従わないと書かれている場合、それはいくつかのことを意味する可能性がありますが、GNU coreutilsバージョンは次のように述べています。
シンボリックリンクのあるパスが一方に含まれている場合、2つの同等のディレクトリは異なると見なされます。
これは範囲がはるかに狭いです。つまり、which
が手動で重複を取り除くためにすべてのパスを正規化しようとしないことを意味しますが、ツールが一般的にOSによるシンボリックリンクをオプトアウトすることを意味するものではありません。たとえば、/bin
が/usr/bin
へのシンボリックリンクである場合、which -a sh
を実行すると、both/bin/sh
と/usr/bin/sh
が返されます。
シェルは、パス名解決のルールに従うという点で、そのドキュメントに準拠しています。 which
はそのドキュメントに準拠しています。 2つはわずかに異なることをします。
which
の出力は、リンクのファイル名とパスであり、シンボリックリンクが指すパスではありません。これはmanページで詳しく説明されています。
コマンドが実行されると、セクション4.13パス名の解決 同じ に従って、リンクが「追跡」されます。ファイルを実行するための関連する句は次のとおりです。
他のすべての場合、システムは残りのパス名がある場合は、シンボリックリンクの内容をプレフィックスとして付けます。ただし、シンボリックリンクの内容が空の文字列の場合、いずれかのパス名の解決は失敗し、関数は[ENOENT ]エラーおよび同等の診断メッセージを書き込むユーティリティ、またはシンボリックリンクを含むディレクトリのパス名をシンボリックリンクの内容の代わりに使用する必要があります。シンボリックリンクの内容が文字のみで構成されている場合、残りのパス名のすべての先頭文字は、結果の結合パス名から省略され、シンボリックリンクの内容の先頭文字のみが残ります。プレフィックスが発生する場合、結合された長さが{PATH_MAX}を超え、実装がこれをエラーと見なすと、パス名の解決は失敗し、関数は[ENAMETOOLONG]エラーを報告し、ユーティリティは同等の診断メッセージを書き込みます。それ以外の場合、解決されるパス名は、作成されたばかりのパス名の解決になります。結果のパス名がで始まらない場合、パス名の最初のファイル名の先行は、シンボリックリンクを含むディレクトリと見なされます。