stdoutとエラーをリダイレクトすると、>>なしで追加が機能します。

stdoutとエラーをリダイレクトすると、>>なしで追加が機能します。

ファイルがありますが、test.txt名前がtest。私が試したとき

ls test test.txt > new 2>new

使われなかったのでnew上書きすると予想しました。>>しかし、出力ファイルにこれら2つの内容を追加しました。なぜですか?

答え1

長い話を短く bash何でも書き込み前に関連するすべてのファイルを開いて切り取ります。印刷開始時にファイルが切り捨てられたため(2回)、両方とも転送されstdoutます。stderrnewbashls

これがbashI / Oリダイレクトの準備/処理方法です。コマンドを>ファイルにリダイレクトするように要求すると、デフォルトでファイルが開き、bash必要に応じて生成されます。ファイルがすでに存在する場合は切り捨てられます。あなたの場合、これはopenシステムコールといくつかのフラグを介して行われます。

open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)

O_CREATファイルがない場合は作成し、O_TRUNCあれば切り捨てます。このシステムコールはopenリダイレクト初期化の一部です。つまり、次のような複数のリダイレクト操作を使用する場合...bash

$ ls test test.txt > new 2>new

...bashすべての関連ファイルを開くことから始めます。したがって、実行する前に同じフラグで2回開きますlsnew

open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)

つまり、デフォルトでコマンドを実行すると、次のbash操作が順番に実行されます。

  1. 標準出力として開き、new必要に応じてファイルを作成/切り捨てます。
  2. 標準エラーで開き、new必要に応じてファイルを作成/切り捨てます。
  3. 実行ls:内容を作成しますnew

ご覧のとおり、関連bashするすべてのファイルが切り捨てられます。今後start を使用してls何かを実行するときを意味します。... >new 2>newnewそれから、出力はその位置にリダイレクトされます。必要な動作をするには、stdoutとstderrを独立してbashキャプチャしls、書き込み前に1つずつ開く必要があります。オリジナル:

  1. 開始ls
  2. 何が起きたらstdout開いて切ってnew使ってください。
  3. 何が起こったら、stderrもう一度開いてカットしてnew書いてください。

ただし、メッセージは互いに絡み合っている可能性があります。リダイレクトされたプログラムはに何かを書き、stdout他のものに書き込んstderrでから戻ってきますstdout。これらすべてを管理するのは恐ろしいでしょう。(これは望ましくない(定義されていませんか?)動作を引き起こす可能性があります...)

答え2

これら2つを追加しないでください。奇妙な結果が表示されます。

$ ls testasdasd qtsingleapp-homecu-bcbf-3e8 >new 2>new
$ cat new
qtsingleapp-homecu-bcbf-3e8
: No such file or directory

この 2 つがある場合は、次のようになります。

$ ls testasdasd qtsingleapp-homecu-bcbf-3e8 >new 2>&1
$ cat new
ls: cannot access testasdasd: No such file or directory
qtsingleapp-homecu-bcbf-3e8

それではstrace、何が起こっているのか見てみましょう。

$ strace -f -e trace=open,close,write,fcntl,dup2 sh -c 'ls testasdsad qtsingleapp-homecu-bcbf-3e8 > new 2>new'
open("/etc/ld.so.cache", O_RDONLY)      = 3
close(3)                                = 0
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
close(3)                                = 0
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_DUPFD, 10)                   = 10
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(2, F_DUPFD, 10)                   = 11
close(2)                                = 0
fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 2)                              = 2
close(3)                                = 0
Process 7523 attached
....
[pid  7523] write(2, "ls: ", 4)         = 4
[pid  7523] write(2, "cannot access testasdsad", 24) = 24
[pid  7523] open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid  7523] open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid  7523] write(2, ": No such file or directory", 27) = 27
[pid  7523] write(2, "\n", 1)           = 1
[pid  7523] write(1, "qtsingleapp-homecu-bcbf-3e8\n", 28) = 28
[pid  7523] close(1)                    = 0
[pid  7523] close(2)                    = 0
Process 7523 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
dup2(10, 1)                             = 1
close(10)                               = 0
dup2(11, 2)                             = 2
close(11)                               = 0

の順序を見ると、次のようになりますopenfcntldup2

  • まず、ファイルがnew開き、ファイル記述子が割り当てられます。3
  • その後、ファイル記述子がファイル1記述子にコピーされます。10
  • その後、ファイルディスクリプタ3(例:file)がファイルディスクリプタ(例:今)newにコピーされます。110

> new上記の内容はすべて注文に記載されています。その後、同じ順序が発生しますが、ファイル記述子の場合はコマンドに引用されます22>new

その後、2つのファイル記述子があり、10どちらも11新しいファイルを指し、これらの記述子はstderrとなりstdoutますls。実行時と同じファイルを指してls上書きするため、出力の一部が切り捨てられます。 stderrstdoutnewstdoutstderr

関連情報