"{ exec >/dev/null; } >/dev/null" コードで正確に何が起こっていますか?

"{ exec >/dev/null; } >/dev/null" コードで正確に何が起こっていますか?

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

答え1

私たちに従ってください。

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

段階的に。

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

    ㅏ。{ exec >/dev/null; } >/dev/null、続いて

    雨。echo "Hi"

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

  2. 執行{ exec >/dev/null; } >/dev/null収益は次のとおりです。

    ㅏ。まず、シェルがリダイレクトを実行します。>/dev/null コマンドが終わったら元に戻すことを忘れないでください。

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

    氏。最後に、シェルは標準出力を元の位置に戻します。 (これは、ls -lR /usr/share/fonts >~/FontList.txt自分が属するコマンド期間中にのみ発生するリダイレクトと同じメカニズムです。)

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

答え2

サブシェルまたはサブプロセスの使用を避けるために、複合リストの出力がパイプされると、{}シェル>は複合リストを実行する前に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行目で、少なくともインデックスを持つ別の記述子に記述子1()をコピーする方法を見ることができます(これが実行される操作です。その記述子にコピーした後、指定された数値記述子から始まり、使用可能な最低記述子を返します)。 (説明者)の結果を記述子()にコピーする方法については、137行を参照してください。最後に、行では、記述子に保存された以前の内容が記述子()にコピーされます。最終的な効果は、ワイヤの変更(内部項目に対応する)を分離することです。STDOUT10F_DUPFDopen("/dev/null")31STDOUT147STDOUT101STDOUTSTDOUT144exec >/dev/null

答え3

{ exec >/dev/null; } >/dev/null; echo "Hi"との違いは、fd 10(元のファイルのコピー)を閉じる前と次のコマンド()を実行する前に二重{ exec >/dev/null; }; echo "Hi"リダイレクトが行われることです。dup2(10, 1);stdoutecho

これは、外部リダイレクトが実際に内部リダイレクトよりも優先されるために発生します。これstdoutが完了すると元のfdにコピーされます。

関連情報