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
フォローしましょう
{ exec >/dev/null; } >/dev/null; echo "Hi"
一歩一歩。
2つのコマンドがあります。
a。 { exec >/dev/null; } >/dev/null
、続いて
b。 echo "Hi"
シェルは最初にコマンド(a)を実行し、次にコマンド(b)を実行します。
{ exec >/dev/null; } >/dev/null
の実行は次のように進行します。
a。最初に、シェルはリダイレクト>/dev/null
を実行し、コマンドが終了したときにそれを元に戻すことを忘れないでください。
b。次に、シェルは{ exec >/dev/null; }
を実行します。
c。最後に、シェルは標準出力を元の場所に切り替えます。 (これはls -lR /usr/share/fonts >~/FontList.txt
と同じメカニズムです-リダイレクトは、それらが属するコマンドの間だけ行われます。)
最初のコマンドが完了すると、シェルはecho "Hi"
を実行します。標準出力は、最初のコマンドの前の場所です。
サブシェルまたはサブプロセスを使用しないために、複合リスト_{}
_がパイプライン_>
_である場合、シェルは複合リストを実行する前に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
_(STDOUT
)が、少なくとも_10
_のインデックスを持つ別の記述子にどのようにコピーされるかを確認できます(これは_F_DUPFD
_が行うことです)。その記述子に複製した後の指定された番号)。また、137行目で、open("/dev/null")
(記述子_3
_)の結果が記述子_1
_(STDOUT
)にコピーされる方法も確認してください。最後に、_147
_行で、記述子_10
_に保存された古いSTDOUT
が記述子_1
_(STDOUT
)にコピーされます。最終的な効果は、_144
_行のSTDOUT
への変更を絶縁することです(これは、内部の_exec >/dev/null
_に対応します)。
_{ exec >/dev/null; } >/dev/null; echo "Hi"
_と_{ exec >/dev/null; }; echo "Hi"
_の違いは、次のコマンドを実行する前に、元のstdout
のコピーであるfd 10を閉じる前に、二重リダイレクトがdup2(10, 1);
を実行することです。 (echo
)。
これは、外部リダイレクトが実際に内部リダイレクトをオーバーレイしているためです。それが完了すると、元のstdout
fdをコピーして戻す理由です。