Concatene varios archivos pero incluya el nombre del archivo como encabezados de sección

353

Me gustaría concatenar varios archivos de texto en un archivo grande en la terminal. Sé que puedo hacer esto usando el comando cat. Sin embargo, me gustaría que el nombre de archivo de cada archivo preceda al "volcado de datos" para ese archivo. Alguien sabe cómo hacer esto?

lo que tengo actualmente:

file1.txt = bluemoongoodbeer

file2.txt = awesomepossum

file3.txt = hownowbrowncow

cat file1.txt file2.txt file3.txt

salida deseada:

file1

bluemoongoodbeer

file2

awesomepossum

file3

hownowbrowncow
Mella
fuente
De hecho, estoy usando uuencode y uudecode hacer algo similar, no necesito el archivo legible por entre medio, sólo se necesita el resultado y el tubo a otro comando de nuevo
2
unix.stackexchange.com/questions/12342/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

531

Estaba buscando lo mismo, y descubrí que esto sugiere:

tail -n +1 file1.txt file2.txt file3.txt

Salida:

==> file1.txt <==
<contents of file1.txt>

==> file2.txt <==
<contents of file2.txt>

==> file3.txt <==
<contents of file3.txt>

Si solo hay un archivo, el encabezado no se imprimirá. Si usa utilidades GNU, puede usar -vpara imprimir siempre un encabezado.

DS.
fuente
2
Esto funciona también con la cola de GNU (parte de GNU Coreutils).
ArjunShankar
77
¡Impresionante -n +1opción! Una alternativa: head -n-0 file1 file2 file3.
Llama congelada
2
Funciona muy bien con la cola BSD y la cola GNU en MacOS X. Puede dejar el espacio entre -ny +1, como en -n+1.
vdm
44
tail -n +1 *fue exactamente lo que estaba buscando, gracias!
kR105
1
funciona en MacOsX 10.14.4sudo ulimit -n 1024; find -f . -name "*.rb" | xargs tail -n+1 > ./source_ruby.txt
kolas
186

Usé grep para algo similar:

grep "" *.txt

No le da un 'encabezado', sino que antepone cada línea con el nombre del archivo.

Theo
fuente
10
La salida se rompe si se *.txtexpande a un solo archivo. En este sentido, aconsejaríagrep '' /dev/null *.txt
antak
1
+1 por mostrarme un nuevo uso para grep. Esto satisfizo mis necesidades perfectamente. en mi caso, cada archivo solo contenía una línea, por lo que me dio una salida perfectamente formateada que era fácilmente analizable
verboze
99
grepsolo imprimirá encabezados de archivo si hay más de un archivo. Si desea asegurarse de imprimir siempre la ruta del archivo, use -H. Si no quieres usar los encabezados -h. También tenga en cuenta que se imprimirá (standard input)para STDIN.
Jorge Bucaran
1
También puede usar ag(el buscador plateado): por defecto, ag . *.txtprefija cada archivo con su nombre y cada línea con su número.
anol
1
Es de destacar que pasar -na grep también produce números de línea que le permiten escribir linters simples con puntos precisos que podrían ser, por ejemplo, emacs.
Deprimido Daniel
104

Esto también debería hacer el truco:

find . -type f -print -exec cat {} \;

Medio:

find    = linux `find` command finds filenames, see `man find` for more info
.       = in current directory
-type f = only files, not directories
-print  = show found file
-exec   = additionally execute another linux command
cat     = linux `cat` command, see `man cat`, displays file contents
{}      = placeholder for the currently found filename
\;      = tell `find` command that it ends now here

Además, puede combinar búsquedas a través de operadores booleanos como -ando -or. find -lsTambién es agradable.

Maxim_united
fuente
1
¿Podría explicar más qué hace este comando? Es exactamente lo que necesitaba
AAlvz
44
Este es el comando de búsqueda estándar de Linux. Busca todos los archivos en el directorio actual, imprime su nombre, luego, para cada uno, captura el archivo. Omitir -printno imprimirá el nombre del archivo antes de cat.
Maxim_united
17
También puede usar -printfpara personalizar la salida. Por ejemplo: find *.conf -type f -printf '\n==> %p <==\n' -exec cat {} \;para que coincida con la salida detail -n +1 *
Maxim_united
1
-printf no funciona en mac, a menos que quieras brew install findutilsy luego lo uses en gfindlugar de find.
Matt Fletcher el
1
Si desea colores, puede usar ese hecho que findpermite múltiples -execs:find -name '*.conf' -exec printf '\n\e[33;1m%s\e[0m\n' {} \; -exec cat {} \;
banbh
24

Esto debería funcionar:

for filename in file1.txt file2.txt file3.txt; do
    echo "$filename"
    cat "$filename"
done > output.txt

o para hacer esto para todos los archivos de texto de forma recursiva:

find . -type f -name '*.txt' -print | while read filename; do
    echo "$filename"
    cat "$filename"
done > output.txt
Chris Eberle
fuente
1
no funcionó Acabo de escribir un código awk realmente feo: para i en $ listoffiles do awk '{print FILENAME, $ 0, $ 1, $ 2, $ 3, $ 4, $ 5, $ 6, $ 7, $ 8, $ 9, $ 10, $ 11}' $ i >> concat.txt hecho
Nick
2
...¿cuidado para elaborar? Eso es tan simple como el código bash.
Chris Eberle
@ Nick: tu línea awk ni siquiera debería funcionar, teniendo en cuenta que $0es la línea completa, por lo que en realidad tienes columnas repetidas allí ...
Chris Eberle
@Nick: Solución ingeniosa de lo contrario :)
Chris Eberle
@ Chris: sí, pero es mucho más feo de lo que me gustaría que fuera. ¿Tal vez su código no estaba funcionando para mí porque estoy usando >> para atrapar el stdout?
Nick
17

Tenía una serie de archivos que terminaban en stats.txt que quería concatenar con los nombres de archivo.

Hice lo siguiente y cuando hay más de un archivo, el comando "más" incluye el nombre del archivo como encabezado.

more *stats.txt > stats.txt

o para un caso general

more FILES_TO_CONCAT > OUTPUT_FILE
Steinfadt
fuente
66
more <FILES> | cat
Acumenus
15
find . -type f -print0 | xargs -0 -I % sh -c 'echo %; cat %'

Esto imprimirá el nombre de archivo completo (incluida la ruta), luego el contenido del archivo. También es muy flexible, ya que puede usar -name "expr" para el comando find y ejecutar tantos comandos como desee en los archivos.

kevinf
fuente
1
También es bastante sencillo combinarlo grep. Para usar con bash:find . -type f -print | grep PATTERN | xargs -n 1 -I {} -i bash -c 'echo ==== {} ====; cat {}; echo'
dojuba
5

Me gusta esta opcion

for x in $(ls ./*.php); do echo $x; cat $x | grep -i 'menuItem'; done

La salida se ve así:

./debug-things.php
./Facebook.Pixel.Code.php
./footer.trusted.seller.items.php
./GoogleAnalytics.php
./JivositeCode.php
./Live-Messenger.php
./mPopex.php
./NOTIFICATIONS-box.php
./reviewPopUp_Frame.php
            $('#top-nav-scroller-pos-<?=$active**MenuItem**;?>').addClass('active');
            gotTo**MenuItem**();
./Reviews-Frames-PopUps.php
./social.media.login.btns.php
./social-side-bar.php
./staticWalletsAlerst.php
./tmp-fix.php
./top-nav-scroller.php
$active**MenuItem** = '0';
        $active**MenuItem** = '1';
        $active**MenuItem** = '2';
        $active**MenuItem** = '3';
./Waiting-Overlay.php
./Yandex.Metrika.php
ch3ll0v3k
fuente
4

Así es como normalmente manejo el formato así:

for i in *; do echo "$i"; echo ; cat "$i"; echo ; done ;

Generalmente canalizo al gato en un grep para obtener información específica.

MorpheousMarty
fuente
3
Ten cuidado aquí. for i in *no incluirá subdirectorios.
Acumenus
4

Y la solución awk que falta es:

$ awk '(FNR==1){print ">> " FILENAME " <<"}1' *
kvantour
fuente
Gracias por tu gran respuesta. ¿Puedes explicar el significado de 1al final de la expresión?
bwangel
1
Gracias. Puede echar un vistazo a cuál es el significado de 1 al final del guión awk
kvantour
3

puedes usar este comando simple en lugar de usar un bucle for,

ls -ltr | awk '{print $9}' | xargs head
Gagan
fuente
3

Si te gustan los colores, prueba esto:

for i in *; do echo; echo $'\e[33;1m'$i$'\e[0m'; cat $i; done | less -R

o:

tail -n +1 * | grep -e $ -e '==.*'

o: (con el paquete 'multitail' instalado)

multitail *
sjas
fuente
1
Por el bien de los colores que resaltan el nombre del archivo:find . -type f -name "*.txt" | xargs -I {} bash -c "echo $'\e[33;1m'{}$'\e[0m';cat {}"
MediaVince
3

Si todos los archivos tienen el mismo nombre o pueden coincidir con ellos find, puede hacer (por ejemplo):

find . -name create.sh | xargs tail -n +1

para buscar, muestre la ruta y el gato de cada archivo.

drkvogel
fuente
2

Aquí hay una manera realmente simple. Dijiste que quieres atrapar, lo que implica que quieres ver todo el archivo. Pero también necesita imprimir el nombre del archivo.

Prueba esto

head -n99999999 * o head -n99999999 file1.txt file2.txt file3.txt

Espero que ayude

Trey Brister
fuente
44
sería una sintaxis más limpia head -n -0 file.txt file2.txt file3.txt. Funciona para headGNU coreutils
doubleDown
1

Este método imprimirá el nombre del archivo y luego el contenido del archivo:

tail -f file1.txt file2.txt

Salida:

==> file1.txt <==
contents of file1.txt ...
contents of file1.txt ...

==> file2.txt <==
contents of file2.txt ...
contents of file2.txt ...
serenesat
fuente
3
Esto -fes útil si desea rastrear un archivo en el que se está escribiendo, pero que no está dentro del alcance de lo que solicitó el OP.
tripleee
1

Si quieres reemplazar esos feos ==> <== con algo más

tail -n +1 *.txt | sed -e 's/==>/\n###/g' -e 's/<==/###/g' >> "files.txt"

explicación:

tail -n +1 *.txt - salida de todos los archivos en la carpeta con encabezado

sed -e 's/==>/\n###/g' -e 's/<==/###/g'- reemplace ==>con una nueva línea + ### y <==solo con ###

>> "files.txt" - salida de todo a un archivo

boroboris
fuente
0
find . -type f -exec cat {} \; -print
usuario5689319
fuente
0

Si desea que el resultado tenga el mismo formato que el resultado deseado, puede probar:

for file in `ls file{1..3}.txt`; \
do echo $file | cut -d '.' -f 1; \ 
cat $file  ; done;

Resultado:

file1
bluemoongoodbeer
file2
awesomepossum
file3
hownowbrowncow

Puede poner echo -eantes y después del corte para tener también el espacio entre las líneas:

$ for file in `ls file{1..3}.txt`; do echo $file | cut -d '.' -f 1; echo -e; cat $file; echo -e  ; done;

Resultado:

file1

bluemoongoodbeer

file2

awesomepossum

file3

hownowbrowncow
Fekete Sumér
fuente
Explicación: el forbucle recorre la lista lsresultante del comando. $ ls file{1..3}.txtResultado: file1.txt file2.txt file3.txtcada iteración será echola $filecadena y luego se canalizará a un cutcomando que utilicé .como un separador de campo que divide el archivo X.txt en dos partes e imprime el campo1 (campo2 es el txt). El resto debe quedar claro
Fekete Sumér,
0
  • AIX 7.1 ksh

... mirando a aquellos que ya han mencionado la cabeza funciona para algunos de nosotros:

$ r head
head file*.txt
==> file1.txt <==
xxx
111

==> file2.txt <==
yyy
222
nyuk nyuk nyuk

==> file3.txt <==
zzz
$

Mi necesidad es leer la primera línea; como se señaló, si desea más de 10 líneas, deberá agregar opciones (head -9999, etc.).

Perdón por publicar un comentario derivado; No tengo suficiente crédito en la calle para comentar / agregar al comentario de alguien.

JustinKaisse
fuente
-1

Para resolver estas tareas, generalmente uso el siguiente comando:

$ cat file{1..3}.txt >> result.txt

Es una forma muy conveniente de concatenar archivos si el número de archivos es bastante grande.

Igor
fuente
1
Pero esto no responde a la pregunta del OP.
kvantour
-3

Primero creé cada archivo: echo 'information'> file1.txt para cada archivo [123] .txt.

Luego imprimí cada archivo para asegurarme de que la información era correcta: ¿archivo de cola? .Txt

Luego hice esto: archivo de cola? .Txt >> Mainfile.txt. Esto creó Mainfile.txt para almacenar la información en cada archivo en un archivo principal.

cat Mainfile.txt confirmó que estaba bien.

==> file1.txt <== bluemoongoodbeer

==> file2.txt <== awesomepossum

==> file3.txt <== hownowbrowncow

John Ray
fuente