リダイレクトが失敗した場合、bashプログラムは実行されません


9

bashでは、リダイレクトを使用するコマンドが失敗すると、それより前に実行されていたプログラムは実行されないことに気づきました。

たとえば、このプログラムはファイル "a"を開き、50バイトをファイル "a"に書き込みます。ただし、権限が不十分なファイル(〜root / log)へのリダイレクトを指定してこのコマンドを実行しても、「a」のファイルサイズは変更されません。

$ ./write_file.py >> ~root/log
-bash: /var/root/log: Permission denied
cdal at Mac in ~/experimental/unix_write
$ ls -lt
total 16
-rw-rw-r--  1 cdal  staff  0 Apr 27 08:54 a <-- SHOULD BE 50 BYTES

プログラムが実行され、すべての出力がキャプチャされ(ファイル "a"にも書き込まれる)、その後、出力が〜root / logに書き込まれなくなると考えられます。代わりに、プログラムは実行されません。

これはなぜですか、またbashはプログラムを実行する前に実行する「チェック」の順序をどのように選択するのですか?他のチェックも実行されますか?

PS私は、「許可が拒否された」ファイルにリダイレクトされたときに、cronで実行されているプログラムが実際に実行されたかどうかを確認しようとしています。


すべてが正常に機能しています(つまり、.pyファイルの所有権とアクセス権)。プログラムは正常に実行されます。あなたの問題はリダイレクトから来ています。/ rootディレクトリにファイルを書き込む権限がありません。そして、あなたはstdoutそれを正確に行うようにリダイレクトしました。したがって、プログラムが実行されても、出力は表示されません。
MelBurslan 2016

2
メル、それは本当ではない、プログラムが実際に実行されたことはない。以下の回答をご覧ください。
Charlie Dalsass 16

あなた:「write_file.pyプログラムを実行して、その出力を~root/logbashに送信します:「申し訳ありませんが、そのファイルへの書き込みは許可されていません!」シェルは、本来あるべきことを実行しています。すると、すぐに問題の理由が通知され、対処方法を決定する機会が与えられます。すべてのbashメンテナーが知っているように、そのコマンドを実行して出力を保存しないと、非常に悪いことが発生する可能性があります。保存する場所を指定することが十分に重要である場合、ASS | U | MEは、stdoutを保存せずに実行しても問題ありません
Monty Harder

回答:


18

これは実際にはチェックの順序付けの問題ではなく、シェルがセットアップする順序です。リダイレクトは、コマンドが実行される前にセットアップされます。そのため、あなたの例では、シェルはに~root/log関連することを行う前に、追加のためにオープンを試み./write_file.pyます。ログファイルを開くことができないため、リダイレクトは失敗し、シェルはその時点でコマンドラインの処理を停止します。

これを示す1つの方法は、実行不可能なファイルを取得して実行することです。

$ touch demo
$ ./demo
zsh: permission denied: ./demo
$ ./demo > ~root/log
zsh: permission denied: /root/log

これは./demo、リダイレクトを設定できない場合、シェルがそれを見さえしないことを示しています。


うわー、それはとても簡単ですか?私はリダイレクトが最初に行われることを知りませんでした。この回答と他のすばらしい回答にも感謝します。
Charlie Dalsass 16

6
最初に行われなかった場合、出力はどこに書き込まれますか?
Charles Duffy

また、出力を書き込めない場合、どのようにしてコマンドを実行しても安全であることがわかりますか?コマンドがデータストアから削除されている情報を出力する可能性があるため、出力をキャプチャすることが絶対に必要です。あなたがそれらの権限を修正するまで、bashはそれを実行させません。
モンティハーダー

11

bashのmanページ、セクションのリダイレクト(私が強調):

コマンドが実行される前に、その入力と出力は、シェルによって解釈される特別な表記法を使用してリダイレクトされます。

...

ファイルのオープンまたは作成に失敗すると、リダイレクトが失敗します。

そのため、シェルはのターゲットファイルを開こうとしますがstdout、失敗し、コマンドはまったく実行されません。


本当にありがとう。マニュアルページで「出力をリダイレクトできない場合、プログラムは実行されない」と少し明確にしてほしい。
Charlie Dalsass 16

更新しました; 以下のいくつかの段落は隠されています。
マーフィー

実際、それはかなり明確です。「ファイルのオープンまたは作成に失敗すると、リダイレクトが失敗します。」そこにそれがある。再度、感謝します。
Charlie Dalsass 16

3

シェルがプログラムを開始する前にリダイレクトを確立する必要があることを観察する価値があります。

あなたの例を考えてみましょう:

./write_file.py >> ~root/log

シェルで何が起こるか:

  1. 私たち(シェル)fork(); 子プロセスは、その親(シェル)から開いているファイル記述子を継承します。
  2. 子プロセスではfopen()、「〜root / log」(の展開)dup2()をfd 1(およびclose()一時的なfd)に変換します。がfopen()失敗した場合は、呼び出しexit()てエラーを親に報告します。
  3. まだ子には、exec()「./ write_file.py」があります。このプロセスではコードが実行されなくなりました(実行に失敗した場合を除きます。この場合exit()、親にエラーを報告します)。
  4. 親はwait()子を終了させ、その終了コードを($?少なくともにコピーすることによって)処理します。

したがって、リダイレクトはfork()and の間の子でexec()発生するfork()必要があります。これは、シェルのstdoutを変更してはならないため、前に発生することはできません。exec()ファイル名とシェルの実行可能コードがPythonプログラムによって置き換えられたため、発生することはありません。。親は子のファイル記述子にアクセスできません(アクセスしても、exec()stdoutへの最初の書き込みとの間でリダイレクトすることを保証できませんでした)。


0

正反対のことをお詫び申し上げます。シェルはまずI / Oを開き、次に制御をプログラムに渡す必要があります。

tee この場合、役立つかもしれません: ./write_file.py | tee -a ~root/log > /dev/null


ティーが失敗した後、PythonスクリプトはSIGPIPEでただ死ぬのではないですか?
ケビン

私が行ったテストではありませんが、試してみることをお勧めします。
ジュリーペルティエ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.