調達する前にファイルの存在を確認する理由


13

ファイルを入手しようとすると、ファイルが存在しないというエラーが表示されないので、何を修正すればよいかわかりますか?

たとえば、nvmはこれをプロファイル/ rcに追加することをお勧めします。

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

上記を使用nvm.shすると、存在しない場合は「サイレントエラー」が発生します。しかし、試してみる. "$NVM_DIR/nvm.sh"と、出力はになりますFILE_PATH: No such file or directory


3
してはいけません。それは際どいです。ソースを試し、エラーがある場合は処理します
Mikel

2
@Mikel右!なぜ私はどこでもそれを見るのですか?
-Jバリン

3
@FaheemMitha、まあ、あなたがやっていることがまったくセキュリティに敏感な場合(つまり、プログラムが他の誰かに代わって動作する場合)、あなたは本当に競合状態(TOCTOU)に注意する必要があります。ただし、誰かがHOME
-ilkkachu

8
@FaheemMitha 最初に存在を確認することは決して良いことではありません。状況はテストと使用の間で変化する可能性があり、誤検知と誤検知の両方が生じる可能性があります。とにかく、使用中に障害を処理する必要があります。また、パフォーマンスについて言及したように、使用前のテストは、そうしない場合とシステムにそれを行わせる場合の2倍の時間がかかります。ありえない。
user207421

1
@SimonRichter、この場合、nvmは機能しません。ユーザーは自分でファイルが欠落していることを理解する必要があります。
-JBallin

回答:


25

POSIXシェルで.は、は特別なビルトインであるため、失敗するとシェルが終了します(などの一部のシェルではbash、POSIXモードの場合のみ実行されます)。

エラーと見なされるものは、シェルによって異なります。ファイルの解析時に構文エラーですべてが終了するわけではありませんが、ソースファイルが見つからない場合や開くことができない場合はほとんどが終了します。ソース化されたファイルの最後のコマンドがゼロ以外の終了ステータスで返された場合に終了するものは知りません(errexitもちろんオプションがオンでない限り)。

ここでやっています:

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

ファイルが存在する場合はファイルをソースし、存在しない場合はソースにしたくない(または、ここで空になっている-s)場合です。

つまり、ファイルが存在しない場合、エラー(POSIXシェルでは致命的なエラー)と見なされるべきではなく、そのファイルはオプションファイルと見なされます。

ファイルが読み取り可能でない場合、またはディレクトリである場合(または一部のシェルで)構文解析中に構文エラーが発生した場合、それは(致命的な)エラーになります。

競合状態があると主張する人もいます。しかし、それは意味唯一のことは、ファイルが間に削除された場合、シェルはエラーで終了することをだろう[.、私はそれはスクリプトがある一方で、この固定パスファイルが消えて突然であろうとそれにエラーを考慮することが有効だと主張したいですランニング。

一方、

command . "$NVM_DIR/nvm.sh" 2> /dev/null

どこcommand¹が削除特別に属性を.通り動作しないでしょう(それはエラー時にシェルを終了しないように)コマンドを実行します。

  • .のエラーを隠しますが、ソースファイルで実行されるコマンドのエラーも隠します。
  • また、ファイルのアクセス権が間違っているなどの実際のエラー状態も隠されます。

その他の一般的な構文(たとえばgrep -r /etc/default /etc/init*systemdまだ変換されていない(EnvironmentFile=-/etc/default/service代わりにオプションの環境ファイルを指定するために使用される)initスクリプトについては、Debianシステムで参照してください):

  • [ -e "$file" ] && . "$file"

    そこにあるファイルを確認し、空の場合はソースを取得します。開くことができない場合は、致命的なエラーが発生します(そこにある、またはあったとしても)。[ -f "$file" ](存在し、通常のファイルである)、[ -r "$file" ](読み取り可能)、またはそれらの組み合わせのようなより多くのバリアントが表示される場合があります。

  • [ ! -e "$file" ] || . "$file"

    少し改善されたバージョン。存在しないファイルがOKケースであることを明確にします。これ$?は、最後に実行されたコマンドの終了ステータスを反映することも意味します$file(前のケースでは、を取得した場合、それが存在しなかったのか、そのコマンドが失敗したの1かわかりません$file)。

  • command . "$file"

    ファイルがそこにあることを期待しますが、解釈できない場合は終了しないでください。

  • [ ! -e "$file" ] || command . "$file"

    上記の組み合わせ:ファイルが存在しなくても問題ありません。POSIXシェルの場合、ファイルのオープン(または解析)の失敗は報告されますが、致命的ではありません(より望ましい場合があります~/.profile)。


¹注:zshただし、エミュレーションのcommand場合を除き、そのようなものは使用できませんsh。Kornシェルでは、そのノートsource実際の別名であるcommand .、の非特殊なバリアント.


面白い!POSIX shについては知りませんでした。しかし、問題はについて.bash_profileでした。申し訳ありませんが安全よりも安全だと思いますが、bashはPOSIXモード.bash_profileでソースされた場合、これまでにないですか
ミケル

(質問のnvmソースリンクの読み取りに基づいて、この質問をすべてのPOSIXシェルにより広く適用すると解釈できることを理解しています。)
Mikel

@Mikel、私の答えbashは、POSIXモードでないときにも適用されます。あなたはしたいと思い[ -e /file ] && . /fileますが、ファイルが存在しない場合、それは誤りであることを考慮していない場合。もしあればトライ源はその後、エラーを処理し、ここで行うことはできません。
ステファンシャゼラス

1
@Mikel、それは逆効果です。1)POSIXシェル(またはPOSIXモードではbash)でのエラー時の終了を防止しません。2)エラーメッセージ(stdoutの場合)を複製し、.既にエラーを報告します(stderrの場合)。そして、ファイルが存在しないときにエラーと見なさないことを意図している場合、それは正しくありません(そして.、ファイルが存在しないか読み取り可能でなかったために失敗したかどうかを終了ステータスから判断することは不可能です解析できない、または最後のコマンドが失敗した)、これがこの回答でここで行っているポイントです。
ステファンシャゼラス

1
競合状態に関しては、ログインが一貫して失敗するよりも(ユーザーの設定にまったく正しくないものがあったとしても)、ログインが1回失敗した場合(システムで奇妙なことが起こっている場合)の問題ははるかに少ないです。-ファイル)。そのため、このチェックの競合状態は、チェックを持たないことよりも改善されています。
-ruakh

5

のメンテナーnvm

ファイルを削除するだけでnvmを簡単にアンインストールできます。追加の作業を強制する(その行がソースnvmである場所を追跡する)ことは特に価値があるとは思えません。

私の解釈(ステファンの優れた説明とクサラナンダのコメントと組み合わせて):

よりシンプルで安全です。

(さまざまな理由で)ファイルが見つからないために、起動時にPOSIXシェルが終了するのを防ぎます。非POSIX(bashなど)シェルを使用している場合は、必要に応じて条件を削除できます。


1
「初心者向け」だというあなたの解釈に反対します。防御的です。そのファイルが(何らかの理由で)なくなったという理由だけで、起動時にユーザーのログインシェルが予期せず終了することは望ましくありません。そのコード行がの下にあるシェルの初期化ファイルの1つにある/etc場合、一部のユーザーがファイルを所有し、一部のユーザーがファイルを所有できなくなります。私見、nvmメンテナの反応は一面にしか触れていません。
クサラナナンダ

1

なので JBallinステファンChazelas失敗へのログインを引き起こす存在しないファイルをソース、POSIXシェルでは、指摘しています。

しかし、ファイルが存在するかどうかを確認するテストを追加してから、それをソースしようとすると、競合状態と呼ばれるものが発生する可能性があります。何かが変わったらnvm.sh[ -s nvm.sh ]との間でされた. nvm.sh、まれにではありますが、防止しようとしているバグを正確に引き起こします。

一般に、競合状態を防ぐ方法は、やりたいことを試し、失敗した場合はエラーを処理することです。

. "$NVM_DIR/nvm.sh" || echo "Sourcing $NVM_DIR/nvm.sh failed" >&2

上記のように、.失敗するとエラー処理が実行される前にシェルがすぐに終了するため、これはPOSIXシェルでは機能しません。

私の答えは.bash_profile、POSIXモードで実行するべきではないため、POSIXシェルはこの質問には関係ないと主張しています。とにかく上記のコードを実行できます。

最も安全にするために、POSIXモードが有効でないことを確認するか、POSIXモードが https://unix.stackexchange.com/a/383581/3169でます

Stéphaneの答えには、すべてのPOSIXシェルを処理する方法についていくつかの有用な提案がありますが、これはnvm作者の意図であると思いますが、ここでの質問とは微妙に異なっていたため、目標に応じて複数の可能なアプローチがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.