popen 和 JS ffi 的管道損壞錯誤
我正在為 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
解釋該命令行並啟動tr
和head
.如果你做 a
strace -fe write
,你會看到類似的東西:write(1, "AJiYTlFFjjVIzkhCAhccuZddwcydwIIw"..., 4096) = -1 EPIPE (Broken pipe)
系統
write
呼叫失敗並出現 EPIPE 錯誤,而不是觸發 SIGPIPE。無論如何
tr
都會退出。忽略 SIGPIPE 時,由於該錯誤(但這也會觸發錯誤消息)。如果沒有,它會在收到 SIGPIPE 後退出。您確實希望它退出,因為您不希望它/dev/urandom
在這 8 個字節read
被head
.為避免該錯誤消息,您可以使用以下命令恢復 SIGPIPE 的預設處理程序:
trap - PIPE
打電話之前
tr
:popen("trap - PIPE; { tr ... | head -c 8; } 2>&1", ...)