head と tail を使用してさまざまなラインセットをインポートして同じファイルに保存する

head と tail を使用してさまざまなラインセットをインポートして同じファイルに保存する

これは宿題ですが、具体的な宿題の質問はしません。

headとtailを使用してファイルから別の行セットをインポートする必要があります。行6-11と行19-24に似ており、どちらも別のファイルに保存します。私は追加を使用してこれを行うことができることを知っています。

head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1. 

しかし、私たちはそうしてはいけないと思います。
headコマンドとtailコマンドを組み合わせてファイルに保存する特別な方法はありますか?

答え1

同様の構造を使用してコマンドをグループ化する場合は、head別の基本操作を使用してこれを実行できます。{ ... ; }

{ head -n ...; head -n ...; ...; } < input_file > output_file

すべてのコマンドは同じ入力を共有します(ありがとう@mikeserv)。
6-11行と19-24行目を取得するには、

head -n 5 >/dev/null  # dump the first 5 lines to `/dev/null` then
head -n 6             # print the next 6 lines (i.e. from 6 to 11) then
head -n 7 >/dev/null  # dump the next 7 lines to `/dev/null` ( from 12 to 18)
head -n 6             # then print the next 6 lines (19 up to 24)

したがって、デフォルトでは次のように実行します。

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } < input_file > output_file

答え2

{ … }グループ化された構文を使用して、複合コマンドにリダイレクト演算子を適用できます。

{ head -n 11 file | tail -n 6; head -n 24 file | tail -n 6; } >file1

最初のM + N行をコピーして最後のN行だけを保持するのではなく、最初のM行をスキップして次のN行をコピーできます。これは大容量ファイルははるかに高速に処理されます。。パラメータはスキップするのではなく+Ntail1を加えたものです。つまり、印刷する最初の行の行番号で、行番号は1から始まります。

{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1

いずれにせよ、出力ファイルは一度だけ開かれますが、入力ファイルは各断片を抽出するために一度巡回されます。入力をグループ化する方法は?

{ tail -n +6 | head -n 6; tail -n +14 | head -n 6; } <file >file1

一般的に言えば、これはうまくいきません。 (少なくとも入力が通常のファイルの場合、一部のシステムでは機能する可能性があります。)なぜですか?なぜなら入力バッファ。ほとんどのプログラム(インクルードtail)は入力をバイト単位で読み取るのではなく、一度に数キロバイトずつ読み込みます。それが速いからです。したがって、tail数キロバイトを読み、最初に少しスキップし、もう少し渡してからhead停止します。ただし、読み取った内容は読み込まれ、次のコマンドには使用できません。

別の方法headパイプを使うことです/dev/null行をスキップします。

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } <file >file1

繰り返しますが、バッファリングのため動作は保証されません。head入力が通常のファイルから出ると、GNU coreutils(非組み込みLinuxシステムのコマンド)のコマンドと連携します。なぜなら、この実装がhead望むものを読んだらファイルの場所の設定出力のない最初のバイト。入力がパイプの場合は効果はありません。

ファイルから複数行のシーケンスを印刷するより簡単な方法は、より一般的なツールを呼び出すことです。sedまたはアッ。 (遅いかもしれませんが、非常に大きなファイルでのみ動作します。)

sed -n -e '6,11p' -e '19,24p' <file >file1
sed -e '1,5d' -e '12,18d' -e '24q' <file >file1
awk '6<=NR && NR<=11 || 19<=NR && NR<=24' <file >file1
awk 'NR==6, NR==11; NR==19, NR==24' <file >file1

答え3

headとtailを使用する必要があると言われましたが、ここで作業するには確かにsedがより簡単なツールです。

$ cat foo
a 1 1
a 2 1
b 1 1
a 3 1
c 3 1
c 3 1
$ sed -ne '2,4p;6p' foo
a 2 1
b 1 1
a 3 1
c 3 1

別のプロセスを使用して文字列にブロックを作成し、sedを介して実行することもできます。

$ a="2,4p;6p"
$ sed -ne $a foo
a 2 1
b 1 1
a 3 1
c 3 1

-n出力を否定し、pを使用して印刷する範囲を指定します。範囲の最初の数字と最後の数字はカンマで区切られます。

つまり、@don_crisstiが提案したコマンドのグループ化を実行したり、ファイルを数回繰り返すことで、毎回頭/尾部分の行の塊をつかむことができます。

$ head -4 foo | tail -3; head -6 foo | tail -1
a 2 1
b 1 1
a 3 1
c 3 1

ファイルに行やブロックが多いほど、sed は効率的です。

答え4

次のように bash 機能を使用します。

seq 1 30 > input.txt
f(){ head $1 input.txt | tail $2 >> output.txt ;}; f -11 -2; f -24 -3
cat output.txt
10
11
22
23
24

この場合は少し過剰ですが、フィルタが大きくなると役に立ちます。

関連情報