他のスクリプトから呼び出すと、私のスクリプトで読み取りが自動的に失敗するように見えるのはなぜですか?

他のスクリプトから呼び出すと、私のスクリプトで読み取りが自動的に失敗するように見えるのはなぜですか?

あなたのために絵を描いてみましょう。ユーザーの作成、パッケージのインストール/更新などのタスクを実行するためにリモートでコマンドを実行するいくつかのデプロイスクリプトまたはビルドスクリプトなどを作成するので、追加するのを--gecos ''忘れたりadduser追加したりしない可能性があります。DEBIAN_FRONTEND=noninteractiveあなたのアパートやあなたが知らないものについてこの情報を提供してください。今日、これらのスクリプトは非対話型である傾向があります。プロンプトを受信したくないので、何らかの理由でプロンプトが発生した場合は、ジョブ全体が失敗することを望みます。しかし、この状況でbashを使用すると、ゼロ以外のエラーコードで失敗するのではなく(私にもset -e)最悪の作業を実行することがわかります。中断して0を返します。これは、呼び出し元がスクリプトが正しく完了していないが実際に中断されたことを知る方法がないことを意味します。

以下は、問題を簡単に説明して解決する愚かな例です。このスクリプトの周りで多くのことが起こることを想像できます。たぶん、このコードは実際にsshを使用してリモートで実行され、一部のパッケージはetcの代わりにインストールされ、read lineこのブロックの後にすべてのコードが正常に実行されると予想されます。

set -e
# Do stuf...

/bin/bash <<SH
    echo gonna try to be interactive
    read line #tries to read a line from "user"
    echo you wont see me #Will not be executed as Bash will halt execution on previous failing line, this could be vital bit of code to execute, but it's now silently skipped
SH
echo return value $?
#I want to stop here if the above bit of bash failed because some code tried to get user input

出力

gonna try to be interactive
return value 0

bin/shbashの代わりに使用すると、他の動作が発生することがわかりました。読み込み直後に自動的に実行を停止せずに0を返します。実際にはデフォルトで続行されるので、実際にset -eは戻り値1を提供するように明示的に「段落」する必要があります。しかし、bashを正しく機能させる方法はありますか?

ベストアンサー1

他のコマンドと同様に、戻り状態を明示的に確認するreadset -eset -e万病の歯磨き粉ではありませんしかし、まったく使用しないよりも優れています。

どの言語機能も意味がありますが、目的の操作を行わないコードからユーザーを確実に保護することはできません。コンピュータは、通常どおりユーザーが望む操作ではなく、ユーザーが指示した操作を実行するため、コードからエラー信号を送信しません。

/bin/bash <<SH

Bashの標準入力は、ここにある文書の内容を含むファイル(実行されるシェルに応じて一時ファイルまたはパイプ)に接続されます。

read line #tries to read a line from "user"

いいえ、「ユーザーから一行を読みません」。標準入力から1行読みます。ここに文書があります。効果は、シェルがスクリプトを解析する方法によって異なります。これが交換/bin/bash/bin/sh動作を変更する理由です。この特定のスクリプトとこの特定のバージョンのbashの場合、bashは一度に1行だけスクリプトを読み取ります。したがって、1行を読むように指示されたら、それを実行してline設定しますecho you wont see me #Will not be executed as Bash will halt execution on previous failing line, this could be vital bit of code to execute, but it's now silently skipped。コマンドが正常に完了すると、readbash は次の行を実行します。スクリプトの終わりに達したので、最後のコマンドの状態である0を返します。

何が起こっているかを確認するには、別のコード行を追加してください。

set -e
# Do stuf...

/bin/bash <<'SH'
    echo gonna try to be interactive
    read line #tries to read a line from "user"
    echo you wont see me #Will not be executed as Bash will halt execution on previous failing line, this could be vital bit of code to execute, but it's now silently skipped
    echo "Previous command='$_'; status=$?; line='$line'"
SH
echo return value $?

出力:

gonna try to be interactive
Previous command='line'; status=0; line='echo you wont see me #Will not be executed as Bash will halt execution on previous failing line, this could be vital bit of code to execute, but it's now silently skipped'
return value 0

コマンドが正しくありません。行は失敗せず、bashは実行を停止しませんでした。この行は bash にデータとして読み込むように指示したのでスキップしました。

ダッシュは、スクリプトを実行する前にスクリプト全体を読み取るため、異なる動作を観察します。たとえば、コードが関数の内部にある場合は、bashでもこれを観察できます。

子シェルが親シェルの標準入力を読み取るようにするには、子シェルの標準入力をリダイレクトしたり、親シェルの標準入力を他のファイル記述子で使用できるように設定しないでください。


ユーザーの作成、パッケージのインストール/更新などのタスクを実行するためにリモートでコマンドを実行するデプロイスクリプトやビルドスクリプトなどを作成するので、adduserに--gecos ''を追加することを忘れてしまう可能性があります。 no DEBIAN_FRONTEND =すべてのアパートやあなたが知らないことについて非対話型です。今日、これらのスクリプトは非対話型である傾向があります。プロンプトを受信したくないので、何らかの理由でプロンプトが発生した場合は、ジョブ全体が失敗することを望みます。

これはまったく異なる質問です。デプロイスクリプトの標準入力がに接続されていることを確認してください/dev/null。これにより、標準入力から読み取ろうとすると読み取りが失敗します。

ディストリビューターが間違った場所から読み取ろうとしたり、読み取り失敗に正しく反応しない場合、これは環境準備で修正できないディストリビューターのバグです。

¹コードが仕様に準拠していることを証明する方法はありませんが、そうしたい場合はスクリプト言語を使用しません。

おすすめ記事