同じヘッダーで複数のファイルを連結する


26

同じヘッダーとその下の異なるベクターを持つ複数のファイルがあります。それらをすべて連結する必要がありますが、最初のファイルのヘッダーのみを連結し、他のヘッダーはすべて同じであるため連結しないようにします。

例:file1.txt

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B 
C

file2.txt

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
D
E 
F

出力が必要です

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B
C
D
E 
F

Rでスクリプトを記述できますが、シェルでそれが必要ですか?

回答:


17

Rでそれを行う方法を知っている場合は、必ずRでそれを行ってください。古典的なUNIXツールでは、これは最も自然にawkで行われます。

awk '
    FNR==1 && NR!=1 { while (/^<header>/) getline; }
    1 {print}
' file*.txt >all.txt

awkスクリプトの最初の行は、ファイルの最初の行(FNR==1)と一致しますが、すべてのファイルの最初の行(NR==1)でもある場合を除きます。これらの条件が満たされると、式while (/^<header>/) getline;が実行されます。これにより、awkは、現在の行がregexpに一致する限り、別の行の読み取りを続けます(現在の行をスキップします)^<header>。awkスクリプトの2行目は、以前にスキップされた行を除くすべてを印刷します。


ありがとう、ジル。各ファイルはGB単位です。Rはこれを行うのに効率的ではありません。それが私が尋ねた理由です。
ヤナ

@Janaヘッダーのように見えるがファイルの先頭にない行はありますか?そうでない場合、最速の方法は使用することですgrepsputnikの回答のように)。
ジル 'SO-悪

ヘッダー行はすべてのファイルに類似しておらず、各ファイルの先頭にあります。うん、grepの方が速かった。あなたの両方に感謝
ジャナ

1
@Janaところで、すべてのファイルに同じ数のヘッダー行がある場合、別の方法があります(さらに高速になると思います):(head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txtヘッダー行が10行ある場合)。また、ファイルの名前に数字が含まれている場合は、とのfile9.txtfile89.txtでソートされることに注意してくださいfile90.txt。あなたのファイルは次のように数字を持っている場合はfile001.txt、...、 files009.txtfiles010.txt、...、その後、files*.txt彼らは右の順に一覧表示されます。
ジル 'SO-悪

正規表現の一致を必要としないより良いソリューション(stackoverflow.com/a/16890695/310441から): awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

cat+grep上記の" " に似た別のソリューションで、tailand を使用しheadます。

  1. 最初のファイルのヘッダーを出力に書き込みます。

    head -2 file1.txt > all.txt

    - head -2ファイルの最初の2行を取得します。

  2. すべてのファイルのコンテンツを追加します。

    tail -n +3 -q file*.txt >> all.txt

    -は-n +3なりtail、最後まで3から印刷行を -qファイル名(リードを持つヘッダを印刷しないことを指示man>>しないように、それを上書きし、ファイルに追加します>

そして、両方のコマンドを1行で入力できることを確認してください。

head -2 file1.txt > all.txt; tail -n +3 -q file*.txt >> all.txt

または成功チェックのためにそれらの間に;置く代わりに&&


3
私はそれをさらに簡単にすることを提案します:(head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txtまたは(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
-HongboZhu

4

これを試してみてください:

$ cat file1.txt; grep -v "^<header" file2.txt
<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B 
C
D
E 
F

注意

  • -v一致反転するフラグ手段
  • ^REGEX、手段は、文字列の先頭
  • たくさんのファイルがあれば、できます

array=( files*.txt )
{ cat ${array[@]:0:1}; grep -v "^<header" ${array[@]:1}; } > new_file.txt

これは、配列のスライス手法です。


sputnickに感謝しますが、連結するファイル(file1.txt、file2.txt、file3.txt..filen.txt)は30個まであります。すべてのファイル名を入力する必要がありますか、それ以外の方法がありますか?
ヤナ

スライシングテクニックで編集した投稿を参照してください
ジルケノー

これにより<header>、ファイルの先頭だけでなく、どこでも行が削除されます。データによっては、ここでは問題にならない場合があります。
ジル 'SO-悪

1
シンプル:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
ジル「SO-悪であるのをやめなさい」

@Gilles:久しぶりにあなたの答えに気付きましたが、とても役に立ちました
ジャナ

1

tail(GNU、少なくとも上の)コマンドは、最初の行の所定数をスキップするオプションを有します。2行目以降の印刷、つまり1行のヘッダーをスキップするには、次のようにします。tail -n+2 myfile

したがって、最初のファイルの2行ヘッダーを保持し、2番目のファイルは保持しないようにするには、Bashで:

cat file1.txt <(tail -n+3 file2.txt) > combined.txt

または、多くのファイルの場合:

head -n1 file1.txt > combined.txt
for fname in *.txt
do
    tail -n+3 $fname >> combined.txt
done

特定の文字列がすべてのヘッダー行に存在することがわかっているが、残りの入力ファイルにgrep -vは存在しない場合、sputnikが示したように、より単純なアプローチです。


1

短い(必ずしも高速ではない)sed

sed -e '3,${/^<header>/d' -e '}' file*.txt > all.txt

これにより<header>...、3行目から始まるすべての行が削除されるため、最初のヘッダーが保持され、他のヘッダーは削除されます。ヘッダーの行数が異なる場合は、それに応じてコマンドを調整します(たとえば、6行ヘッダーの7代わりに使用します3)。
ヘッダーの行数が不明な場合は、次のようにしてみてください。

sed '1{
: again
n
/^<header>/b again
}
/^<header>/d
' file*.txt > all.txt

0

array =(* .txt); head -1 $ {array [0]}> all.txt; tail -n +2 -q $ {array [@]:0} >> all.txt

結合/連結する必要のある同じヘッダーを持つ.txtファイルを含むフォルダーを使用しているとすると、このコードは、txtファイルをすべて1つのヘッダーを持つall.txtに結合します。最初の行(セミコロンで区切られた行)は連結するすべてのテキストファイルを収集し、2行目は最初のtxtファイルのヘッダーをall.txtに出力し、最後の行はヘッダーなしで収集されたすべてのテキストファイルを連結します( 2行目以降の連結)とall.txtに追加します


ほんの少しの説明が、将来のユーザーを支援するのに
大いに
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.