我有一個呼叫函數的 bash 腳本。除此之外,該函數執行一個接收其輸出的管道。為了簡化它,這是一個人為的例子:
#!/bin/bash
func() {
ls "$@" | sort | rev > /tmp/output
}
func "$@"
然後你會做類似的事情來運行它。它會做它的事情並將其有效負載傳遞到文件。螢幕上沒有輸出。
$ ./myscript .
sort
現在,假設我想要標準輸出的輸出。我該怎麼做呢?
我可以這樣實現:
ls "$@" | sort | tee /dev/tty | rev > /tmp/output
但是,使用/dev/tty
是錯誤的,因為這是行不通的:
$ ./myscript > myfile
是否有更正確的方法來從函數內的管道內引用 bash 腳本的標準輸出?
(我在 Arch Linux 上使用 bash 4.3.0)
答案1
我能想到的最簡單的方法是:
ls "$@" | sort | tee >(rev > /tmp/output)
將向tee
STDOUT 發送一份副本,並且由於其後不再有 a |
,因此這是繼承的,這意味著如果沒有重定向,它將轉到 TTY,myfile
如果是,則將轉到您的 TTY。
另一個副本將發送到rev > /tmp/output
其 STDIN。在 bash 中,>(...)
運行括號之間的任何內容,然後將 替換>(...)
為連接到其 STDIN 的管道的路徑。
答案2
由於另一個答案對此並不清楚,因此另一種(另一種)方法是
exec 3>&1
ls | sort | tee /dev/fd/3 | rev > /tmp/output
這exec 3>&1
重複將檔案描述符 1 (stdout) 指定為檔案描述符tee /dev/fd/3
3 sort
。這應該可以在任何 shell 中工作,但可能取決於作業系統。一個應該獨立於作業系統但bash
特定於作業系統的變體是:
exec 3>&1
ls | sort | tee >( cat >&3 ) | rev > /tmp/output
在 中bash
,為您提供一個指向正在執行的進程的管道的檔案句柄>(command)
command
。在任何 (?) shell 中,運行command >&fd
command
其標準輸出重定向到指定的檔案描述符,該檔案描述符必須是先前建立的。
一個更複雜的版本,應該可以在任何 *nix (Posix) 系統上工作,是
pipe_to_stdout=$(mktemp -u)
mkfifo "$pipe_to_stdout"
cat "$pipe_to_stdout" &
ls | sort | tee "$pipe_to_stdout" | rev > /tmp/output
rm "$pipe_to_stdout"
這與第二個範例幾乎相同(cat
從 fifo 讀取並寫入 stdout),但在 shell 層級使用較少的煙霧和鏡子。
請注意,這些答案是靈活的,因為您ls
只需重新排列命令即可將 的輸出寫入標準輸出。