如何从命名管道中逐行读取并退出?

如何从命名管道中逐行读取并退出?

我有以下 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

答案1

read()如果写入端关闭,管道将显示 EOF(从零字节读取返回)。但我认为发生的情况是,既然<>以读写模式打开管道,管道总是有一个写入器,外壳保持文件句柄打开。

我认为您应该能够跳过在外壳中打开管道,而只需执行以下操作:

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"同时运行这两个部分。

答案2

我会建议几个替代方案,因为:

  • 根据您问题中的脚本, FIFO 似乎实际上不需要;
  • 原则上,由于您使用的是 Bash,因此您可以利用 NUL 字符作为分隔符(POSIX 文件路径中唯一不允许的字节);但不幸的是,它fzf似乎不适用于包含换行符的文件名;
  • 从文件中读取/tmp可能会造成重大安全问题:如果其他人创建了/tmp/fuzzy-opener(作为常规文件),您的脚本将很乐意应用于open其内容(尽管在某些系统上,在可字写的粘性文件中打开不拥有的文件)使用exec 3<>will 的目录提出错误)。

您可以使用:

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的输出被重定向到一个新的文件描述符,该文件描述符是通过复制管道的写入端而获得的,从而连接到读取器命令。

相关内容