以下は、「ダブルスクリプティング」が意味する非常に簡単な例です。
#!/bin/bash
INPUTFILE=$1
grep '^#' "$INPUTFILE"
grep -v '^#' "$INPUTFILE" | sort
スクリプト(と呼びます)はtwopass.sh
ファイルパスを唯一の引数として使用します。次に、最初に始まるすべての行を元の順序で印刷しますINPUTFILE
。第二に、印刷されます。INPUTFILE
#
ソート順内部のすべての行が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
このスクリプトを変更するにはどうすればよいですか?返品stdin
についても同じことをしますか?
つまり、必要な新しいバージョンのスクリプトを使用すると、次の行は上記のものと同じ出力を生成する必要があります。
./twopass.sh < example.txt
私はこの質問に対する答えに非常にbash
興味がありますzsh
。
答え1
通常、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
出力ファイル名をスキップするのはGNUgrep
拡張なので、grep
サポートしていない場合はその拡張子に置き換えることができますcat -- "$@" | grep ...
。)
bash
一時ファイルを検索または生成する機能はサポートされていませんが、または/を呼び出すことがzsh
できksh93
ます。perl
python
ただし、特定のユースケースでは、次のようにすることもできます。
#! /bin/sh -
gawk -e '
/^#/ {print; next}
{print | "sort"}' -E /dev/null "$@"
-e
+このトリックを-E
使用するには、文字を含むファイル名を処理できる必要があります=
(-
引数はまだgawk
名前付きファイルではなくstdinを意味すると解釈されます-
)。
上に並べられた出力表示の保証後ろにsort
読むべきコメントみんな出力を開始する前に入力してください。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
grep
grep -v | sort
4>&1
sort
grep
grep -v
答え2
sort
ソートしたい出力部分のみが適用されます。grep -E '^#' "$INPUTFILE";(grep -E -v '^#' "$INPUTFILE" | sort )