為什麼在 cygwin 中重定向到管道時 /dev/stderr 無效?

為什麼在 cygwin 中重定向到管道時 /dev/stderr 無效?

/dev/stderr我最近偶然發現了一個令人驚訝的問題,該問題在最新的 cygwin 上無效,該問題也存在於成熟的 Debian 安裝中。 (編輯:與我最初的想法相反,我的 Debian 系統不會暴露此錯誤,而只是產生所需的輸出。我現在必須假設這是一個 cygwin 錯誤。)

背景:我使用的工具可以產生數千行輸出(特別是:大型生產系統上的版本控制系統)。我正在透過腳本控制運行它們,並且希望可以選擇將嘈雜的工具輸出重定向到日誌檔案。一個簡單的解決方案似乎總是將它們的(stderr 和 stdout)輸出重定向到儲存在環境變數中的檔案系統目標。如果需要輸出到終端(或某些使用者控制的目的地),則目的地DBG_STDERR將只是“/dev/stderr”,否則是一些臨時檔案名稱。典型的工具執行線將類似於noisy_command >> "$DBG_STDERR" 2>&1

除非我透過管道傳輸腳本的輸出,否則這工作正常。這是一個最小的再現:

$ uname -a
CYGWIN_NT-6.1-WOW xxxxxxx 2.8.1(0.312/5/3) 2017-07-03 14:06 i686 Cygwin

$ bash --version
GNU bash, version 4.4.12(3)-release (i686-pc-cygwin)

$ cat say-something.sh
#!/bin/sh
echo something > /dev/stderr

$ (x=$(./say-something.sh 2> /dev/stderr)) 2>&1 |cat
./say-something.sh: line 2: /dev/stderr: No such file or directory

$ (x=$(./say-something.sh 2> /dev/stderr)) 2>&1
something

$ (x=$(./say-something.sh 2> /dev/stderr))  |cat
something

$ x=$(./say-something.sh 2> /dev/stderr) 2>&1 |cat
something

當然,所有的重定向和嵌套 shell 脫離上下文看起來都很有趣。額外的 shell 是必要的,因為 say-something.sh 實際上會被另一個腳本呼叫。 fd 2 到 stderr 的冗餘重定向是方便可選重定向到檔案的「開關」(/dev/stderr,或不同的路徑,實際上是變數的可設定內容)。

似乎這條管道的所有組成部分都是必要的,正如失敗範例後的實驗所顯示的那樣:它們都成功了。

  • 我們需要標準輸出的最後一個管道
  • 我們需要呼叫者將 stderr 複製到 stdout
  • 我們需要命令替換的外殼。

答案1

/dev/stderr當重定向到管道時,該名稱實際上是有效的。可能不太可能的是/dev/stderr直接打開最終的目標。就看:

$ (echo Testing testing > /dev/stderr) |& cat
Testing testing

|由or創建的管道|&通常是匿名管道;顯示的名稱與檔案系統中的物件不對應。作為範例,您可以嘗試一些簡單的操作,例如:

$ ls -la /dev/fd/ |& cat
total 0
dr-x------ 2 alexp alexp  0 Jul  6 18:23 .
dr-xr-xr-x 9 alexp alexp  0 Jul  6 18:23 ..
lrwx------ 1 alexp alexp 64 Jul  6 18:23 0 -> /dev/pts/4
l-wx------ 1 alexp alexp 64 Jul  6 18:23 1 -> pipe:[1058859]
l-wx------ 1 alexp alexp 64 Jul  6 18:23 2 -> pipe:[1058859]
lr-x------ 1 alexp alexp 64 Jul  6 18:23 3 -> /proc/4335/fd

嘗試打開 的(最終)目標是非常不尋常的/dev/stderr;名稱/dev/stderr按順序提供避免費心找出實際目標。

答案2

我認為問題在於產生的讀取進程。它有自己的重定向管道,當進程停止時該管道會關閉(您獲得的 pid 不是 shell 的 pid,而是 readlink 進程的 pid)。當進程退出時,管道將變得無效。嘗試使用 fifos/命名管道。

相關內容