web-dev-qa-db-fra.com

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

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 seul 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?

29
Jana

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 s'il s'agit également de 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 à continuer de 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.

Une autre solution, similaire à "cat+grep "d'en haut, en utilisant tail et head:

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

    head -2 file1.txt > all.txt
    

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

  2. Ajoutez le contenu de tous les fichiers:

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

    - -n +3 fait tail imprimer les lignes du 3ème à la fin, -q lui dit de ne pas imprimer l'en-tête avec le nom du fichier (lire 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 ; mettre && entre eux pour vérifier la réussite.

44
xealits

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 -v flag signifie inverser la correspondance de grep
  • ^ in 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 bash array.

4
Gilles Quenot

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

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

Cela supprimera toutes les lignes commençant par <header>... à partir de 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 l'en-tête à 6 lignes, utilisez 7 au lieu de 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
1
don_crissti

La commande tail (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 ignorer 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 -v est une approche plus simple, comme l'a montré spoutnik.

1
etal

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 en 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 commençant la concaténation à partir de la ligne 2) et l'ajoute à all.txt.

0
Eric