名前付きパイプを一時ファイルとして使用するにはどうすればいいですか?

名前付きパイプを一時ファイルとして使用するにはどうすればいいですか?

私が使用している vim プラグインの 1 つは、このスクリプトを使用して、stdin からの読み取りをサポートしていないリンターに入力を渡します。

set -eu

# All of the following arguments are read as command to run.
file_extension="$1"
shift

temp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'ale_linter')
temp_file="$temp_dir/file$file_extension"
trap 'rm -r "$temp_dir"' EXIT

while read -r; do
  echo "$REPLY" >> "$temp_file"
done

"$@" "$temp_file"

最初はなぜそのようなものを使わないのかと少し戸惑いました

some input | some_program /dev/stdin

しかし、リンターとして試してみたところ、ghc/dev/stdin が実際のファイルではないというエラーを出力していることがわかりました (実際はそうではありません)。

そこで、一時ファイルの代わりに名前付きパイプを使用できるかどうか疑問に思います。一時ファイルへの書き込みに満足できない理由は、SSD の状態です。もっと良い方法があるなら、なぜそれをしないのでしょうか?

答え1

いいえ、それらのファイルを拒否するプログラムは、通常、ファイルがシーク可能(任意のオフセットでコンテンツにアクセスしたり、巻き戻し後に複数回アクセスしたりする必要があるなど)。または、ファイルを複数回開く必要がある場合もあります。また、ファイル (の一部) を書き換えたり、切り捨てたりする必要がある場合もあります。

いずれの場合も、名前のないものpipe( や|など) と名前のあるものでは違いはありません。/dev/stdin

実際、Linux では、 stdin がパイプ (名前付きかどうかに関係なく) の場合、名前付きパイプとまったく同じように動作し、プログラムはそれを実際の名前付きパイプと/dev/stdin区別できません。/dev/stdin

他のシステムでは、まったく同じではありませんが、実際には、/dev/stdin名前付きパイプを開くと、パイプへのファイル記述子が取得されますが、これはどちらの方法でもシークできません。

したがって、一時ファイルを作成する必要があります。シェルによってはzsh、これが簡単になるものもあります。 の場合は、次のようになります。

#! /bin/zsh -
"$@" =(cat)

Linux および で、ヒアドキュメント用に削除された一時ファイルを使用するシェル ( bash、およびzshの一部の実装などksh) では、次の操作を実行できます。

#! /bin/bash -
"$@" /dev/fd/3 3<< EOF
$(cat)
EOF

ただし、ファイルに NUL 文字が含まれていたり、空行で終わっていたりすると、ファイルの内容が壊れる可能性があります。

バージョン 5 以降、bash は here doc 一時ファイルを読み取り専用にするため、アプリケーションがそのファイルを変更する必要がある場合は、次のコマンドで書き込み権限を復元する必要があることに注意してください。

#! /bin/bash -
{
  chmod u+w /dev/fd/3 && # only needed in bash 5+
    "$@" /dev/fd/3
} 3<< EOF
$(cat)
EOF

while read質問があったので、そのループについてメモします。

read -r変数名のない最初のsh構文は有効ではありません。構文はISO 9899 で指定されているshのと同様に、POSIX (ISO 9945、IEEE Std 1003.1) で指定されています。C

その仕様では変数名の引数が必要であることに気づくでしょうread。省略した場合の動作は次のようになります。未指定実際にはshインタープリタの実装によって異なります。

bashは GNUshインタプリタであり、gccは GNU C コンパイラです。 と はどちらもbashgccこれらの標準で指定されている内容を超えた拡張機能を持っています。

の場合read、は であるかのようにbash扱います。POSIX仕様では、 は文字または入力の終わりに達するまでstdinを読み取り、読み取った文字を変数に格納し、read -rIFS= read -r REPLYIFS= read -r REPLY\n$REPLY成功改行文字が読み込まれた場合(完全な行)または失敗それ以外の場合 (改行の前の EOF など)、読み取られたデータに NUL 文字または有効な文字を形成しないバイト シーケンスが含まれている場合、動作は未定義のままになります。

の場合bash、有効な文字を形成しない場合でも読み取られたバイトを保存し、NUL 文字を削除します。

read -rはまたはread -r REPLYに似ており、またはベースの POSIX 系シェルではエラーを報告します。kshzshyashash

echoの引数にバックスラッシュ文字が含まれず、最初の引数が でない限り、の動作は未指定です-n

まとめると、shあなたが扱っている特定の実装(およびバージョン)を知らない限り、

while read -r; do
  echo "$REPLY" >> "$temp_file"
done

で十分です。具体的にはbash、データに NUL 文字が含まれず、改行文字で終了し、どの行も^-[neE]+$拡張正規表現に一致しない (および/または OS/X の のように環境やbashコンパイル方法によってshはバックスラッシュ文字が含まれない) 場合に限り、stdin を temp_file に保存します。

非常に非効率的で、シェルでテキストを処理する方法ではありません

ここで必要なのは:

cat > "$temp_file"

cat標準コマンド引数が与えられない場合は、stdin を stdout にダンプするだけです。そのまま

関連情報