¿Cuál es la mejor manera de sacar un segmento de un archivo de texto?

13

Cuál es una buena forma de extraer, digamos, las líneas 20 a 45 de un archivo de texto enorme. No interactivamente, por supuesto!

Chris Huang-Leaver
fuente

Respuestas:

12

tu podrías intentar:

cat textfile | head -n 45 | tail -n 26

o

cat textfile | awk "20 <= NR && NR <= 45" 

actualizar:

Como señaló Mahomedalid, catno es necesario y es un poco redundante, pero es un comando limpio y legible.

Si catte molesta, una mejor solución sería:

<textfile awk "20 <= NR && NR <= 45"
Stefan
fuente
2
awk NR==20,NR==45 textfilefunciona también y se lee fácilmente.
Ephemient
Me gusta más el uso de stdin, tiene cierta consistencia global con el resto de nix
Stefan
1
La lectura de los argumentos de la línea de comandos también es coherente con otras utilidades de UNIX, y mi punto principal era demostrar el ,operador de rango de awk .
Ephemient
jajaja, me refería a @adam. pero sí, me gusta tu sugerencia
Stefan
Creo que la respuesta de @ ephemient es la mejor aquí. De lo contrario, los comandos son bastante crípticos.
Léo Léopold Hertz 준영
13

Aún más simple:

sed -n '20,45p;45q' < textfile

El indicador -n deshabilita la salida predeterminada. El "20,45" aborda las líneas 20 a 45, inclusive. El comando "p" imprime la línea actual. Y la q se cierra después de imprimir la línea.

dkagedal
fuente
1
+1 agradable, me gusta, pero su línea 20 a 45 :)
Stefan
1
ok ok, lo
edité
La eliminación del qcomando (todo a partir de ;) mejoró el rendimiento para mí al extraer una sola línea 26995107 de un archivo de 27169334 líneas.
Ruslan
6

Esta no es una respuesta, pero no puede publicarla como un comentario.

Mikeserv sugirió otra forma (muy rápida) de hacerlo aquí :

{ head -n 19 >/dev/null; head -n 26; } <infile

Usando el mismo archivo de prueba que aquí y el mismo procedimiento, aquí hay algunos puntos de referencia (líneas de extracción 1000020-1000045):

mikeserv :

{ head -n 1000019 >/dev/null; head -n 26; } <iplist

real    0m0.059s

Stefan :

head iplist -n 1000045 | tail -n 26

real    0m0.054s

Estas son, con mucho, las soluciones más rápidas y las diferencias son insignificantes (para una sola pasada) (intenté con diferentes rangos: un par de líneas, millones de líneas, etc.).

Sin embargo, hacerlo sin la tubería podría ofrecer una ventaja significativa para una aplicación que necesita buscar en múltiples rangos de líneas de manera similar, como:

for  pass in 0 1 2 3 4 5 6 7 8 9
do   printf "pass#$pass:\t"
     head -n99 >&3; head -n1
done <<1000LINES 3>/dev/null
$(seq 1000)
1000LINES

... que imprime ...

pass#0: 100
pass#1: 200
pass#2: 300
pass#3: 400
pass#4: 500
pass#5: 600
pass#6: 700
pass#7: 800
pass#8: 900
pass#9: 1000

... y solo lee el archivo una vez.


Las otras soluciones sed/ awk/ perlleen todo el archivo y, dado que se trata de archivos enormes, no son muy eficientes. Agregué algunas alternativas que exito quit después de la última línea en el rango especificado:

Stefan :

awk "1000020 <= NR && NR <= 1000045" iplist

real    0m2.448s

vs.

awk "NR >= 1000020;NR==1000045{exit}" iplist

real    0m0.243s

dkagedal ( sed):

sed -n 1000020,1000045p iplist

real    0m0.947s

vs.

sed '1,1000019d;1000045q' iplist

real    0m0.143s

Steven D :

perl -ne 'print if 1000020..1000045' iplist

real    0m2.041s

vs.

perl -ne 'print if $. >= 1000020; exit if $. >= 1000045;' iplist

real    0m0.369s
don_crissti
fuente
+1 ¡Creo que esta es la mejor respuesta aquí! Sería bueno saber cuánto tiempo lleva esto awk NR==1000020,NR==1000045 textfileen su sistema.
Léo Léopold Hertz 준영
3
ruby -ne 'print if 20 .. 45' file
usuario1606
fuente
1
un compañero rubí, usted tiene mi voto señor
Stefan
1
Mientras estamos en eso, ¿por qué no python -c 'import fileinput, sys; [sys.stdout.write(line) for nr, line in enumerate(fileinput.input()) if 19 <= nr <= 44]'también? :-P Esto es algo que Ruby, inspirado en Perl, inspirado en awk / sed, puede hacer fácilmente.
Ephemient
2

Como sed y awk ya se tomaron, aquí hay una solución perl:

perl -nle "print if ($. > 19 && $. < 46)" < textfile

O, como se señala en los comentarios:

perl -ne 'print if 20..45' textfile
Steven D
fuente
2
¿Qué pasa con todos esos personajes extra? No es necesario quitar y volver a agregar nuevas líneas, el flip-flop supone una comparación con el número de línea, y el operador de diamantes ejecuta argumentos si se proporcionan. perl -ne'print if 20..45' textfile
Ephemient
1
Agradable. -nle es un poco un reflejo, supongo, en cuanto al resto, no tengo excusa salvo ignorancia.
Steven D