Tengo un archivo con -|
como delimitador después de cada sección ... necesito crear archivos separados 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
-|
Respuestas:
Un trazador de líneas, sin programación. (excepto la expresión regular, etc.)
csplit --digits=2 --quiet --prefix=outfile infile "/-|/+1" "{*}"
probado en:
csplit (GNU coreutils) 8.30
Notas sobre el uso en Apple Mac
"Para los usuarios de OS X, tenga en cuenta que la versión
csplit
que viene con el sistema operativo no funciona. Querrá la versión en coreutils (instalable a través de Homebrew), que se llamagcsplit
". - @Danial"Solo para agregar, puedes hacer que la versión para OS X funcione (al menos con High Sierra). Solo necesitas modificar un poco los argumentos
csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Las características que no parecen funcionar son"{*}"
, tuve que ser específico en el número de separadores, y es necesario agregarlo-k
para evitar que se eliminen todos los archivos de salida si no puede encontrar un separador final. Además, si lo desea--digits
, debe usar-n
en su lugar ". - @Pebblfuente
--elide-empty-files
, de lo contrario, habrá un archivo vacío al final.--digits=2
controla la cantidad de dígitos utilizados para numerar los archivos de salida (2 es el valor predeterminado para mí, por lo que no es necesario).--quiet
suprime la salida (tampoco es realmente necesario o solicitado aquí).--prefix
especifica el prefijo de los archivos de salida (el predeterminado es xx). Por lo tanto, puede omitir todos los parámetros y obtendrá archivos de salida comoxx12
.csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Las características que no parecen funcionar son"{*}"
, tuve que ser específico en el número de separadores y necesitaba agregar-k
para evitar que se borren todos los archivos de salida si no puede encontrar un separador final. Además, si lo desea--digits
, debe usar-n
en su lugar.awk '{f="file" NR; print $0 " -|"> f}' RS='-\\|' input-file
Explicación (editada):
RS
es el separador de registros, y esta solución usa una extensión gnu awk que le permite tener más de un carácter.NR
es el número de registro.La declaración de impresión imprime un registro seguido de
" -|"
en un archivo que contiene el número de registro en su nombre.fuente
RS
es el separador de registros, y esta solución usa una extensión gnu awk que le permite tener más de un carácter. NR es el número de registro. La declaración de impresión imprime un registro seguido de "- |" en un archivo que contiene el número de registro en su nombre.>
, por lo que puede construirlo como desee. por ejemplo,print $0 "-|" > "file" NR ".txt"
awk '{f="file" NR; print $0 " -|" > f}'
Debian lo ha hecho
csplit
, pero no sé si eso es común a todas / la mayoría / las demás distribuciones. Sin embargo, si no es así, no debería ser demasiado difícil rastrear la fuente y compilarla ...fuente
csplit
está en POSIX, esperaría que esté disponible esencialmente en todos los sistemas similares a Unix.csplit --prefix gold-data - "/^==*$/
vscsplit --prefix gold-data - "/^=+$/
. Al menos GNU grep tiene-e
.Resolví un problema ligeramente diferente, donde el archivo contiene una línea con el nombre donde debe ir el texto que sigue. Este código de 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 "$_"}; }
fuente
while
ciclo final . Si encuentra lamff
expresión regular al principio de la línea, usa el resto de la línea como el nombre del archivo para abrir y comenzar a escribir. Nunca cierra nada, por lo que se quedará sin identificadores de archivos después de unas pocas docenas.while
ciclo final y cambiando awhile (<>)
El siguiente comando funciona para mí. Espero eso ayude.
awk 'BEGIN{file = 0; filename = "output_" file ".txt"} /-|/ {getline; file ++; filename = "output_" file ".txt"} {print $0 > filename}' input
fuente
close
el archivo anterior cuando inicia uno nuevo.if (file) close(filename);
antes de asignar un nuevofilename
valor.; close(filename)
. Realmente simple, pero realmente corrige el ejemplo anteriorTambién puedes usar awk. No estoy muy familiarizado con awk, pero lo siguiente pareció funcionar para mí. Generaba part1.txt, part2.txt, part3.txt y part4.txt. Tenga en cuenta que el último archivo partn.txt que genera está vacío. No estoy seguro de cómo solucionarlo, pero estoy seguro de que podría hacerse con algunos ajustes. ¿Alguna sugerencia de alguien?
archivo awk_pattern:
BEGIN{ fn = "part1.txt"; n = 1 } { print > fn if (substr($0,1,2) == "-|") { close (fn) n++ fn = "part" n ".txt" } }
comando bash:
awk -f awk_pattern input.file
fuente
Aquí hay una secuencia de comandos de Python 3 que divide un archivo en varios archivos según un nombre de archivo proporcionado por los delimitadores. Archivo de entrada de ejemplo:
# 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á el guión:
#!/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)
Finalmente, así es como se ejecuta:
$ python3 script.py -i input-file.txt -o ./output-folder/
fuente
Úselo
csplit
si lo tiene.Si no lo tiene, pero tiene Python ... no use Perl.
Lectura perezosa del archivo
Es posible que su archivo sea demasiado grande para guardarlo en la memoria de una vez; es preferible leer línea por línea. Suponga que el archivo de entrada se llama "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"
fuente
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 la versión formateada:
#!/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; )
fuente
cat
es inútil .cat
en un solo archivo en cada situación. Hay una pregunta de Stack Overflow con más discusión (aunque la respuesta aceptada es en mi humilde opinión); stackoverflow.com/questions/11710552/useless-use-of-catcsplit
, una solución Awk probablemente sea mucho más preferible a esta solución (incluso si tuviera que solucionar los problemas informados por shellcheck.net, etc., tenga en cuenta que actualmente no encuentra todos los errores en esto).cat
sigue siendo inútil, y el resto del guión podría simplificarse y corregirse bastante; pero seguirá siendo lento. Ver, por ejemplo, stackoverflow.com/questions/13762625/…Este es el tipo de problema para el que escribí context-split: 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
fuente
csplit
utilidad estándar . Vea la respuesta de @ richard .Aquí hay un código de Perl que hará 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);
fuente