popen と JS ffi でパイプ破損エラーが発生します。

popen と JS ffi でパイプ破損エラーが発生します。

フェイペイnodejsの場合、これはこの質問とはあまり関係がなく、実際にはパイプラインをよりよく理解するものですが、いくつかのコンテキストを提供します。

function exec(cmd) {
  var buffer = new Buffer(32);
  var result = '';
  var fp = libc.popen('( ' + cmd + ') 2>&1', 'r');
  var code;

  if (!fp) throw new Error('execSync error: '+cmd);

  while( !libc.feof(fp) ){
    libc.fgets(buffer, 32, fp)
    result += buffer.readCString();
  }
  code = libc.pclose(fp) >> 8;

  return {
    stdout: result,
    code: code
  };
}

このexec関数を使用して実行すると、このコードが表示されます。

 tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}

エラーが発生します。

write error: Broken pipe
tr: write error

しかし、予想した結果が出ました。つまり、8つの乱数です。これは私を混乱させましたが、狂気のインターネット検索中に私は見つけました。これスタックの答えは私の場合に完全に機能します。

しかし、まだいくつかの質問があります。

なぜ:

tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}

私のexecコマンドを呼び出すと壊れたパイプエラーが発生しますが、シェルから呼び出すとそうではありませんか?なぜ電話したのか理解できません。

tr -dc "[:alpha:]" < /dev/urandom

無限に読みますが、パイプで接続すると次のようになります。

head -c ${1-8}

壊れたパイプエラーが発生せず、うまく動作します。この内容を読むにはhead時間がかかり、tr永遠にかかるようです。少なくとも壊れたパイプは捨てなければなりません。head最初の8バイトが消費された後でも、出力は印刷され続け、壊れたtrパイプは実行が停止したため廃棄されます。trhead

どちらのシナリオも私にとっては意味がありますが、相互に排他的なようです。通貨の違いが何なのかわかりません。

exec(tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8})

そして

tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}

コマンドラインから直接、特に<無限のファイルが何かに移動してから|別のものに移動して、無限に実行されないのはなぜですか?私は何年もこれをやってきましたが、なぜこのようなことが起こったのか疑問に思ったことはありません。

最後に、この壊れたパイプエラーを無視できますか?回避策がありますか?私のC ++ ish JavaScriptコードに何か問題があるのでしょうか?私はいくつかの一般的な基本を見逃していますか?

- - - 編集する

いくつかのコードを乱す

exec('head -10 /dev/urandom | tr -dc "[:alpha:]" | head -c 8')

パイプラインエラーは発生しません!

答え1

一般的trhead

tr実行中のプロセスがSIGPIPEを無視するように設定されているため、対応するエラーメッセージが表示されます。私はこれがpopen()あなたの言語で実装することによって達成できると思います。

次の手順で再現できます。

sh -c 'trap "" PIPE; tr -dc "[:alpha:]" < /dev/urandom | head -c 8'

次の手順を実行して、何が起こっているかを確認できます。

strace -fe signal sh your-program

(またはLinuxを使用していない場合は、システムがそれに対応するもの)。これにより、次の内容が表示されます。

rt_sigaction(SIGPIPE, {SIG_IGN, ~[RTMIN RT_1], SA_RESTORER, 0x37cfc324f0}, NULL, 8) = 0

または

signal(SIGPIPE, SIG_IGN)

コマンドラインは同じプロセスまたはそのサブプロセスのいずれかで実行され、前のプロセスで/bin/sh開始して完了します。trhead

を実行すると、strace -fe write次の内容が表示されます。

write(1, "AJiYTlFFjjVIzkhCAhccuZddwcydwIIw"..., 4096) = -1 EPIPE (Broken pipe)

writeSIGPIPE をトリガーするのではなく、EPIPE エラーが原因でシステム呼び出しが失敗します。

とにかく終了しますtr。 SIGPIPEが無視された場合(ただしエラーメッセージもトリガーされます)、そうでない場合はSIGPIPEを受信して​​終了します。/dev/urandomこの8バイト以降はread読み続けたいと思わないので終了しますhead

このエラーメッセージを回避するには、次のコマンドを使用してSIGPIPEのデフォルトハンドラを復元できます。

trap - PIPE

電話する前にtr

popen("trap - PIPE; { tr ... | head -c 8; } 2>&1", ...)

答え2

これは私にとっても効果的です。エラーメッセージを表示したくない場合は、trを追加してください2>/dev/null

tr -dc "[:alpha:]" < /dev/urandom 2>/dev/null | head -c 8

関連情報