最初の行を除いて、ファイルから余分なヘッダー行を削除します


18

このおもちゃの例のようなファイルがあります。実際のファイルには400万行ありますが、そのうち約10行を削除する必要があります。

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
ID  Data1  Data2
4    100    100
ID  Data1  Data2
5    200    200

最初の行を除いて、ヘッダーのように見える行を削除します。

最終ファイル:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200

これどうやってするの?

回答:


26
header=$(head -n 1 input)
(printf "%s\n" "$header";
 grep -vFxe "$header" input
) > output
  1. 入力ファイルのヘッダー行を変数に取得します
  2. ヘッダーを印刷する
  3. ファイルを処理してgrep、ヘッダーに一致する行を省略します
  4. 上記の2つのステップからの出力を出力ファイルにキャプチャします

2
または多分{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
-iruvar

両方の良い追加。-n 1の賛成で、間接的にPOSIXは最近削除されたことを指摘-1構文を頭からためdon_crisstiのおかげ
ジェフ・シャラー

3
@JeffSchaller、最近 12年前と同様。そしてhead -1、その数十年前に廃止されました。
ステファンシャゼル

36

使用できます

sed '2,${/ID/d;}'

これにより、IDが2行目から始まる行が削除されます。


3
いいね または、パターンマッチングをより具体的にするためにsed '2,${/^ID Data1 Data2$/d;}' file(もちろん、列間に適切な数のスペースを使用します)
ジェフシャラー

セミコロンは1つのコマンドでしか省略できないと思っていましたが、大丈夫です。
bkmoney

正気sedではない、いいえ。
mikeserv

aaaand -iは、インプレース編集の勝利です。
user2066657

4
それともsed '1!{/ID/d;}'
ステファンChazelas

10

中括弧が気に入らない人向け

sed -e '1n' -e '/^ID/d'
  • npass行番号を意味します1
  • d で始まる一致した行をすべて削除します ^ID

5
これはsed '1n;/^ID/d'ファイル名に短縮することもできます。ちょうど提案
バレンティンバジラミ

これはIDfoo、ヘッダーとは異なる行も出力することに注意してください(この場合、違いはほとんどありませんが、あなたは決して知りません)。
テルドン

6

ここに楽しいものがあります。sed直接使用して、最初の行のすべてのコピーを削除し、他のすべてをそのままにしておくことができます(最初の行自体を含む)。

sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//' input

1{h;n;}最初の行を保留スペースに入れて印刷し、次の行を読み取りsedます。最初の行の残りのコマンドをスキップします。(2行目の最初の1テストスキップしますが、そのテストは2行目に適用されないので問題ありません。)

G 改行の後にホールドスペースの内容がパターンスペースに追加されます。

/^\(.*\)\n\1$/d改行の後の部分(つまり、ホールドスペースから追​​加されたもの)が改行の前の部分と完全に一致する場合、パターンスペースの内容を削除します(したがって、次の行にスキップします)。これは、ヘッダーを複製する行が削除される場所です。

s/\n.*$//Gコマンドによって追加されたテキストの部分を削除します。そのため、印刷されるのはファイルのテキスト行だけです。

ただし、正規表現は高価であるPため、改行より後の部分(つまり、ホールドスペースから追​​加されたもの)が部分と完全に一致しない場合、同じ条件(否定)を使用して改行までリントすることで、少し高速なアプローチが得られます改行の前に、無条件にパターンスペースを削除します。

sed '1{h;n;};G;/^\(.*\)\n\1$/!P;d' input

入力が与えられたときの出力:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200


@don_crissti、興味深い追加; ありがとう!私はおそらくより長いが同等のものを選ぶでしょうsed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input。どういうわけか私にとっては読みやすいです。:)
ワイルドカード


5

以下に、最初の行を事前に知る必要のない、さらに2つの選択肢を示します。

perl -ne 'print unless $_ eq $k; $k=$_ if $.==1; 

この-nフラグはperlに入力ファイルをループするよう指示し、各行をとして保存し$_ます。$k=$_ if $.==1;最初の行は、(セーブ$.ので、行番号である$.==1としてのみ第一ラインのために真となります)$kprint unless $k eq $_それは中に保存されたものと同じでない場合は、現在の行を出力します$k

または、同じものawk

awk '$0!=x;(NR==1){x=$0}' file 

ここでは、現在の行が変数に保存されているものと同じかどうかをテストしますx。テスト$0!=xがtrueと評価された場合(現在の行$0がと同じでない場合x)、true式でのawkのデフォルトのアクションは印刷であるため、行が印刷されます。最初の行(NR==1)はとして保存されxます。これは、現在の行が一致するかどうかをチェックした後に行わxれるため、最初の行も確実に印刷されます。


ツールボックスの一般化されたスクリプトとなるため、最初の行のアイデアを知る必要がないのが好きです。
マークスチュワート

1
このawkメソッドは、個別の行ごとに空/偽の配列エントリを作成します。4Mラインの場合、すべてが異なり(Qから明らかではない)、かなり短い(表示される)場合はおそらく大丈夫ですが、それ以上またはそれ以上のラインがある場合は、スラッシングまたは死ぬ可能性があります。!($0 in a)作成せずにテストし、これを回避するか、awkはperlの場合と同じロジックを実行できます:'$0!=x; NR==1{x=$0}'またはヘッダー行を空にできる場合'NR==1{x=$0;print} $0!=x'
-dave_thompson_085

1
@ dave_thompson_085行ごとの配列はどこで作成されますか?という意味!a[$0]ですか?なぜエントリを作成するのaですか?
テルドン

1
それがawkの仕組みだからです。gnu.org/software/gawk/manual/html_node/…、特に「注意」を参照してください。
-dave_thompson_085

1
@ dave_thompson_085まあ私はのろわれます!おかげで、私はそれを知らなかった。修正されました。
テルドン

4

AWKは、そのような目的にも非常に適切なツールです。コードの実行例を次に示します。

$ awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt | head -n 10                                
ID  Data1  Data2
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100

内訳

  • NR == 1 {print} テキストファイルの最初の行を印刷するように指示します
  • NR != 1 && $0!~/ID Data1 Data2/ 論理演算子&&は、AW​​Kに1に等しくない行を印刷するように指示しますID Data1 Data2{print}部品の不足に注意してください。awkでは、テスト条件がtrueと評価された場合、行が印刷されると想定されます。
  • | head -n 10出力を最初の10行に制限するためのほんの小さな追加です。AWK部品自体には関係なく、デモ目的でのみ使用されます。

それをファイルに入れたい場合は、次の> newFile.txtようにコマンドの最後に追加してコマンドの出力をリダイレクトします。

awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > newFile.txt

どのように持ちこたえますか?かなり良い:

$ time awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > /dev/null                            
    0m3.60s real     0m3.53s user     0m0.06s system

サイドノート

生成されたサンプルファイルは、100万から100万までループし、ファイルの最初の4行を印刷するために実行されました(したがって、4行×100万は400万行に相当します)。

awk 'BEGIN{ for(i=1;i<=1000000;i++) printf("ID  Data1  Data2\n1    100    100\n     100    200\n3    200    100\n");  }' > rmLines.txt

これはID Data1 Data2 foo、ヘッダーとは異なる行も出力することに注意してください(この場合、違いはほとんどありませんが、あなたは決して知りません)。
テルドン

@terdonはい、まさにその通りです。OPは、しかし、彼らは削除するだけで一つのパターンを指定し、彼の例では、これをサポートするように見える
Sergiy Kolodyazhnyy

3

Awk、任意のヘッダーに自動的に適応:

awk '( FNR == 1) {header=$0;print $0;}
     ( FNR > 1) && ($0 != header) { print $0;}'  file1  file2 ....

つまり、最初の行でヘッダーを取得して印刷し、そのヘッダーの後続の行DIFFERENTを印刷します。

FNR =現在のファイルのレコード数。これにより、複数のファイルを持つことができ、それぞれのファイルで同じことができます。


2

完全を期すために、PerlソリューションIMOは@terdonが提供したものよりもわずかにエレガントです。

perl -i -p -e 's/^ID.*$//s if $. > 1' file

1
ああ、しかし私の一番の目的は、パターンを指定する必要を回避し、代わりに最初の行からそれを読み取ることでした。アプローチは、で始まる行を単に削除しIDます。これにより、保持すべき行が削除されないという保証はありません。優雅さを育てたので、and gを使用しても意味がありません。実際、あなたのすべてのオプションはここを除いて役に立たない; 使用していない機能をアクティブにします。それでは、同じことをするだろう。^$m///s$s/^ID.*//s
テルドン

@terdon、まあまあ。あなたのものはもっと普遍的です!
クブブフェトウィッツ

2

質問を少しだけ押し戻すために...多分あなたの入力はそれ自体がいくつかのTSVファイルをまとめた結果であるように見えます。処理パイプラインのステップをバックアップできる場合(それを所有している場合、または行う人と話をすることができる場合)、ヘッダー対応ツールを使用して最初にデータを連結し、それによって必要な問題を取り除くことができます余分なヘッダー行を削除します。

たとえば、Millerを使用する場合:

$ cat f1.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
$ cat f2.tsv
ID  Data1 Data2
4 100 100
$ cat f3.tsv
ID  Data1 Data2
5 200 200

$ cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
ID  Data1 Data2
4 100 100
ID  Data1 Data2
5 200 200

$ mlr --tsvlite cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200

1
この情報を追加していただきありがとうございます。私のパイプラインのほとんどは、個々のサンプルのファイルを結合およびマージする必要があるため、これは将来非常に役立ちます。
ガイウスアウグストゥス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.