¿Cómo dividir un archivo y mantener la primera línea en cada una de las piezas?
Dado: Un archivo de datos de texto grande (por ejemplo, formato CSV) con una primera línea 'especial' (por ejemplo, nombres de campos).
Se busca: Un equivalente del comando coreutils split -l
, pero con el requisito adicional de que la línea de encabezado del archivo original aparezca al principio de cada una de las piezas resultantes.
Supongo que algún brebaje de split
y head
hará el truco?
9 answers
Este es el script de robhruska limpiado un poco:
tail -n +2 file.txt | split -l 4 - split_
for file in split_*
do
head -n 1 file.txt > tmp_file
cat $file >> tmp_file
mv -f tmp_file $file
done
He quitado wc
, cut
, ls
y echo
en los lugares donde son innecesarios. Cambié algunos de los nombres de archivo para hacerlos un poco más significativos. Lo dividí en varias líneas solo para que fuera más fácil de leer.
Si quieres ponerte elegante, puedes usar mktemp
o tempfile
para crear un nombre de archivo temporal en lugar de usar uno codificado.
Editar
Usando GNU split
es posible hacer esto:
split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }; export -f split_filter; tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_
Desglosado por legibilidad:
split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }
export -f split_filter
tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_
Cuando se especifica --filter
, split
ejecuta el comando (una función en este caso, que debe exportarse) para cada archivo de salida y establece la variable FILE
, en el entorno del comando, en el nombre del archivo.
Un script o función de filtro podría hacer cualquier manipulación que quisiera al contenido de salida o incluso al nombre del archivo. Un ejemplo de esto último podría ser la salida a un nombre de archivo fijo en una variable directorio: > "$FILE/data.dat"
, por ejemplo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-20 17:41:42
Podría usar la nueva funcionalidad filter filter en GNU coreutils split >= 8.13 (2011):
tail -n +2 FILE.in |
split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-08 00:09:05
Puedes usar [mg]awk:
awk 'NR==1{
header=$0;
count=1;
print header > "x_" count;
next
}
!( (NR-1) % 100){
count++;
print header > "x_" count;
}
{
print $0 > "x_" count
}' file
100 es el número de líneas de cada corte. No requiere archivos temporales y se puede poner en una sola línea.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-12 15:25:48
Soy un novato cuando se trata de Bash-fu, pero fui capaz de inventar esta monstruosidad de dos comandos. Estoy seguro de que hay soluciones más elegantes.
$> tail -n +2 file.txt | split -l 4
$> for file in `ls xa*`; do echo "`head -1 file.txt`" > tmp; cat $file >> tmp; mv -f tmp $file; done
Esto es asumiendo que su archivo de entrada es file.txt
, no está utilizando el argumento prefix
para split
, y está trabajando en un directorio que no tiene ningún otro archivo que comience con el formato de salida predeterminado split
xa*
. Además, reemplace el ' 4 ' con el tamaño de línea dividida deseado.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 16:22:53
Esta es una versión más robusta del script de Denis Williamson. El script crea una gran cantidad de archivos temporales, y sería una pena si se dejaran por ahí si la ejecución estaba incompleta. Por lo tanto, vamos a añadir la captura de señal (ver http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html y luego http://tldp.org/LDP/abs/html/debugging.html ) y eliminar nuestros archivos temporales; esta es una buena práctica de todos modos.
trap 'rm split_* tmp_file ; exit 13' SIGINT SIGTERM SIGQUIT
tail -n +2 file.txt | split -l 4 - split_
for file in split_*
do
head -n 1 file.txt > tmp_file
cat $file >> tmp_file
mv -f tmp_file $file
done
Sustitúyase " 13 " por cualquier código de retorno tú quieres. Oh, y probablemente deberías usar mktemp de todos modos (como algunos ya han sugerido), así que adelante y elimina 'tmp_file" de la rm en la línea de trampa. Vea la página man de signal para ver más señales que capturar.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 20:04:39
Nunca estoy seguro de las reglas de copiar scripts directamente desde los sitios de otras personas, pero Geekology tiene un buen script para hacer lo que quieras, con algunos comentarios que confirman que funciona. Asegúrese de hacer tail
-n
+2
como se indica en un comentario cerca de la parte inferior.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 16:13:58
Me gustó la versión awk de marco, adoptó a partir de esto un solo liner simplificado donde puede especificar fácilmente la fracción dividida tan granular como desee:
awk 'NR==1{print $0 > FILENAME ".split1"; print $0 > FILENAME ".split2";} NR>1{if (NR % 10 > 5) print $0 >> FILENAME ".split1"; else print $0 >> FILENAME ".split2"}' file
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-21 17:43:21
Me gustaron mucho las versiones de Rob y Dennis, tanto que quise mejorarlas.
Aquí está mi versión:
in_file=$1
awk '{if (NR!=1) {print}}' $in_file | split -d -a 5 -l 100000 - $in_file"_" # Get all lines except the first, split into 100,000 line chunks
for file in $in_file"_"*
do
tmp_file=$(mktemp $in_file.XXXXXX) # Create a safer temp file
head -n 1 $in_file | cat - $file > $tmp_file # Get header from main file, cat that header with split file contents to temp file
mv -f $tmp_file $file # Overwrite non-header containing file with header-containing file
done
Diferencias:
- in_file es el argumento de archivo que desea dividir manteniendo las cabeceras
- Use
awk
en lugar detail
debido a queawk
tiene un mejor rendimiento - dividir en 100.000 archivos de línea en lugar de 4
- El nombre del archivo dividido será el nombre del archivo de entrada agregado con un guion bajo y números (hasta 99999-desde " - d-a 5" argumento dividido)
- Use mktemp para manejar archivos temporales de forma segura
- Utilice una sola línea
head | cat
en lugar de dos líneas
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-29 21:42:58
Use GNU Parallel:
parallel -a bigfile.csv --header : --pipepart 'cat > {#}'
Si necesita ejecutar un comando en cada una de las partes, GNU Parallel también puede ayudarlo a hacerlo:
parallel -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
parallel -a bigfile.csv --header : --pipepart --fifo my_program_reading_from_fifo {}
parallel -a bigfile.csv --header : --pipepart --cat my_program_reading_from_a_file {}
Si desea dividir en 2 partes por núcleo de CPU (por ejemplo, 24 núcleos = 48 partes de igual tamaño):
parallel --block -2 -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
Si desea dividir en bloques de 10 MB:
parallel --block 10M -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-22 13:42:15