「ステップ2」スクリプトは、ファイルまたは標準入力からの入力読み取りをどのようにサポートしますか?

「ステップ2」スクリプトは、ファイルまたは標準入力からの入力読み取りをどのようにサポートしますか?

以下は、「ダブルスクリプティング」が意味する非常に簡単な例です。

#!/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ます。perlpython

ただし、特定のユースケースでは、次のようにすることもできます。

#! /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ます。書き込みが完了するまで出力が開始されないようにするために使用されます(実行時にパイプを開いたままにするため)。teegrepgrep -v | sort4>&1sortgrepgrep -v

答え2

sortソートしたい出力部分のみが適用されます。grep -E '^#' "$INPUTFILE";(grep -E -v '^#' "$INPUTFILE" | sort )

関連情報