Dado: Un gran texto-archivo de datos (por ejemplo, el formato CSV) con un ‘especial’ primera línea (por ejemplo, nombres de campo).

Quería: Un equivalente de la coreutils split -l comando, pero con el requisito adicional de que la línea de encabezado del archivo original aparecen al principio de cada una de las piezas resultantes.

Supongo que algún brebaje de split y head va a hacer el truco?

  • Parece razonable que alguien debería agregar que como una característica integrada de split, ¿no?
  • Probablemente el mayor factor en contra de que esto se convierta en un built-in es que generalmente reconstruir un archivo dividido por hacer cat a b c > reconstructed. Extrañas líneas en el archivo de la normal de enfoque de la reconstrucción no se reproduce el archivo original.
  • Eso es lo que la próxima (no) «unsplit --remove-header» utilidad es para! Pero en serio, split, si fuera a tener un «repetir el encabezado de» opción, debe todavía defecto a su comportamiento actual. Que solo uso encabezado cosas si realmente la quería.
  • Sí, creo que --keep-first N sería una buena opción para split que podría ser útil tanto en línea y en modo byte
  • Creo que es una buena idea, absolutamente muy útil para dividir un archivo para su distribución en lugar de la reconstrucción. Es uno de esos «tan simple, ¿cómo es que no hay todavía» características de una utilidad Unix tan viejo, que soy escéptico de que las «personas a cargo» no han rechazado las propuestas anteriores para hacer esta funcionalidad exacta, por alguna razón u otra.
  • Sin duda.
  • Creo que el razonamiento podría ser simplemente debido a la especificación POSIX para dividir no tener esa opción. Sólo puedo imaginar lo difícil que es para agregar funcionalidad a los estándares POSIX! opengroup.org/onlinepubs/009695399/utilities/split.html
  • He actualizado mi respuesta con una característica interesante que GNU split proporciona.

InformationsquelleAutor Arkady | 2009-09-11

12 Comentarios

  1. 46

    Este es robhruska del script 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. He cambiado algunos de los nombres de los archivos para hacerlos un poco más significativos. Me rompió en varias líneas sólo para hacer más fácil la lectura.

    Si quieres conseguir la suposición, puede utilizar mktemp o tempfile para crear un nombre temporal en lugar de utilizar un código rígido uno.

    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_

    Roto para mejorar la 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 --filter se especifica, split se ejecuta el comando (una función, en este caso, que debe ser exportado) para cada archivo de salida y establece la variable FILE, en el comando del medio ambiente, el nombre del archivo.

    Un filtro de secuencia de comandos o la función podría hacer cualquier manipulación que quería a la salida de contenido o incluso el nombre de archivo. Un ejemplo de esto último podría ser la salida a un fijo de nombre de archivo en una variable de directorio: > "$FILE/data.dat" por ejemplo.

    • Esto sin duda va a trabajar. Yo sólo estaba esperando que por alguna mancha de one-liner como for $part in (split -l 1000 myfile); cat <(head -n1 myfile) $part > myfile.$part; done
    • Que no puede trabajar porque split, de la necesidad, no de salida en stdout.
    • split puede la salida de la nombres de los archivos a stdout, aunque (siempre que estemos hablando de lo split debe a hacer 🙂
    • Estás en lo correcto. Que podría ser mano. Lo siento, no confundas tu one-liner.
    • Mac OS X 10.10.4 trabajado con el original fragmento de código, pero no el de una línea de GNU versión split.
    • Tenga en cuenta que las utilidades de GNU están disponibles para mac OS X. el Uso de Homebrew, por ejemplo.
    • stackoverflow.com/a/30005262/1014710 tiene las instrucciones para el Homebrew GNU coreutils instrucciones

  2. 12

    Usted podría utilizar el nuevo –la funcionalidad de filtro en GNU coreutils split >= 8.13 (2011):

    tail -n +2 FILE.in |
    split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'
    • Me gusta el one-liner versión. Sólo para hacerlo más genérico para bash, yo lo hice: tail -n +2 FILE.in | split -d --lines 50 - --filter='bash -c "{ head -n1 ${FILE%.*}; cat; } > $FILE"' FILE.in.x
  3. 10

    Puede utilizar [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 sector.
    No requiere archivos temp y se puede poner en una sola línea.

    • Upvoting por enseñarme algo nuevo, pero si voy a escribir un pequeño script, que bien podría hacerlo en Perl o Python 🙂
  4. 4

    Soy un novato cuando se trata de Bash-fu, pero yo era capaz de inventar este dos de comando de la monstruosidad. Estoy seguro de que hay más soluciones 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 suponiendo que el archivo de entrada es file.txt, usted no está usando el prefix argumento para split, y estás trabajando en un directorio que no tiene ningún otro los archivos que comienzan con split‘s por defecto xa* formato de salida. Además, reemplace el ‘4’ con su división deseado tamaño de la línea.

  5. 3

    Esto va a separar el csv en piezas de 999 líneas, con el encabezado en la parte superior de cada uno

    cat bigFile.csv | parallel --header : --pipe -N999 'cat >file_{#}.csv'

    Basado en Ole Tange la respuesta.
    (re Ole respuesta: Usted no puede utilizar la línea de contar con pipepart)

    • Simple, directo al punto y funciona.
    • por favor notar que, si tenemos en cuenta que la fila de encabezado en cada archivo, a continuación, cada uno más pequeño que el archivo va a tener 1000 filas en esta solución.
    • Por lo que yo uso 999 🙂
  6. 2

    Esta es una versión más robusta de Denis Williamson‘s de la secuencia de comandos. El script crea un montón de archivos temporales, y sería una lástima si se quedaron en torno a si la ejecución fue incompleta. Por lo tanto, vamos a añadir la señal de la captura (ver http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html y, a continuación,http://tldp.org/LDP/abs/html/debugging.html) y quitar nuestros archivos temporales; esta es una de las mejores prácticas 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

    Reemplazar ’13’, con lo que el código de retorno que desee. Oh, y probablemente debería ser el uso de mktemp de todos modos (como algunos ya lo han sugerido), así que adelante y eliminar ‘tmp_file» de la rm en la trampa de la línea. Ver la señal de el hombre de la página para más señales de captura.

  7. 1

    Yo no estoy seguro acerca de las reglas de la copia de secuencias de comandos directamente desde los sitios de otras personas, pero Geekology tiene un buen guión para hacer lo que quieres, con un par de comentarios de confirmar que funciona. Asegúrese de hacer tail -n +2 como se señaló en un comentario cerca de la parte inferior.

  8. 1

    Me gustó el awk versión de marco, adoptado a partir de este simplificada de una línea donde usted puede especificar fácilmente la división fracción 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
    • Me gusta esta solución, sin embargo, es limitado a sólo dos dividir archivos
    • Si te gusta no es el upvote característica para ella 😉 puede ser fácilmente ajustado para más archivos, pero sí que no es tan flexible como el de split -l
    • «uno forro» …pshh
  9. 1

    Me gustó mucho Rob y Dennis versiones, tanto que quería mejorar.

    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:

    1. in_file es el archivo con el argumento de que desea dividir el mantenimiento de los encabezados de
    2. Uso awk en lugar de tail debido a awk tener un mejor rendimiento
    3. dividido en 100.000 línea de los archivos en lugar de 4
    4. Split nombre de archivo será el nombre del archivo de entrada con un guión bajo y números (hasta 99999 – de la «-d -5» split argumento)
    5. Uso mktemp para manejar en forma segura los archivos temporales
    6. Uso único head | cat línea en lugar de dos líneas
  10. 1

    Uso de GNU Paralelo:

    parallel -a bigfile.csv --header : --pipepart 'cat > {#}'

    Si usted necesita para ejecutar un comando en cada una de las partes, luego de GNU Paralelo puede ayudar a hacer eso, también:

    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 quieres dividir en 2 partes por el núcleo de la 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 quieres dividir en 10 MB bloques:

    parallel --block 10M -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
  11. 1

    A continuación es un 4 recubrimiento que puede ser utilizado para preservar el csv de encabezado (utilizando : la cabeza, los de split, find, grep, xargs, y sed)

     
    csvheader=`head -1 bigfile.csv` 
    split -d -l10000 bigfile.csv smallfile_ 
    encontrar .|grep smallfile_ | xargs sed-i "1s/^/$csvheader\n/" 
    sed-i '1d' smallfile_00 
    
    

    Explicación:

    • Captura de la cabecera a una variable denominada csvheader
    • Dividir el bigfile en un número de archivos pequeños (con prefijo smallfile_)
    • Encontrar todos smallfiles e inserte el csvheader en la PRIMERA línea el uso de xargs y sed-i. Tenga en cuenta que usted necesita para usar sed dentro de «comillas dobles» con el fin de utilizar variables.
    • El primer archivo llamado smallfile_00 ahora tienen redundante cabeceras de las líneas 1 y 2 (a partir de los datos originales, así como de la sed insertar encabezado en el paso 3). Podemos eliminar la redundancia del encabezado con sed-i ‘1d’ comando.
  12. 1

    Inspirado por @Arkady comentario en una sola línea.

    • MIARCHIVO variable simplemente para reducir repetitivo
    • split no mostrar el nombre de archivo, pero la --additional-suffix opción nos permite controlar fácilmente qué esperar
    • eliminación de archivos intermedios a través de rm $part (se asume que no hay archivos con el mismo sufijo)

    MYFILE=mycsv.csv && for part in $(split -n4 --additional-suffix=foo $MYFILE; ls *foo); do cat <(head -n1 $MYFILE) $part > $MYFILE.$part; rm $part; done

    Evidencia:

    -rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xaafoo
    -rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xabfoo
    -rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xacfoo
    -rw-rw-r--  1 ec2-user ec2-user  32040110 Jun  1 23:18 mycsv.csv.xadfoo

    y, por supuesto, head -2 *foo para ver el encabezado se agrega.

Dejar respuesta

Please enter your comment!
Please enter your name here