
私が使用している 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 コンパイラです。 と はどちらもbash
、gcc
これらの標準で指定されている内容を超えた拡張機能を持っています。
の場合read
、は であるかのようにbash
扱います。POSIX仕様では、 は文字または入力の終わりに達するまでstdinを読み取り、読み取った文字を変数に格納し、read -r
IFS= read -r REPLY
IFS= read -r REPLY
\n
$REPLY
成功改行文字が読み込まれた場合(完全な行)または失敗それ以外の場合 (改行の前の EOF など)、読み取られたデータに NUL 文字または有効な文字を形成しないバイト シーケンスが含まれている場合、動作は未定義のままになります。
の場合bash
、有効な文字を形成しない場合でも読み取られたバイトを保存し、NUL 文字を削除します。
read -r
はまたはread -r REPLY
に似ており、またはベースの POSIX 系シェルではエラーを報告します。ksh
zsh
yash
ash
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 にダンプするだけです。そのまま。