Bash
如何逐行讀取命名管道並退出?
我有以下 bash 腳本,我想將其用作模糊文件打開器。我創建了一個 fifo,生成了一個執行 fzf 的新終端,並將 fzf 的輸出重定向到 fifo。然後我呼叫一個從 fifo 讀取並打開文件的函式。
我的問題是函式內部的while循環
open
永遠不會結束。讀取完所有行後,如何關閉 fifo?#!/usr/bin/env bash FIFO=/tmp/fuzzy-opener [ -e "$FIFO" ] || mkfifo "$FIFO" exec 3<> "$FIFO" function open { while read file; do # open every $file based on its mime-type done <&3 echo 'done' # this is never reached } alacritty -e sh -c "fzf -m >&3" \ && open
我會建議幾個替代方案,因為:
- 根據您問題中的腳本,實際上似乎不需要 FIFO;
- 原則上,由於您使用的是 Bash,因此您可以利用 NUL 字元作為分隔符(POSIX 文件路徑中唯一不允許的字節);但不幸的是,
fzf
它似乎不適用於包含換行符的文件名;- 從文件中讀取
/tmp
可能會帶來一個重大的安全問題:如果其他人創建/tmp/fuzzy-opener
(作為正常文件),您的腳本會很高興地應用於open
其內容(儘管在某些系統上,以可寫文字的便簽打開一個不屬於自己的文件目錄使用exec 3<>
將引發錯誤)。您可以使用:
function open { while IFS= read -r -d '' file do echo "$file" # Replace with the actual open action done } alacritty -e sh -c 'fzf -m --disabled --print0 >&3' 3>&1 | open
可以通過移除
-d ''
和--print0
(考慮到上述限制,什麼都不會失去fzf
)來實現便攜;或者,使用數組來儲存選定的文件名:function open { for file do echo "$file" # Replace with the actual open action done } mapfile -t -d '' toopen < <(alacritty -e sh -c 'fzf -m --print0 >&3' 3>&1) && open "${toopen[@]}"
在這兩種情況下,要點是
fzf
’ 的輸出被重定向到一個新的文件描述符,該描述符複製了管道的寫入端,因此連接到讀取器命令。
read()
如果寫入端關閉,管道將顯示 EOF(從零字節讀取返回)。但我認為發生的情況是,由於<>
以讀寫模式打開管道,管道總是有一個作家,外殼保持文件句柄打開。我認為你應該能夠跳過在 shell 中打開管道,而是這樣做:
alacritty -e sh -c "fzf -m >$FIFO" && open < "$FIFO"
或者更準確地說:
alacritty -e sh -c 'fzf -m >"$1"' sh "$FIFO" && open < "$FIFO"
我在這裡假設問題中的構造以其他方式工作,如果 alacritty 在後台生成終端並立即退出,則應該這樣做。如果沒有,您需要
alacritty -e sh ... & open < "$FIFO"
同時執行這兩個部分的東西。