Tengo un archivo con -| como delimitador después de cada sección…necesidad de crear archivos independientes para cada sección usando unix.

ejemplo de archivo de entrada

wertretr
ewretrtret
1212132323
000232
-|
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|

Resultado esperado en el Archivo 1

wertretr
ewretrtret
1212132323
000232
-|

Resultado esperado en el Archivo 2

ereteertetet
232434234
erewesdfsfsfs
0234342343
-|

Resultado esperado en el Archivo 3

jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
  • Estás escribiendo un programa o quieres hacer esto mediante utilidades de línea de comandos?
  • el uso de utilidades de línea de comandos será preferible..
  • Usted podría utilizar awk, sería fácil escribir un 3 o 4 de la línea de programa que lo haga. Por desgracia, yo estoy fuera de práctica.
InformationsquelleAutor user1499178 | 2012-07-03

11 Comentarios

  1. 80

    Un forro, sin necesidad de programación. (excepto la regexp etc.)

    csplit --digits=2  --quiet --prefix=outfile infile "/-|/+1" "{*}"
    • +1 – corto: csplit -n2 -s -b outfile infile "/-|/+1" "{*}"
    • Yo lo hice en mucho tiempo, así que la explicación no era necesario.
    • Sugiero agregar --elide-empty-files, de lo contrario habrá un archivo vacío al final.
    • Para los usuarios de OS X, tenga en cuenta que la versión de csplit que viene con el sistema operativo no funciona. Usted querrá que la versión en coreutils (instalable a través de Homebrew), que se llama gcsplit.
    • Sólo para los que se preguntan qué los parámetros decir: --digits=2 controla el número de dígitos utilizados para el número de archivos de salida (2 es el valor predeterminado para mí, así que no es necesario). --quiet suprime la salida (también no es realmente necesario o solicitado por aquí). --prefix especifica el prefijo de los archivos de salida (el valor predeterminado es xx). Así que usted puede saltar todos los parámetros y obtener los archivos de salida como xx12.
    • Solo para agregar, usted puede conseguir la versión para OS X funcione (al menos en lo Alto de la Sierra). Usted sólo necesita ajustar la args un poco csplit -k -f=outfile infile "/-\|/+1" "{3}". Características que no parecen funcionar son los "{*}", tenía que ser específico sobre el número de separadores, y sea necesario para agregar -k para evitar la eliminación de todas las outfiles si no puede encontrar un final separador. También si quieres --digits, usted necesita usar -n lugar.

  2. 29
    awk '{print $0 " -|"> "file" NR}' RS='-\|'  input-file

    Explicación (editado):

    RS es el separador de registros, y esta solución utiliza un gnu awk extensión que le permite ser más de un carácter. NR es el número de registro.

    La instrucción print imprime un registro seguido por " -|" en un archivo que contiene el número de registro en su nombre.

    • Cómo funciona esto en realidad archivos de gran tamaño (> 3 GB)? Yo no estoy familiarizado con awk.
    • Podría por favor explicar las diferentes partes? ¿Qué es RS? ¿Qué es NR?
    • RS es el separador de registros, y esta solución utiliza un gnu awk extensión que le permite ser más de un carácter. NR es el número de registro. La instrucción print imprime un registro seguido de » -|» en un archivo que contiene el número de registro en su nombre.
    • Esto debería funcionar bien con archivos de gran tamaño. awk procesa el archivo de registros de uno en uno, de modo que sólo se lee tanto como necesita. Si la primera aparición de el separador de registro muestra hasta muy tarde en el archivo, puede ser una memoria de la crisis desde un registro completo debe caber en la memoria. También, tenga en cuenta que el uso de más de un carácter en la RS no es un estándar de awk, pero esto va a funcionar en gnu awk.
    • Para mí es dividir 3.3 GB en 31.728 s
    • Cómo personalizar la extensión de archivo (por ejemplo,file1.txt, file2.txt, etc)?
    • El nombre de archivo es sólo la cadena en el lado derecho de la >, así que usted puede construir como más te guste. por ejemplo, print $0 "-|" > "file" NR ".txt"

  3. 7

    Debian ha csplit, pero no sé si eso es común a todos/la mayoría de las otras distribuciones. Si no, sin embargo, no debería ser demasiado difícil de rastrear la fuente y compilarlo…

    • Estoy de acuerdo. Mi Debian cuadro dice que csplit es parte de gnu coreutils. Por lo que cualquier sistema operativo Gnu, como todas las distribuciones Gnu/Linux se tienen. Wikipedia también menciona «El Single UNIX® Especificación, número 7′ en el csplit página, así que sospecho que tengo.
    • Desde csplit es en POSIX, yo esperaría a estar disponible en básicamente todos los sistemas Unix.
    • Aunque csplit es POISX, el problema (parece hacer una prueba con el sistema Ubuntu sentado en frente de mí) es que no hay ninguna manera obvia de hacer uso de una versión más moderna sintaxis regex. Comparar: csplit --prefix gold-data - "/^==*$/ vs csplit --prefix gold-data - "/^=+$/. Al menos GNU grep ha -e.
  4. 5

    He resuelto un problema un poco diferente, donde el archivo contiene una línea con el nombre que en el texto que sigue debe ir. Este código perl hace el truco para mí:

    #!/path/to/perl -w
    
    #comment the line below for UNIX systems
    use Win32::Clipboard;
    
    # Get command line flags
    
    #print ($#ARGV, "\n");
    if($#ARGV == 0) {
        print STDERR "usage: ncsplit.pl --mff -- filename.txt [...] \n\nNote that no space is allowed between the '--' and the related parameter.\n\nThe mff is found on a line followed by a filename.  All of the contents of filename.txt are written to that file until another mff is found.\n";
        exit;
    }
    
    # this package sets the ARGV count variable to -1;
    
    use Getopt::Long;
    my $mff = "";
    GetOptions('mff' => $mff);
    
    # set a default $mff variable
    if ($mff eq "") {$mff = "-#-"};
    print ("using file switch=", $mff, "\n\n");
    
    while($_ = shift @ARGV) {
        if(-f "$_") {
        push @filelist, $_;
        } 
    }
    
    # Could be more than one file name on the command line, 
    # but this version throws away the subsequent ones.
    
    $readfile = $filelist[0];
    
    open SOURCEFILE, "<$readfile" or die "File not found...\n\n";
    #print SOURCEFILE;
    
    while (<SOURCEFILE>) {
      /^$mff (.*$)/o;
        $outname = $1;
    #   print $outname;
    #   print "right is: $1 \n";
    
    if (/^$mff /) {
    
        open OUTFILE, ">$outname" ;
        print "opened $outname\n";
        }
        else {print OUTFILE "$_"};
      }
    • ¿Puede por favor explicar por qué este código funciona? Tengo una situación similar a lo que he descrito aquí de que la salida de los nombres de archivo están incrustadas en el archivo. Pero yo no soy regular de perl de usuario así que no puedo hacer sentido de este código.
    • El real de la carne de vacuno en la final while bucle. Si se encuentra el mff regex al comienzo de la línea, utiliza el resto de la línea como el nombre de archivo para abrir y empezar a escribir. Nunca se cierra nada, de manera que se ejecute fuera de los identificadores de archivo después de un par de docenas.
    • La secuencia de comandos en realidad iba a ser mejorado mediante la eliminación de la mayor parte de el código antes de la final while bucle y el cambio de while (<>)
  5. 3

    El siguiente comando funciona para mí. Espero que ayude.

    awk 'BEGIN{file = 0; filename = "output_" file ".txt"}
        /-|/{getline; file ++; filename = "output_" file ".txt"}
        {print $0 > filename}' input
    • Este se ejecute fuera de los identificadores de archivo normalmente después de un par de docenas de archivos. La solución es explícitamente close el antiguo archivo al iniciar una nueva.
    • ¿cómo se puede cerrar (principiante awk pregunta). Se puede proporcionar una actualización de ejemplo?
    • Este cuadro es probablemente demasiado pequeño para cualquier ejemplo útil pero básicamente if (file) close(filename); antes de asignar un nuevo filename valor.
    • aah encontrado la manera de cerrar: ; close(filename). Muy simple, pero realmente corrige el ejemplo de arriba
    • Gracias @tripleee para el rápido y útil explicación 🙂
    • Revierte la edición, ya que proporcionó un roto la secuencia de comandos. Importantes cambios a otras las respuestas de las personas probablemente debería evitarse — siéntase libre de publicar una nueva respuesta de su propio (tal vez como un wiki) si usted piensa separado respuesta es merecido.

  6. 2

    También puede utilizar awk. No estoy muy familiarizado con awk, pero el siguiente lo hizo parecer a trabajar para mí. Se genera part1.txt, part2.txt, part3.txt y part4.txt. Tenga en cuenta, que la última partn.txt archivo que esto genera es vacío. No estoy seguro de cómo arreglar eso, pero estoy seguro de que se podría hacer con un poco de ajuste. Alguna sugerencia de alguien?

    awk_pattern de archivo:

    BEGIN{ fn = "part1.txt"; n = 1 }
    {
       print > fn
       if (substr($0,1,2) == "-|") {
           close (fn)
           n++
           fn = "part" n ".txt"
       }
    }

    de comandos bash:

    awk -f awk_pattern input.file

  7. 1

    Aquí una secuencia de comandos de Python 3 que divide un archivo en varios archivos basado en un nombre de archivo proporcionada por los delimitadores. Ejemplo de archivo de entrada:

    # Ignored
    
    ######## FILTER BEGIN foo.conf
    This goes in foo.conf.
    ######## FILTER END
    
    # Ignored
    
    ######## FILTER BEGIN bar.conf
    This goes in bar.conf.
    ######## FILTER END

    Aquí está la secuencia de comandos:

    #!/usr/bin/env python3
    
    import os
    import argparse
    
    # global settings
    start_delimiter = '######## FILTER BEGIN'
    end_delimiter = '######## FILTER END'
    
    # parse command line arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--input-file", required=True, help="input filename")
    parser.add_argument("-o", "--output-dir", required=True, help="output directory")
    
    args = parser.parse_args()
    
    # read the input file
    with open(args.input_file, 'r') as input_file:
        input_data = input_file.read()
    
    # iterate through the input data by line
    input_lines = input_data.splitlines()
    while input_lines:
        # discard lines until the next start delimiter
        while input_lines and not input_lines[0].startswith(start_delimiter):
            input_lines.pop(0)
    
        # corner case: no delimiter found and no more lines left
        if not input_lines:
            break
    
        # extract the output filename from the start delimiter
        output_filename = input_lines.pop(0).replace(start_delimiter, "").strip()
        output_path = os.path.join(args.output_dir, output_filename)
    
        # open the output file
        print("extracting file: {0}".format(output_path))
        with open(output_path, 'w') as output_file:
            # while we have lines left and they don't match the end delimiter
            while input_lines and not input_lines[0].startswith(end_delimiter):
                output_file.write("{0}\n".format(input_lines.pop(0)))
    
            # remove end delimiter if present
            if not input_lines:
                input_lines.pop(0)

    Por último aquí está la forma de ejecutar es:

    $ python3 script.py -i input-file.txt -o ./output-folder/
  8. 1

    Uso csplit si la tienes.

    Si no lo hace, pero usted tiene Python… no uso de Perl.

    Perezoso lectura del archivo

    El archivo puede ser demasiado grande para tener en la memoria de todos a la vez – de la lectura línea por línea, puede ser preferible. Suponga que el archivo de entrada se denomina «samplein»:

    $ python3 -c "from itertools import count
    with open('samplein') as file:
        for i in count():
            firstline = next(file, None)
            if firstline is None:
                break
            with open(f'out{i}', 'w') as out:
                out.write(firstline)
                for line in file:
                    out.write(line)
                    if line == '-|\n':
                        break"
    • Esto va a leer todo el archivo en la memoria, lo que significa que va a ser ineficaces o incluso fallar para archivos de gran tamaño.
    • He actualizado la respuesta para manejar archivos muy grandes.
  9. 0
    cat file| ( I=0; echo -n "">file0; while read line; do echo $line >> file$I; if [ "$line" == '-|' ]; then I=$[I+1]; echo -n "" > file$I; fi; done )

    y el formato de la versión:

    #!/bin/bash
    cat FILE | (
      I=0;
      echo -n"">file0;
      while read line; 
      do
        echo $line >> file$I;
        if [ "$line" == '-|' ];
        then I=$[I+1];
          echo -n "" > file$I;
        fi;
      done;
    )
    • Como siempre, el cat es Inútil.
    • La página vinculada se explica con mucho más detalle cómo se puede evitar cat en un solo archivo en cada situación. Hay un Desbordamiento de Pila pregunta con más discusión (aunque la aceptan respuesta es en mi humilde opinión off); stackoverflow.com/questions/11710552/useless-use-of-cat
    • El shell es normalmente muy ineficiente en este tipo de cosas de todos modos; si usted no puede utilizar csplit, un Awk solución es, probablemente, mucho de la copa a esta solución (incluso si usted fuera a solucionar los problemas reportados por la shellcheck.net, etc; tenga en cuenta que actualmente no encontrar todos los errores en este).
    • pero si la tarea es hacerlo sin awk, csplit y etc – sólo bash?
    • A continuación, el cat es inútil, y el resto de la secuencia de comandos podría ser simplificado y se ha corregido una buena oferta; pero va a ser lento. Ver, por ejemplo, stackoverflow.com/questions/13762625/…
  10. 0

    Aquí es un perl de código que va a hacer la cosa

    #!/usr/bin/perl
    open(FI,"file.txt") or die "Input file not found";
    $cur=0;
    open(FO,">res.$cur.txt") or die "Cannot open output file $cur";
    while(<FI>)
    {
        print FO $_;
        if(/^-\|/)
        {
            close(FO);
            $cur++;
            open(FO,">res.$cur.txt") or die "Cannot open output file $cur"
        }
    }
    close(FO);
  11. -1

    Este es el tipo de problema que escribí contexto-split para:
    http://stromberg.dnsalias.org/~strombrg/context-split.html

    $ ./context-split -h
    usage:
    ./context-split [-s separator] [-n name] [-z length]
            -s specifies what regex should separate output files
            -n specifies how output files are named (default: numeric
            -z specifies how long numbered filenames (if any) should be
            -i include line containing separator in output files
            operations are always performed on stdin
    • Uh, parece que es esencialmente un duplicado de la norma csplit utilidad. Consulte @richard respuesta.
    • Esta es realmente la mejor solución de la omi. He tenido que dividir un 98G mysql dump y csplit, por alguna razón, se come toda mi memoria RAM, y es asesinado. Aunque sólo es necesario para que coincida con una línea en el tiempo. No tiene ningún sentido. Esta secuencia de comandos de python funciona mucho mejor y no comer toda la ram.

Dejar respuesta

Please enter your comment!
Please enter your name here