これは宿題ですが、具体的な宿題の質問はしません。
1つのファイルから異なる行セットを取得するには、headとtailを使用する必要があります。つまり、6行目から11行目と19行目から24行目を取り出し、両方を別のファイルに保存します。appendを使用してこれを行うことができることはわかっています。
head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.
しかし、そうする必要はないと思います。head
コマンドと tail コマンドを組み合わせてファイルに保存する具体的な方法はありますか?
答え1
次のような構文を使ってhead
コマンドをグループ化すれば、単独でも基本的な算術演算でも実行できます。{ ... ; }
{ head -n ...; head -n ...; ...; } < input_file > output_file
すべてのコマンドは同じ入力を共有します(マイクサーブ
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行を複製することができます。これは大きなファイルでは測定可能なほど高速化+N
の引数はtail
スキップする行数ではなく、それに 1 を加えた数、つまり 1 から番号が付けられた行で印刷する最初の行の番号であることに注意してください。
{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1
どちらの方法でも、出力ファイルは 1 回だけ開かれますが、入力ファイルは抽出するスニペットごとに 1 回走査されます。入力をグループ化するのはどうでしょうか?
{ 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またはawk(遅くなる可能性がありますが、これは非常に大きなファイルの場合にのみ問題になります。)
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/tail で行のチャンクを取得することもできます。
$ 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
この場合、これは少しやり過ぎですが、フィルターが大きくなればメリットになる可能性があります。