Shell

popen 和 JS ffi 的管道損壞錯誤

  • March 28, 2022

我正在為 nodejs 使用ffi,這在很大程度上與這個問題無關,這實際上是關於更好地理解管道,但確實提供了一些上下文。

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 個隨機數。這讓我很困惑,但是在一些瘋狂的Google搜尋中,我發現這個堆棧答案非常適合我的情況。

不過,我還有一些問題。

為什麼:

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

使用我的 exec 命令呼叫時拋出一個損壞的管道錯誤,但從 shell 呼叫時卻沒有?我不明白為什麼當我打電話時:

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

它無休止地閱讀,但是當我將它傳送到:

head -c ${1-8}

它可以正常工作而不會引發損壞的管道錯誤。似乎這head將需要它需要的東西,並且tr會永遠閱讀。至少它應該扔破管子;head會消耗前 8 個字節,然後tr仍然會輸出輸出,並且tr由於head已停止執行,因此會拋出損壞的管道。

這兩種情況對我來說都很有意義,但似乎它們是彼此獨有的。我不明白呼叫之間有什麼不同:

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')

不會引發管道錯誤!

通常,tr不應該能夠寫入該錯誤消息,因為在head.

您收到該錯誤消息是因為不知何故,正在執行的程序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.

如果你做 a strace -fe write,你會看到類似的東西:

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

系統write呼叫失敗並出現 EPIPE 錯誤,而不是觸發 SIGPIPE。

無論如何tr都會退出。忽略 SIGPIPE 時,由於該錯誤(但這也會觸發錯誤消息)。如果沒有,它會在收到 SIGPIPE 後退出。您確實希望它退出,因為您不希望它/dev/urandom在這 8 個字節readhead.

為避免該錯誤消息,您可以使用以下命令恢復 SIGPIPE 的預設處理程序:

trap - PIPE

打電話之前tr

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

引用自:https://unix.stackexchange.com/questions/77239