リダイレクトの使用方法を学んでいます。一般的な作業は次のとおりです。
command > file 2>&1
APUE 3.10と3.12を参照すると、コアシステムコールの順序は次のとおりです。
open(file) == 3
dup2(3,1)
dup2(1,2)
私のアイデアをテストするためにシェルスクリプトを生成し、straceコマンドを使用して実行しました。私のtest.shは次のようになります。
#!/bin/sh
echo 'hello'
whatfuckis # For get a stderr
次に、straceコマンドを使用してシステムコールとシグナルを追跡します。
strace -y -o trace.log ./test.sh > tmp.txt 2>&1
tmp.txtは期待どおりにエラーメッセージを受け取ります。 Trace.logには、読み取り、書き込み、閉じる、fcntl..などの多くのシステムコールが表示され、ファイル記述子2(stderr)がtmp.txtを指す結果も表示されます。
問題は、dup2などのファイル記述子のコピーに関する特定の操作が表示されないことです。これは奇妙です。ファイル記述子の重複を追跡する方法は? Trace.logの主な情報は次のとおりです。
read(10</home/madhouse/Applications/Lab/test.sh>, "#!/bin/sh\n\necho 'hello'\nwhatfuck"..., 8192) = 54
write(1</home/madhouse/Applications/Lab/tmp.txt>, "hello\n", 6) = 6
stat("/home/MATLAB/R2016b/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/home/madhouse/Qt5.14.2/5.14.2/gcc_64//whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/home/madhouse/Qt5.14.2/Tools/QtCreator/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/local/lib/nodejs/node-v12.22.5-linux-x64/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/home/madhouse/.local/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/local/sbin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/sbin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/games/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/usr/local/games/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
stat("/snap/bin/whatfuckis", 0x7ffc61f2a380) = -1 ENOENT (No such file or directory)
write(2</home/madhouse/Applications/Lab/tmp.txt>, "./test.sh: 4: ", 14) = 14
write(2</home/madhouse/Applications/Lab/tmp.txt>, "whatfuckis: not found", 21) = 21
write(2</home/madhouse/Applications/Lab/tmp.txt>, "\n", 1) = 1
read(10</home/madhouse/Applications/Lab/test.sh>, "", 8192) = 0
exit_group(127) = ?
+++ exited with 127 +++
アビロのログ
167936 execve("/usr/bin/sh", ["sh", "-c", "./test.sh > tmp.txt 2>&1"], 0x7ffc000ac898 /* 76 vars */) = 0
167936 dup2(3</home/madhouse/Applications/Lab/tmp.txt>, 1) = 1</home/madhouse/Applications/Lab/tmp.txt>
167936 close(3</home/madhouse/Applications/Lab/tmp.txt>) = 0
167936 dup2(1</home/madhouse/Applications/Lab/tmp.txt>, 2) = 2</home/madhouse/Applications/Lab/tmp.txt>
167936 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f67e121d850) = 167937
167937 execve("./test.sh", ["./test.sh"], 0x55855ac8c2d8 /* 76 vars */) = 0
167937 write(1</home/madhouse/Applications/Lab/tmp.txt>, "hello\n", 6) = 6
167937 write(2</home/madhouse/Applications/Lab/tmp.txt>, "./test.sh: 4: ", 14) = 14
167937 write(2</home/madhouse/Applications/Lab/tmp.txt>, "whatfuckis: not found", 21) = 21
167937 write(2</home/madhouse/Applications/Lab/tmp.txt>, "\n", 1) = 1
167936 dup2(10</dev/pts/2>, 1</home/madhouse/Applications/Lab/tmp.txt>) = 1</dev/pts/2>
167936 dup2(11</dev/pts/2>, 2</home/madhouse/Applications/Lab/tmp.txt>) = 2</dev/pts/2>
答え1
2つの質問があります。
- コマンドはリダイレクトを実行しているため、
strace
対話型シェルでリダイレクトが実行され、関連するstrace
システムコールは表示されません。 - システム
dup2
コールは、コマンドが実行される前に子プロセス内で実行されます。デフォルトでは、strace
コマンドのサブアイテムは従わないため、サブアイテム自体のトレースは表示されません。タグがサブアイテムにも従うようにするには、-f
マークアップにタグを追加する必要があります。strace
最も重要なことは、予想される検査を実行するには、次のことを実行する必要があることです。
strace -yf -o trace.log sh -c './test.sh > tmp.txt 2>&1'
これにより、予想される順序が表示されます。
$ grep -E 'dup2\(|clone\(|execve\(|(open|write|close)\(.*tmp.txt' trace.log
31769 execve("/usr/bin/sh", ["sh", "-c", "./test.sh > tmp.txt 2>&1"], [/* 124 vars */]) = 0
31769 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ffff7fd19d0) = 31770
31770 open("tmp.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3</tmp/tmp.txt>
31770 dup2(3</tmp/tmp.txt>, 1</dev/pts/445>) = 1</tmp/tmp.txt>
31770 close(3</tmp/tmp.txt>) = 0
31770 dup2(1</tmp/tmp.txt>, 2</dev/pts/445>) = 2</tmp/tmp.txt>
31770 execve("./test.sh", ["./test.sh"], [/* 123 vars */]) = 0
31770 dup2(3</tmp/test.sh>, 255) = 255</tmp/test.sh>
31770 write(1</tmp/tmp.txt>, "hello\n", 6) = 6
31770 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ffff7fd19d0) = 31771
31771 write(2</tmp/tmp.txt>, "./test.sh: line 3: whatfuckis: c"..., 49) = 49
システムコールは、実際の実行前にopen
コマンドのサブコマンドpiddup2
内で実行されます。31770
sh -c...
test.sh
子の代わりに親のファイル記述子をコピーします。
(シェル実装に応じて)実行が可能ですclone
。この場合、親はソースを保存する必要があります。標準出力そして標準エラーエラー率ファイル記述子を削除し、子プロセスが終了した後に復元します。
$ grep -E 'dup2\(|clone\(|execve\(|(open|write|close|openat)\(.*tmp.txt|F_DUPFD|exit_group' trace.log
1094 execve("/usr/bin/sh", ["sh", "-c", "./test.sh > tmp.txt 2>&1"], 0x7fffd0324118 /* 20 vars */) = 0
1094 openat(AT_FDCWD, "tmp.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3</tmp/tmp.txt>
1094 fcntl(1</dev/pts/0>, F_DUPFD, 10) = 10</dev/pts/0>
1094 dup2(3</tmp/tmp.txt>, 1) = 1</tmp/tmp.txt>
1094 close(3</tmp/tmp.txt>) = 0
1094 fcntl(2</dev/pts/0>, F_DUPFD, 10) = 11</dev/pts/0>
1094 dup2(1</tmp/tmp.txt>, 2) = 2</tmp/tmp.txt>
1094 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f844aa71690) = 1095
1095 execve("./test.sh", ["./test.sh"], 0x7f844aa9dc08 /* 20 vars */) = 0
1095 fcntl(3</tmp/test.sh>, F_DUPFD, 10) = 10</tmp/test.sh>
1095 write(1</tmp/tmp.txt>, "hello\n", 6) = 6
1095 write(2</tmp/tmp.txt>, "./test.sh: 3: ", 14) = 14
1095 write(2</tmp/tmp.txt>, "whatfuckis: not found", 21) = 21
1095 write(2</tmp/tmp.txt>, "\n", 1) = 1
1095 exit_group(127) = ?
1094 dup2(10</dev/pts/0>, 1</tmp/tmp.txt>) = 1</dev/pts/0>
1094 dup2(11</dev/pts/0>, 2</tmp/tmp.txt>) = 2</dev/pts/0>
1094 exit_group(127)
dup2
各実行を開始する前に、1094
親プロセス(pid)がファイル記述子1と2をファイル記述子10と11にそれぞれコピーする方法(fcntl
システムコールとF_DUPFD
コマンドを使用)を確認してください。
その後、子プロセス(pid 1095
)が完了すると、親プロセスはファイル記述子10と11(元のファイル記述子)を復元します。標準出力そして標準エラーエラー率) ファイル記述子 1 と 2 に戻ります。