Concaténer plusieurs fichiers avec le même en-tête


26

J'ai plusieurs fichiers avec le même en-tête et différents vecteurs en dessous. J'ai besoin de les concaténer tous, mais je veux que l'en-tête du premier fichier soit concaténé et je ne veux pas que les autres en-têtes soient concaténés car ils sont tous les mêmes.

par exemple: 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

J'ai besoin que la sortie soit

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

Je pourrais écrire un script en R mais j'en ai besoin en shell?

Réponses:


17

Si vous savez comment le faire en R, alors faites-le certainement en R. Avec les outils Unix classiques, cela se fait le plus naturellement dans awk.

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

La première ligne du script awk correspond à la première ligne d'un fichier ( FNR==1) sauf si c'est également la première ligne de tous les fichiers ( NR==1). Lorsque ces conditions sont remplies, l'expression while (/^<header>/) getline;est exécutée, ce qui oblige awk à lire une autre ligne (en sautant la ligne actuelle) tant que la ligne actuelle correspond à l'expression régulière ^<header>. La deuxième ligne du script awk imprime tout sauf les lignes qui ont été précédemment ignorées.


Merci Gilles. Chacun de mes fichiers est en Go. R ne sera pas efficace pour ce faire. C'est pourquoi j'ai demandé.
Jana

@Jana Y a-t-il des lignes qui ressemblent à des en-têtes mais qui ne sont pas en haut du fichier? Sinon, le moyen le plus rapide est d'utiliser grep(comme dans la réponse de Spoutnik ).
Gilles 'SO- arrête d'être méchant'

Non, les lignes d'en-tête sont similaires à tous les fichiers et elles sont juste en haut de chaque fichier. Ouais grep était plus rapide. Merci à vous deux
Jana

1
@Jana Soit dit en passant, si tous vos fichiers ont le même nombre de lignes d'en-tête, voici une autre façon (qui devrait être encore plus rapide): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(si vous avez 10 lignes d'en-tête). De plus, si vos fichiers ont des numéros dans leurs noms, méfiez-vous qui file9.txtest trié entre file89.txtet file90.txt. Si vos fichiers ont de tels chiffres file001.txt, ..., files009.txt, files010.txt, ..., puis files*.txtleur liste dans l'ordre.
Gilles 'SO- arrête d'être méchant'

Une meilleure solution (de stackoverflow.com/a/16890695/310441 ) qui ne nécessite pas de correspondance d'expressions régulières: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

Une autre solution, similaire à " cat+grep" ci-dessus, utilisant tailet head:

  1. Écrivez l'en-tête du premier fichier dans la sortie:

    head -2 file1.txt > all.txt

    - head -2obtient les 2 premières lignes du fichier.

  2. Ajoutez le contenu de tous les fichiers:

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

    - -n +3crée taildes lignes d'impression du 3ème à la fin, lui -qdit de ne pas imprimer l'en-tête avec le nom du fichier (lu man), >>ajoute au fichier, ne le remplace pas par >.

Et bien sûr, vous pouvez mettre les deux commandes sur une seule ligne:

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

ou au lieu de les ;mettre &&entre eux pour vérifier le succès.


3
Je suggère de continuer simplement à: (head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txtou(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu

4

Essayez de faire ceci:

$ 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

REMARQUE

  • le -vdrapeau signifie inverser le match de
  • ^dans REGEX , signifie début de la chaîne
  • si vous avez un tas de fichiers, vous pouvez le faire

:

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

C'est une technique de découpage de tableau .


Merci sputnick, mais j'ai ~ 30 fichiers (file1.txt, file2.txt, file3.txt..filen.txt) à concaténer. Dois-je taper chaque nom de fichier ou existe-t-il d'autres façons de le faire?
Jana

Voir mon article édité avec la technique de découpage
Gilles Quenot

Cela supprime les <header>lignes n'importe où dans les fichiers, pas seulement au début. Cela peut ne pas être un problème ici, selon les données.
Gilles 'SO- arrête d'être méchant'

1
Plus simple:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Gilles 'SO- arrête d'être méchant'

@ Gilles: J'ai remarqué votre réponse après un long moment mais elle a été très utile
Jana

1

La tailcommande (sur GNU, au moins) a une option pour ignorer un nombre donné de lignes initiales. Pour imprimer à partir de la deuxième ligne, c'est-à-dire sauter un en-tête d'une ligne, procédez comme suit:tail -n+2 myfile

Donc, pour garder l'en-tête à deux lignes du premier fichier mais pas du second, dans Bash:

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

Ou, pour de nombreux fichiers:

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

Si une certaine chaîne est connue pour être présente dans toutes les lignes d'en-tête mais jamais dans le reste des fichiers d'entrée, grep -vc'est une approche plus simple, comme l'a montré sputnik.


1

Plus court (pas nécessairement plus rapide) avec sed:

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

Cela supprimera toutes les lignes <header>...commençant par la ligne 3, donc le premier en-tête est conservé et les autres en-têtes sont supprimés. S'il y a un nombre différent de lignes dans l'en-tête, ajustez la commande en conséquence (par exemple pour un en-tête à 6 lignes, utilisez 7plutôt que 3).
Si le nombre de lignes dans l'en-tête est inconnu, vous pouvez essayer comme ceci:

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

En supposant que vous utilisez un dossier avec des fichiers .txt avec le même en-tête qui doivent être combinés / concaténés, ce code combinerait tous les fichiers txt dans all.txt avec un seul en-tête. la première ligne (lignes séparées par des points-virgules) rassemble tous les fichiers texte à concaténer, les secondes lignes sortent l'en-tête du premier fichier txt dans all.txt et la dernière ligne concatène tous les fichiers texte rassemblés sans l'en-tête (en démarrant le concaténation à partir de la ligne 2) et l'ajoute à all.txt .


une infime explication aiderait grandement les futurs utilisateurs
Jeff Schaller
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.