以下は、「2パススクリプト」の意味する非常に単純な例です。
#!/bin/bash
INPUTFILE=$1
grep '^#' "$INPUTFILE"
grep -v '^#' "$INPUTFILE" | sort
このスクリプト(twopass.sh
と呼びます)は、ファイルへのパスINPUTFILE
を唯一の引数として受け取ります。次に、最初に#
で始まるINPUTFILE
のすべての行を元の順序で出力します。そして、次に、ソートされた順序でnotを実行するINPUTFILE
のすべての行を#
で開始して出力します。
たとえば、ファイルexample.txt
に次の行が含まれている場合
# foo comes first
# bar comes second
# baz comes third
wobble
quux
wibble
frobozz
...次に、twopass.sh
スクリプトを適用すると、次のようになります。
% ./twopass.sh example.txt
# foo comes first
# bar comes second
# baz comes third
frobozz
quux
wibble
wobble
alsoがstdin
に対して同じ操作を実行できるように、このスクリプトを変更するにはどうすればよいですか?
つまり、目的の新しいバージョンのスクリプトを使用すると、以下の行で上記と同じ出力が生成されます。
./twopass.sh < example.txt
bash
とzsh
の両方について、この質問への回答に興味があります。
一般的なケースでは、stdinを複数回処理できるようにするには、最初の読み取り後にシークして再度読み取ることができるようにする必要があります(パイプなどのすべてのタイプのファイルでは不可能です)。ソケット、ターミナル)、またはその入力を通常のファイルまたはメモリに保存し、複数回読み取ることができることがわかっている場合。
組み込みのシークとzshやksh93などの一時ファイル管理サポートを備えたシェルを使用すると、より簡単です。
_#! /bin/zsh -
zmodload zsh/system || exit
if (($#)); then
# arguments are provided. They are assumed to be file arguments
# to process (use ./- for the file called -)
grep -h -- '^#' "$@"
grep -vh -- '^#' "$@" | sort
else
# process stdin
if (( (pos = systell(0)) >= 0 )); then
# input is seekable
grep '^#'
sysseek $pos || {
syserror -p "Cannot go back: "
exit 1
}
grep -v '^#' | sort
else
# not seekable, store input in a temporary file using =(cat)
() {
grep -- '^#' $1
grep -v -- '^#' $1
} =(cat)
fi
fi
_
(ファイル名の出力をスキップする_-h
_はGNU grep
拡張子です。grep
がそれをサポートしていない場合は、 _cat -- "$@" | grep ...
_)を使用します。
bash
は一時ファイルのシークや作成をサポートしていませんが、zsh
、_ksh93
_またはPerl
/python
を呼び出すことができます。そのため。
ただし、特定のユースケースでは、次のようにすることもできます。
_#! /bin/sh -
gawk -e '
/^#/ {print; next}
{print | "sort"}' -E /dev/null "$@"
_
_-e
_文字を含むファイル名を処理するには、_-E
_ + _=
_トリックが必要でした(_-
_引数は、gawk
によって引き続き解釈されます) stdinを意味し、_-
_というファイルではありません)。
上記のソートされた出力は、コメントが表示されることが保証されています後sort
が読み取る必要があるコメントallそのコメント何かを出力する前に入力してください。 sort
は、メモリまたは一時ファイルにデータを保持します。
次のようなアプローチ:
_#! /bin/zsh -
{ cat -- "$@" > >(grep '^#' 4>&1 >&3) | grep -v '^#' | sort; } 3>&1
_
または、ksh93またはbashと互換性を持たせるには:
_{
cat -- "$@" |
{ tee >(grep '^#' 4>&1 >&3); } |
grep -v '^#' |
sort
} 3>&1
_
cat
の出力がtee
edでgrep
と_grep -v | sort
_の両方に出力される場合も機能します。 _4>&1
_は、sort
が書き込みを完了する前にgrep
が出力を開始しないことを保証するために使用されます(実行中に_grep -v
_へのパイプも開いているため)。