標準入力全体に標準入力の最後の行を追加します。

標準入力全体に標準入力の最後の行を追加します。

次のスクリプトを考えてみましょう。

tmpfile=$(mktemp)

cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS

cat <(tail -1 "$tmpfile") "$tmpfile"

これは働き、出力されます:

line 3
line 1
line 2
line 3

入力ソースが実際のファイルではなく標準入力であるとします。

cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS

コマンドを修正するには:

cat <(tail -1 "$tmpfile") "$tmpfile"

それでは、他の状況でも同じ出力が生成されますか?

メモ:私が使用している特定のHeredocとHeredoc自体の使用は単なる例です。許容される回答は次のとおりです。stdin経由で任意のデータを受信する

答え1

努力する:

awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'

はい

入力として変数を定義します。

$ input="line 1
> line 2
> line 3"

次のコマンドを実行してください。

$ echo "$input" | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 3
line 1
line 2
line 3

もちろんhere-docを使うこともできます。

$ cat <<EOS | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 1
line 2
line 3
EOS
line 3
line 1
line 2
line 3

どのように動作しますか?

  • x=x $0 ORS

    これにより、入力の各行が変数に追加されますx

    まったくORSそうではありません。出力レコード区切り記号。デフォルトでは改行文字です。

  • END{printf "%s", $0 ORS x}

    ファイル全体を読み取った後、最後の行が印刷され、$0その後にファイル全体の内容が印刷されますx

これは入力全体をメモリに読み込むため、例えばギガバイト)入力。

答え2

stdinが検索可能ファイルを指している場合(ここのbash(他のすべてのシェルではない)のドキュメントが一時ファイルとして実装されている場合)、内容全体を読み取る前に尾を取得してから戻ることができます。

探すzsh演算子はtcl / perl / pythonなどのシェルまたはスクリプト言語で使用できますが、使用する必要があるksh93場合はいつでもより高いレベルのインタプリタを呼び出すことができますbashbashbash

ksh93 -c 'tail -n1; cat <#((0))' <<...

または

zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...

stdinが検索できないファイル(パイプやソケットなど)を指している場合、これは機能しません。その場合、唯一のオプションは、入力全体を読み取って保存することです(メモリまたは一時ファイルに...)。

メモリ内ストレージのためのいくつかのソリューションが提供されました。

一時ファイルを使用するには、zsh次のようにします。

seq 10 | zsh -c '{ cat =(sed \$w/dev/fd/3); } 3>&1'

Linuxでここで説明されているように一時ファイルを使用するシェルを使用している場合は、bash実際にzshこの資料で生成された一時ファイルを使用して出力を保存できます。

seq 10 | {
  chmod u+w /dev/fd/3 # only needed in bash5+
  cat > /dev/fd/3
  tail -n1 /dev/fd/3
  cat <&3
} 3<<EOF
EOF

答え3

cat <<EOS | sed -ne '1{h;d;}' -e 'H;${G;p;}'
line 1
line 2
line 3
EOS

これを使用されるものに変換する際の問題は、ファイルの終わりを見つけるためにファイル全体を読む必要があることtailです。tailパイプラインでそれを使用するには、次のものが必要です。

  1. 文書の全内容を に提供してくださいtail
  2. それを提供する再び到着するcat
  3. そのような順序で。

トリッキーな部分は、文書の内容をコピーすること(そうすること)ではなく、tee中間一時ファイルを使用せずに文書の残りの部分が出力される前に出力を取得することです。tail

使用sed(またはawkJohn1024 本当に)データをメモリに保存することで、データの二重解析とソートの問題を排除します。

私が提案する解決策sed

  1. 1{h;d;}、最初の行を予約済みスペースにそのまま保存し、次の行にジャンプします。
  2. H、改行文字を含む予約済みスペースに行を追加します。
  3. ${G;p;}、新しい行を含む最後の行に予約済みスペースを追加し、結果データを印刷します。

これはJohn1024のソリューションを文字通り翻訳したものですが、sedPOSIX規格は少なくとも8192バイト(8KiB)の予約済みスペースしか保証しないことに注意してください。おすすめこのバッファは必要に応じて動的に割り当てられ、拡張されます。sedGNUとBSDの両方がsedこれを行います。


名前付きパイプの使用を許可する場合:

mkfifo mypipe
cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
line 1
line 2
line 3
EOS
rm -f mypipe

teeこれは、データを送信するのmypipeと同時にデータを送信するために使用されますcat。ユーティリティcatは最初に出力を読み取り、次にtail(読み取りmypipeからtee書き込みまで)出力から直接文書のコピーを追加しますtee

ただし、文書が次の場合、重大な欠陥があります。大きすぎる(パイプのバッファサイズより大きい) (名前が指定されていない) パイプがクリアされるのを待っている間、書き込みおよびブロックされますteemypipe読む前は消去されませんcatcat終わるまで読まないでくださいcattailそしてtailそれが終わるまでは終わらないでしょう。teeこれは典型的なデッドロック状態です。

多様性

tee >( tail -n 1 >mypipe ) | cat mypipe -

同じ問題があります。

答え4

順序に興味がない場合。これでこれが機能しますcat lines | tee >(tail -1)。他の人が言ったように。必要な順序で操作を実行するには、ファイルを2回読み取るか、ファイル全体をバッファリングする必要があります。

関連情報