名前付きパイプで1行ずつ読み込んで終了する方法は?

名前付きパイプで1行ずつ読み込んで終了する方法は?

難読化されたファイルオープナーとして使用したい次の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が表示されます(0バイトの読み取りから返されます)。しかし、私の考えでは、<>パイプは読み取り/書き込みモードで開かれているので、パイプには常に作成者があり、シェルはファイルハンドルを開いたままにしておくことです。

私はシェルでパイプを開くことをスキップして、次のことができるはずだと思います。

alacritty -e sh -c "fzf -m >$FIFO" && open < "$FIFO"

または、より適切には次のようになります。

alacritty -e sh -c 'fzf -m >"$1"' sh "$FIFO" && open < "$FIFO"

ここでは、質問の構成が異なる動作をしていると仮定します。敏捷性がバックグラウンドで端末を作成し、すぐに終了する場合は、そうする必要があります。そうでない場合は、alacritty -e sh ... & open < "$FIFO"2つの部分を同時に実行する必要があります。

答え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どちらの場合も、リーダーコマンドが接続されているパイプの書き込み端をコピーした新しいファイルディスクリプタに出力がリダイレクトされることが重要です。

関連情報