Bash

如何逐行讀取命名管道並退出?

  • April 1, 2021

我有以下 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"同時執行這兩個部分的東西。

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