Concatena più file con la stessa intestazione


26

Ho più file con la stessa intestazione e diversi vettori sottostanti. Devo concatenarli tutti, ma voglio concatenare solo l'intestazione del primo file e non voglio concatenare altre intestazioni poiché sono tutte uguali.

ad esempio: 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

Ho bisogno che l'output sia

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

Potrei scrivere una sceneggiatura in R ma ne ho bisogno nella shell?

Risposte:


17

Se sai come farlo in R, allora fallo in R. Con gli strumenti unix classici, questo è naturalmente fatto in awk.

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

La prima riga dello script awk corrisponde alla prima riga di un file ( FNR==1) tranne se è anche la prima riga tra tutti i file ( NR==1). Quando vengono soddisfatte queste condizioni, while (/^<header>/) getline;viene eseguita l'espressione , che fa sì che awk continui a leggere un'altra riga (saltando quella corrente) finché quella corrente corrisponde alla regexp ^<header>. La seconda riga dello script awk stampa tutto tranne le righe precedentemente ignorate.


Grazie Gilles. Ognuno dei miei file è in GB. R non sarà efficiente farlo. Ecco perché ho chiesto.
Jana,

@Jana Ci sono linee che sembrano intestazioni ma non sono all'inizio del file? In caso contrario, il modo più veloce è quello di utilizzare grep(come nella risposta di Sputnik ).
Gilles 'SO- smetti di essere malvagio' l'

No, le righe di intestazione sono simili a tutti i file e sono solo all'inizio di ogni file. Sì grep era più veloce. Grazie a entrambi
Jana

1
@Jana A proposito, se tutti i tuoi file hanno lo stesso numero di righe di intestazione, ecco un altro modo (che mi aspetto di essere ancora più veloce): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(se hai 10 righe di intestazione). Inoltre, se i tuoi file hanno numeri nei loro nomi, fai attenzione che file9.txtsia ordinato tra file89.txte file90.txt. Se i file sono numeri come file001.txt, ..., files009.txt, files010.txt, ..., allora files*.txtsarà elencarli nel giusto ordine.
Gilles 'SO- smetti di essere malvagio'

Una soluzione migliore (da stackoverflow.com/a/16890695/310441 ) che non richiede la corrispondenza regex: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

Un'altra soluzione, simile a " cat+grep" dall'alto, utilizzando taile head:

  1. Scrivi l'intestazione del primo file nell'output:

    head -2 file1.txt > all.txt

    - head -2ottiene 2 prime righe del file.

  2. Aggiungi il contenuto di tutti i file:

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

    - -n +3crea taillinee di stampa dal 3 ° alla fine, gli -qdice di non stampare l'intestazione con il nome del file (letto man), >>aggiunge al file, non lo sovrascrive come >.

E sicuro che puoi mettere entrambi i comandi in una riga:

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

o invece di ;metterli &&tra loro per il controllo del successo.


3
Suggerisco di approfondirlo semplicemente a: (head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txtoppure(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu

4

Prova a fare questo:

$ 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

NOTA

  • la -vbandiera significa invertire la partita di
  • ^in REGEX , significa l' inizio della stringa
  • se hai un sacco di file, puoi farlo

:

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

È una tecnica di affettatura array.


Grazie sputnick, ma ho ~ 30 file (file1.txt, file2.txt, file3.txt..filen.txt) da concatenare. Devo digitare ogni nome di file o ci sono altri modi per farlo?
Jana,

Vedi il mio post modificato con tecnica di slicing
Gilles Quenot

Questo rimuove le <header>linee ovunque nei file, non solo all'inizio. Questo potrebbe non essere un problema qui, a seconda dei dati.
Gilles 'SO- smetti di essere malvagio' l'

1
Più semplice:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Gilles 'SO- smetti di essere malvagio'

@Gilles: ho notato la tua risposta dopo molto tempo ma è stato molto utile
Jana,

1

Il tailcomando (almeno su GNU) ha un'opzione per saltare un dato numero di linee iniziali. Per stampare dalla seconda riga in poi, ovvero saltare un'intestazione di una riga, eseguire:tail -n+2 myfile

Quindi, per mantenere l'intestazione a due righe del primo file ma non del secondo, in Bash:

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

Oppure, per molti file:

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

Se è noto che una determinata stringa è presente in tutte le righe di intestazione ma mai nel resto dei file di input, grep -vè un approccio più semplice, come ha mostrato sputnik.


1

Più breve (non necessariamente più veloce) con sed:

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

Ciò eliminerà tutte le righe che iniziano a <header>...partire dalla riga 3, quindi la prima intestazione viene preservata e le altre intestazioni vengono rimosse. Se nell'intestazione è presente un numero diverso di righe, regolare il comando di conseguenza (ad esempio, per utilizzare l'intestazione a 6 righe 7anziché 3).
Se il numero di righe nell'intestazione è sconosciuto, puoi provare in questo modo:

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

Supponendo che si stia utilizzando una cartella con file .txt con la stessa intestazione che deve essere combinata / concatenata, questo codice combinerebbe tutti i file txt in all.txt con una sola intestazione. la prima riga (linee separate da punti e virgola) raccoglie tutti i file di testo per concatenare, la seconda riga genera l'intestazione dal primo file txt in all.txt e l'ultima riga concatena tutti i file di testo raccolti senza l'intestazione (avviando il concatenazione dalla riga 2 in poi) e la aggiunge a all.txt .


una piccola spiegazione farebbe molto per aiutare i futuri utenti
Jeff Schaller
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.