コード「{exec> / dev / null; }> / dev / null」内部で何が起きているのでしょうか?


15

execリダイレクションを含むコマンドリストをリダイレクトする場合、exec> / dev / nullは後で適用されるようには見えません:

{ exec >/dev/null; } >/dev/null; echo "Hi"

「こんにちは」と印刷されます。

{}コマンドリストはパイプラインの一部でない限り、サブシェルとは見なされないためexec >/dev/null、現在のシェル環境内でコマンドリストを適用する必要があるという印象を受けました。

これを次のように変更すると:

{ exec >/dev/null; } 2>/dev/null; echo "Hi"

期待どおりの出力はありません。将来のコマンドでも、ファイル記述子1は/ dev / nullをポイントしたままです。これは再実行によって示されます:

{ exec >/dev/null; } >/dev/null; echo "Hi"

出力されません。

私は台本を作ってそれを追跡しようとしましたが、ここで何が起こっているのか正確にはわかりません。

このスクリプトの各ポイントで、STDOUTファイル記述子はどうなりますか?

編集:私のstrace出力の追加:

read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
close(10)                               = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90)        = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3)                     = 3

変だね; を再現できませんclose(10)。straceを実行したスクリプトの内容全体を投稿することもできますか?
DepressedDaniel

:@DepressedDanielはここで完全なスクリプトとstraceのあるスクリプト straceの
ジョーイPabalinas

;}に迷いがあり、結局> /dev/null、複合リスト{}に適用しないという意味が変わります。
DepressedDaniel

@DepressedDanielああ、あなたは完全に正しいです!今、出力は私が期待するものです。ご回答ありがとうございます!
ジョーイパバリナス16

回答:


17

フォローしましょう

{ exec >/dev/null; } >/dev/null; echo "Hi"

ステップバイステップ。

  1. 次の2つのコマンドがあります。

    a。 { exec >/dev/null; } >/dev/null、 に続く

    b。 echo "Hi"

    シェルは最初にコマンド(a)を実行し、次にコマンド(b)を実行します。

  2. の実行 { exec >/dev/null; } >/dev/null次のよう収益:

    a。最初に、シェルはリダイレクト>/dev/null 実行し、コマンドの終了時にそれを元に戻すことを忘れないでください。

    b。次に、シェルが実行され{ exec >/dev/null; }ます。

    c。最後に、シェルは標準出力を以前の場所に戻します。(これは、と同じメカニズムですls -lR /usr/share/fonts >~/FontList.txt-リダイレクトは、それらが属するコマンドの期間のみ行われます。)

  3. 最初のコマンドが完了すると、シェルが実行されecho "Hi"ます。標準出力は、最初のコマンドの前の場所です。


2aが2bの前に実行される理由はいくつかありますか?(右から左)
ジョーイパバリナス

5
リダイレクト、適用するコマンドの前に実行する必要ありますか?それ以外の場合、どのように機能しますか?
AlexP 16

ああ、そのように考えたことはありません!最初の2つは、素晴らしい答えです。どちらかを決める前に少しだけ説明しますが、両方の説明に感謝します!
ジョーイパバリナス16

残念ながら、答えは1つしか選択できません。そのため、技術的な知識が少ないため、この1つを選択します。したがって、技術に詳しくないユーザーでも役立つと思います。しかし、@ DepressedDanielには、より詳細な説明を提供する同様に素晴らしい回答がありました。
ジョーイパバリナス16

14

サブシェルまたはサブプロセスを使用しないために、複合リストの出力{}がパイプされる>と、シェルは複合リストを実行する前にSTDOUT記述子を保存し、その後復元します。したがって、exec >複合リストでは、古い記述子がSTDOUTとして復元されるポイントを超えてその効果を持ちません。

の関連部分を見てみましょうstrace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n

   132  open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
   133  fcntl(1, F_GETFD)                       = 0
   134  fcntl(1, F_DUPFD, 10)                   = 10
   135  fcntl(1, F_GETFD)                       = 0
   136  fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
   137  dup2(3, 1)                              = 1
   138  close(3)                                = 0
   139  open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
   140  fcntl(1, F_GETFD)                       = 0
   141  fcntl(1, F_DUPFD, 10)                   = 11
   142  fcntl(1, F_GETFD)                       = 0
   143  fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
   144  dup2(3, 1)                              = 1
   145  close(3)                                = 0
   146  close(11)                               = 0
   147  dup2(10, 1)                             = 1
   148  fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
   149  close(10)                               = 0

134行目で、少なくともインデックス付きの記述子1STDOUT)がどのようにコピーされるかを見ることができます10F_DUPFDそれは、その記述子に複製した後、指定された番号から始まる使用可能な最小の記述子を返します)。また、行137でopen("/dev/null")(descriptor 3)の結果が記述子1STDOUT)にコピーされる方法も参照してください。最後に、行147で、STDOUT記述子に保存された古いもの10が記述子にコピーされます1STDOUT)。最終的な効果はSTDOUT、オンラインへの変更を絶縁することです144(これは、innerに対応しますexec >/dev/null)。


FD 1は137行目でFD 3によって上書きされるため、なぜ141行目は10が/ dev / nullを指していないのですか?
ジョーイパバリナス16

@JoeyPabalinas行141は、FD 1(つまり、stdout)を10の後の次の利用可能な記述子に複製しています。これは、そのシステムコールからの戻り値でわかるように、11になります。10はbashにハードコードされているため、bashの記述子の保存は、でスクリプトで操作する可能性のある1桁の記述子と干渉しませんexec
DepressedDaniel

したがって、fcntl(1、F_DUPFD、10)は、FD 1が現在どこを指していても常にSTDOUTを参照しますか?
ジョーイパバリナス16

@JoeyPabalinasあなたの質問が何かわかりません。FD 1は、IS STDOUT。それらは同じものです。
DepressedDaniel

元の投稿に完全なstrace出力を追加しました。
ジョーイパバリナス16

8

{ exec >/dev/null; } >/dev/null; echo "Hi"とは、{ exec >/dev/null; }; echo "Hi"二重リダイレクトがないことであるdup2(10, 1);オリジナルのコピーであるFD 10を閉じる前に、stdout(次のコマンドを実行する前に、echo)。

外部リダイレクトが実際に内部リダイレクトをオーバーレイしているため、このようになります。そのため、元のstdoutfdが完了するとコピーされます。


簡単な方法で違いを説明するための+1。AlexPの答えには、この説明がありません。
カミルマシオロウスキ16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.