ファイル記述子のコピーなしでstdoutとstderrを同じファイルにリダイレクトするのは安全ですか?


27

空のディレクトリから始めます。

$ touch aFile
$ ls
aFile

次にls、2つの引数があり、そのうちの1つはこのディレクトリにありません。両方の出力ストリームをという名前のファイルにリダイレクトしますoutput。私が使う>>同時に書くことを避けるためします。

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

うまくいくようです。このアプローチには危険がありますか?


6
それは素早い投票でした。5秒ほどかかりました。私の質問の価値をこれほど迅速に評価できることを教えてください。さらに良いことに、それを改善するために何が悪いのでしょうか?
exit_status

ls aFile not_exist &>>outputここでもっと標準を使用してみませんか?(注、bashを使用していると仮定しています。)
FedonKadifeli

5
それは私が私が尋ねていることを理解するのに役立ちませんので。これらのストリームを移植可能な方法で同じファイルにリダイレクトする方法を知っています。私が知りたいのは、私が質問で提案したものに何か問題があるかどうかです。@FedonKadifeli
exit_status

1
@FedonKadifeli &>>は標準ではありません。これは非推奨のあいまいな構文であり、シェルごとに動作が異なります。どこから物を手に入れるのかしら。
ビリーおじさん

4
Bashは標準ではありません。POSIX標準では、ls &>>foo ...2つのコマンドls &ととして解析する必要があり>>foo ...、これが/bin/shUbuntuのような他のシェルが解析する方法です。非推奨になっているので、ここを見ることができます-私はそれがどんな種類の権威であるふりもしませんが。bashしかし、メンテナーにそれを使用することを良いアイデアと考えるかどうか尋ねることができます。
ビリーおじさん

回答:


22

いいえ、標準と同じくらい安全ではありません>>bar 2>&1

書いているとき

foo >>bar 2>>bar

を使用してbarファイルを2回開きO_APPEND、それぞれ独自の状態(ポインター、オープンモードなど)を持つ2つの完全に独立したファイルオブジェクト[1]を作成します。

これは2>&1dup(2)システムコールを呼び出しているだけでなく、stderrとstdoutを同じファイルオブジェクトに対して交換可能なエイリアスにします。

今、それには問題があります:

O_APPEND複数のプロセスが一度にファイルにデータを追加すると、NFSファイルシステム上のファイルが破損する可能性があります。これは、NFSがファイルへの追加をサポートしていないため、クライアントカーネルがそれをシミュレートする必要があるためです。これは競合状態なしでは実行できません。

あなたは、通常のようにファイルの確率に数えることができるbarfoo >>bar 2>&1はかなり低いという二つの別々の場所から同時にに書き込まれています。しかし、あなたによって>>bar 2>>barはそれを理由もなく何十桁も増やしました。

[1] POSIX専門用語の「ファイルの説明を開く」。


3
正式には、追加モードファイルの場合は安全です。引用されている問題は、NFSのバグであり、ファイルシステムとして不適切(POSIX非準拠)になっています。ただし、非追加モードの場合は、決して安全ではありません。
R ..

1
それは重要ではありません。OPの二重追加は(完全に無意味であることに加えて)使用するのが安全ではありません。そして、O_APPENDとにかくやりそこなうの一種である-を正しく実装するのはかなり面倒。
mosvy

NFSの競合状態は、異なるクライアント間でのみ発生すると考えています。クライアントOSは、プロセス間のすべての書き込みを調整する必要があります。
バーマー

クライアントOSがnfsファイルの独自のビューのみに関心がある場合に当てはまる@Barmar。ただし、で開いたnfsファイルに書き込む場合O_APPEND、クライアントは最初にサーバーからファイルの「実際の」サイズを取得し(iノードを「再検証」)、次にシーク+書き込み+キャッシュiノードの更新を行い、最後の部分のみがこれは、最初の部分がサーバーから古いサイズを取得し、ローカル/キャッシュiノードから正しいサイズをオーバーライドできることを意味します。と同じ問題lseek(SEEK_END)
mosvy

同じクライアント上の2つのストリーム間で競合状態がどのように発生するかはまだわかりません。両方のストリームは、同じローカルキャッシュiノードを参照する必要があります。
バーマー

22

あなたがするとき何が起こるか

some_command >>file 2>>file

それは file二回追加用に開かれます。これはPOSIXファイルシステムで安全に実行できます。データが標準出力ストリームまたは標準エラーストリームのどちらに送られるかに関係なく、ファイルが追加用に開かれたときにファイルに発生する書き込みは、ファイルの最後で発生します。

これは、基礎となるファイルシステムでのアトミック追加書き込み操作のサポートに依存しています。NFSなどの一部のファイルシステムは、アトミックアペンドをサポートしていません。たとえば、「UNIXではファイルはアトミックに追加されますか?」という質問を参照してください StackOverflowを。

を使用して

some_command >>file 2>&1

ただし、NFSでも動作します。

ただし、

some_command >file 2>file

シェルは出力ファイルを切り捨て(2回)、いずれかのストリームで発生した書き込みは、もう一方のストリームによって既に書き込まれたデータを上書きするため、安全ではありません。

例:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

hello文字列は、(終端改行で)最初に書き込まれ、その後、ストリングabcの改行に続くは上書き、標準誤差から書き込まれますhell。結果は、abc改行を含む文字列で、その後に最初のecho出力の左と改行が続きoます。

この文字列は最後に書き込まれ、文字列よりも長いechoため、2つの傷の周りのスワッピングはhello出力ファイルでのみ生成されabcます。リダイレクトが発生する順序は重要ではありません。

より慣用的なものを使用する方がより良く安全です

some_command >file 2>&1

1
それは現代のシェルには当てはまりますが、BourneシェルやThomsonシェル(どこ>>から来たのか)ではそうで>>はありませんでした。Solaris 10でも、/bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'出力しますb
ステファンシャゼラス

@StéphaneChazelasそれは、Solaris 10のの実装sh、またはそのファイルシステムの問題ですか?
クサラナンダ

1
それ>>はもともと何をしていたのか、O_APPENDで開くのではなく、最後までせずに開くことでした。それはそれほど問題ではありません、それはそれがやっていたことであり、行うために文書化されました。
ステファンシャゼル

0

何を達成したいかによります。出力と同じファイルにエラーがあるかどうかはあなた次第です。これは、シェルの機能を使用してファイルにテキストを保存するだけで、必要に応じてリダイレクトできます。絶対的なyesまたはnoはありません。Linuxのすべてをいくつかの方法で行うことができるので、これが私の方法ls notExistingFile existingFile >> output 2>&1 です。


ここで言っていること以上のものがあります。の>代わりに同じ練習をすると、>>一部の文字が上書きされます。シェルでリダイレクトできるのは、それだけではありません。なぜなら、でリダイレクトする>と、結果が異なるからです。でニュアンス>がありますが、>>何かありますか?
exit_status

はい、違います。私が言ったように、それはあなたの目標に依存します >-上書きします。>>-追加
エンジェル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.