Verketten Sie mehrere Dateien mit demselben Header


26

Ich habe mehrere Dateien mit dem gleichen Header und verschiedenen Vektoren darunter. Ich muss sie alle verketten, möchte aber, dass nur der Header der ersten Datei verkettet wird, und ich möchte nicht, dass andere Header verkettet werden, da sie alle gleich sind.

Zum Beispiel: 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

Ich brauche die Ausgabe zu sein

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

Ich könnte ein Skript in R schreiben, aber ich brauche es in der Shell?

Antworten:


17

Wenn Sie wissen, wie man es in R macht, dann machen Sie es auf jeden Fall in R. Mit klassischen Unix-Werkzeugen geschieht dies am natürlichsten in awk.

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

Die erste Zeile des awk-Skripts stimmt mit der ersten Zeile einer Datei überein ( FNR==1), es sei denn, es ist auch die erste Zeile aller Dateien ( NR==1). Wenn diese Bedingungen erfüllt sind, wird der Ausdruck while (/^<header>/) getline;ausgeführt, wodurch awk eine weitere Zeile liest (die aktuelle überspringt), solange die aktuelle Zeile mit dem regulären Ausdruck übereinstimmt ^<header>. Die zweite Zeile des awk-Skripts druckt alles außer den zuvor übersprungenen Zeilen.


Vielen Dank, Gilles. Jede meiner Dateien ist in GB. R wird dies nicht effizient tun. Darum habe ich gefragt.
Jana

@Jana Gibt es Zeilen, die wie Überschriften aussehen, sich aber nicht oben in der Datei befinden? Wenn nicht, ist der schnellste Weg grep(wie in der Antwort von sputnik ).
Gilles 'SO- hör auf böse zu sein'

Nein, die Kopfzeilen sind allen Dateien ähnlich und befinden sich nur am Anfang jeder Datei. Ja, grep war schneller. Vielen Dank euch beiden
Jana

1
@Jana Übrigens, wenn alle Ihre Dateien die gleiche Anzahl von Kopfzeilen haben, ist hier ein anderer Weg (von dem ich erwarte, dass er noch schneller ist): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(wenn Sie 10 Kopfzeilen haben). Wenn Ihre Dateien Nummern im Namen haben, achten Sie darauf, dass file9.txtdiese zwischen file89.txtund sortiert sind file90.txt. Wenn Sie Ihre Dateien haben Zahlen mögen file001.txt, ..., files009.txt, files010.txt, ..., dann files*.txtwird sie in der richtigen Reihenfolge aufzulisten.
Gilles 'SO- hör auf böse zu sein'

Eine bessere Lösung (von stackoverflow.com/a/16890695/310441 ), die keine Regex-Übereinstimmung erfordert: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

Eine andere Lösung, ähnlich " cat+grep" von oben, verwendet tailund head:

  1. Schreiben Sie den Header der ersten Datei in die Ausgabe:

    head -2 file1.txt > all.txt

    - head -2Erhält 2 erste Zeilen der Datei.

  2. Fügen Sie den Inhalt aller Dateien hinzu:

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

    - -n +3macht tailDruckzeilen von 3 bis zum Ende, -qweist es an, den Header mit dem Dateinamen nicht zu drucken (lesen man), >>fügt der Datei hinzu, überschreibt sie nicht als >.

Und sicher können Sie beide Befehle in eine Zeile setzen:

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

oder anstatt sie zur Erfolgskontrolle dazwischen zu ;legen &&.


3
Ich schlage vor, es einfach weiter zu machen: (head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txtoder(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu

4

Versuchen Sie Folgendes:

$ 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

HINWEIS

  • Das -vFlag bedeutet, die Übereinstimmung von
  • ^In REGEX bedeutet dies den Anfang der Zeichenfolge
  • Wenn Sie eine Reihe von Dateien haben, können Sie dies tun

:

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

Es ist eine Array-Schneidetechnik.


Danke sputnick, aber ich muss ~ 30 Dateien (file1.txt, file2.txt, file3.txt..filen.txt) verketten. Soll ich jeden Dateinamen eingeben oder gibt es andere Möglichkeiten, dies zu tun?
Jana

Siehe meinen bearbeiteten Beitrag mit Aufschnitttechnik
Gilles Quenot

Dies entfernt <header>Linien irgendwo in den Dateien, nicht nur am Anfang. Dies ist hier möglicherweise kein Problem, abhängig von den Daten.
Gilles 'SO- hör auf böse zu sein'

1
Einfacher:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Gilles 'SO- hör auf böse zu sein'

@ Gilles: Ich habe Ihre Antwort nach einer langen Zeit bemerkt, aber es war sehr nützlich
Jana

1

Der tailBefehl (zumindest unter GNU) hat die Option, eine bestimmte Anzahl von Anfangszeilen zu überspringen. Gehen Sie wie folgt vor, um ab der zweiten Zeile zu drucken, dh einen einzeiligen Header zu überspringen:tail -n+2 myfile

So behalten Sie den zweizeiligen Header der ersten Datei in Bash bei, nicht jedoch den der zweiten:

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

Oder für viele Dateien:

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

Wenn bekannt ist, dass eine bestimmte Zeichenfolge in allen Kopfzeilen, jedoch niemals in den übrigen Eingabedateien vorhanden ist, grep -vist dies ein einfacherer Ansatz, wie sputnik gezeigt hat.


1

Kürzer (nicht unbedingt schneller) mit sed:

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

Dadurch werden alle Zeilen gelöscht, die mit <header>...Zeile 3 beginnen, sodass der erste Header erhalten bleibt und die anderen Header entfernt werden. Wenn der Header eine andere Anzahl von Zeilen enthält, passen Sie den Befehl entsprechend an (z. B. für Header mit 6 Zeilen 7anstelle von 3).
Wenn die Anzahl der Zeilen in der Kopfzeile unbekannt ist, können Sie Folgendes versuchen:

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

Angenommen, Sie verwenden einen Ordner mit TXT-Dateien mit demselben Header, der kombiniert / verkettet werden muss. In diesem Code werden alle TXT-Dateien in all.txt mit nur einem Header kombiniert . Die erste Zeile (durch Semikolon getrennte Zeilen) fasst alle zu verkettenden Textdateien zusammen, die zweite Zeile gibt den Header der ersten TXT-Datei in die Datei all.txt aus , und die letzte Zeile verkettet alle ohne den Header gesammelten Textdateien (durch Starten der Verkettung ab Zeile 2) und hängt es an all.txt an .


Eine kleine Erklärung würde den zukünftigen Anwendern helfen
Jeff Schaller
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.