
Quiero combinar miles de pequeños archivos de texto en un archivo de texto grande. Los tengo en directorios con la estructura: timestamp1/status.txt
. Por ejemplo: 20130430133144/status.txt
. Hasta ahora sé que
cat */* > bigtextfile.txt
Funciona para pequeñas cantidades de archivos. ¿Pero funcionará para números más altos? Me pregunto si cat
recopilaremos el contenido de todos los archivos y luego intentaremos guardarlos en el archivo bigtextfile
. De lo contrario, supongo que debe haber otra forma de hacerlo, como buscar un archivo, agregarlo bigtextfile
, luego buscar otro y así sucesivamente.
Respuesta1
En:
cat */* > bigtextfile.txt
El shell se expandirá */*
a la lista ordenada de archivos coincidentes (no ocultos) y se ejecutará cat
con esas rutas de archivo como argumentos.
cat
Abrirá cada archivo por turno y escribirá en su salida estándar lo que lee del archivo. cat
no contendrá más de un búfer lleno de datos (algo así como unos pocos kilobytes) a la vez en la memoria.
Sin embargo, un problema que puede encontrar es que la lista de argumentos cat
es tan grande que alcanza el límite del tamaño de argumentos de la execve()
llamada al sistema. Por lo tanto, es posible que deba dividir esa lista de archivos y ejecutarla cat
varias veces.
Podrías usar xargs
para eso (aquí con GNU o BSD para las opciones xargs
no estándar ):-r
-0
printf '%s\0' */* | xargs -r0 cat -- > big-file.txt
(debido a que printf
está integrado en el shell, no pasa por la execve
llamada al sistema, por lo que no pasa por su límite).
O find
haga la lista de archivos y ejecute tantos comandos cat como sea necesario:
find . -mindepth 2 -maxdepth 2 -type f -exec cat {} + > big-file.txt
O portátil:
find . -path './*/*' -prune -type f -exec cat {} + > big-file.txt
(Sin embargo, tenga en cuenta que, al contrario de */*
, incluirá archivos ocultos (y archivos en directorios ocultos), no buscará archivos en enlaces simbólicos a directorios y la lista de archivos no se ordenará).
Si tiene una versión reciente de Linux, puede eliminar el límite del tamaño de los argumentos haciendo:
ulimit -s unlimited
cat -- */* > big-file.txt
Con zsh
, también puedes usar zargs
:
autoload zargs
zargs -- */* -- cat > big-file.txt
Con ksh93
, puedes usar command -x
:
command -x cat -- */* > big-file.txt
Todos hacen lo mismo, dividen la lista de archivos y ejecutan tantos cat
comandos como sean necesarios.
Nuevamente ksh93
, puedes superar el execve()
límite usando el cat
comando incorporado:
command /opt/ast/bin/cat -- */* > big-file.txt
Respuesta2
No, cat
no almacenará en búfer todos los archivos antes de que comience a escribirse.
Sin embargo, si tiene una gran cantidad de archivos, puede tener un problema con la cantidad de argumentos pasados cat
. De forma predeterminada, el kernel de Linux solo permite pasar una cantidad fija de argumentos a cualquier programa (no recuerdo cómo obtener el valor, pero en la mayoría de los casos son unos pocos miles).
Para resolver este problema, puedes hacer algo como esto:
find -mindepth 2 -maxdepth 2 -type f -exec cat {} \; > bigtextfile.txt
Básicamente, esto llamará cat
por separado a todos y cada uno de los archivos encontrados por find
.
Respuesta3
Si la cantidad de archivos es demasiado grande, se */*
obtendrá una lista de argumentos demasiado grande. Si es así, algo parecido servirá:
find . -name "*.txt" | xargs cat > outfile
(La idea es utilizar find
para recoger los nombres de los archivos y convertirlos en una secuencia; xargs
corta esta secuencia en partes manejables para entregárselas cat
, lo que las concatena en la secuencia de salida de xargs
y que va a outfile
).