Concatenar múltiples archivos con el mismo encabezado

26

Tengo varios archivos con el mismo encabezado y diferentes vectores debajo de eso. Necesito concatenarlos a todos, pero solo quiero que se concatene el encabezado del primer archivo y no quiero que se concatenen otros encabezados ya que son todos iguales.

por ejemplo: 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

Necesito que la salida sea

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

¿Podría escribir un script en R pero lo necesito en shell?

Jana
fuente

Respuestas:

17

Si sabe cómo hacerlo en R, entonces hágalo en R. Con las herramientas clásicas de Unix, esto se hace de forma más natural en awk.

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

La primera línea del script awk coincide con la primera línea de un archivo ( FNR==1) excepto si también es la primera línea de todos los archivos ( NR==1). Cuando se cumplen estas condiciones, la expresión while (/^<header>/) getline;se ejecuta, lo que hace que awk siga leyendo otra línea (omitiendo la actual) siempre que la actual coincida con la expresión regular ^<header>. La segunda línea del script awk imprime todo excepto las líneas que se omitieron anteriormente.

Gilles 'SO- deja de ser malvado'
fuente
Gracias Gilles Cada uno de mis archivos está en GB. R no será eficiente para hacer esto. Por eso pregunté.
Jana
@Jana ¿Hay líneas que parecen encabezados pero no están en la parte superior del archivo? Si no, la forma más rápida es usar grep(como en la respuesta de sputnik ).
Gilles 'SO- deja de ser malvado'
No, las líneas de encabezado son similares a todos los archivos y están justo en la parte superior de cada archivo. Sí grep fue más rápido. Gracias a los dos
Jana
1
@Jana Por cierto, si todos sus archivos tienen el mismo número de líneas de encabezado, aquí hay otra forma (que espero sea aún más rápida): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(si tiene 10 líneas de encabezado). Además, si sus archivos tienen números en sus nombres, tenga cuidado de que estén file9.txtordenados entre file89.txty file90.txt. Si los archivos tienen números gustaría file001.txt, ..., files009.txt, files010.txt, ..., a continuación, files*.txtaparecerá una lista de ellos en el orden correcto.
Gilles 'SO- deja de ser malvado'
Una mejor solución (de stackoverflow.com/a/16890695/310441 ) que no requiere coincidencia de expresiones regulares: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen
42

Otra solución, similar a " cat+grep" desde arriba, usando taily head:

  1. Escriba el encabezado del primer archivo en la salida:

    head -2 file1.txt > all.txt

    - head -2Obtiene 2 primeras líneas del archivo.

  2. Agregue el contenido de todos los archivos:

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

    - -n +3hace taillíneas de impresión desde el 3er hasta el final, -qle dice que no imprima el encabezado con el nombre del archivo (leer man), >>agrega al archivo, no lo sobrescribe como >.

Y seguro que puedes poner ambos comandos en una línea:

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

o en lugar de ;poner &&entre ellos para verificar el éxito.

xealits
fuente
3
Sugiero simplemente simplemente: (head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txto(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu
4

Intenta hacer esto:

$ 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 -vbandera significa invertir el partido de
  • ^en REGEX , significa el comienzo de la cadena
  • si tienes un montón de archivos, puedes hacer

:

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

Es una técnica de corte de matriz .

Gilles Quenot
fuente
Gracias sputnick, pero tengo ~ 30 archivos (file1.txt, file2.txt, file3.txt..filen.txt) para concatenar. ¿Debo escribir cada nombre de archivo o hay alguna otra forma de hacerlo?
Jana
Vea mi publicación editada con técnica de corte
Gilles Quenot
Esto elimina <header>líneas en cualquier parte de los archivos, no solo al principio. Esto puede no ser un problema aquí, dependiendo de los datos.
Gilles 'SO- deja de ser malvado'
1
Más simple:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Gilles 'SO- deja de ser malvado'
@Gilles: Noté tu respuesta después de mucho tiempo, pero fue muy útil
Jana
1

El tailcomando (en GNU, al menos) tiene una opción para omitir un número dado de líneas iniciales. Para imprimir desde la segunda línea en adelante, es decir, omitir un encabezado de una línea, haga lo siguiente:tail -n+2 myfile

Entonces, para mantener el encabezado de dos líneas del primer archivo pero no el segundo, en Bash:

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

O, para muchos archivos:

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

Si se sabe que cierta cadena está presente en todas las líneas de encabezado pero nunca en el resto de los archivos de entrada, grep -ves un enfoque más simple, como lo demostró sputnik.

etal
fuente
1

Más corto (no necesariamente más rápido) con sed:

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

Esto eliminará todas las líneas que comiencen a <header>...partir de la línea 3, por lo que se conserva el primer encabezado y se eliminan los otros encabezados. Si hay un número diferente de líneas en el encabezado, ajuste el comando en consecuencia (por ejemplo, para el uso del encabezado de 6 líneas en 7lugar de 3).
Si se desconoce el número de líneas en el encabezado, puede intentar de esta manera:

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

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

Suponiendo que está utilizando una carpeta con archivos .txt con el mismo encabezado que debe combinarse / concatenarse, este código combinaría todos los archivos txt en all.txt con un solo encabezado. la primera línea (líneas separadas por punto y coma) reúne todos los archivos de texto para concatenar, la segunda línea genera el encabezado del primer archivo txt en all.txt , y la última línea concatena todos los archivos de texto reunidos sin el encabezado (iniciando el concatenación desde la fila 2 en adelante) y lo agrega a all.txt .

Eric
fuente
una pequeña explicación ayudaría mucho a los futuros usuarios
Jeff Schaller