¿Cómo mostrar ciertas líneas de un archivo de texto en Linux?

86

Supongo que todos conocen las útiles utilidades de línea cmd de Linux heady tail. headle permite imprimir las primeras X líneas de un archivo, tailhace lo mismo pero imprime el final del archivo. ¿Cuál es un buen comando para imprimir el medio de un archivo? algo así como middle --start 10000000 --count 20(imprima las líneas 10'000'000th hasta th 10'000'010th).

Estoy buscando algo que se ocupe de archivos grandes de manera eficiente. Lo intenté tail -n 10000000 | head 10y es horriblemente lento.

Booz
fuente
55
posible duplicado de serverfault.com/questions/101900/…
Kyle Brandt

Respuestas:

112
sed -n '10000000,10000020p' filename

Es posible que pueda acelerar eso un poco así:

sed -n '10000000,10000020p; 10000021q' filename

En esos comandos, la opción -nhace sedque "se suprima la impresión automática del espacio del patrón". El pcomando "imprime [s] el espacio del patrón actual" y el qcomando "Salga [s] inmediatamente de la secuencia de comandos sed sin procesar más entradas ..." Las comillas son de la sed manpágina .

Por cierto, tu comando

tail -n 10000000 filename | head 10

comienza en la línea número diez millones desde el final del archivo, mientras que su comando "medio" parece comenzar en el número diez millones desde el principio, lo que sería equivalente a:

head -n 10000010 filename | tail 10

El problema es que para los archivos sin clasificar con líneas de longitud variable, cualquier proceso tendrá que pasar por el archivo contando nuevas líneas. No hay forma de atajar eso.

Sin embargo, si el archivo está ordenado (un archivo de registro con marcas de tiempo, por ejemplo) o tiene líneas de longitud fija, puede buscar en el archivo en función de una posición de byte. En el ejemplo del archivo de registro, podría hacer una búsqueda binaria varias veces como lo hace mi script de Python aquí *. En el caso del archivo de longitud de registro fija, es realmente fácil. Solo busca linelength * linecountpersonajes en el archivo.

* Sigo teniendo la intención de publicar otra actualización de ese script. Tal vez lo logre uno de estos días.

Dennis Williamson
fuente
Aquí está una sedversión de Charles middlefunción: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Manejará múltiples argumentos de archivo, nombres de archivo con espacios, etc. Varios archivos se procesan juntos como si hubieran sido capturados de la misma manera que lo sedhace normalmente (de modo que el medio 1000 100 archivo1 archivo2 abarcaría el final del primer archivo hasta el principio del segundo si el primero tiene menos de 1100 líneas).
Dennis Williamson el
La función en mi comentario anterior se puede llamar con un parámetro de nombre de archivo: middle startline count filenameo múltiples nombres de archivo: middle startline count file1 file2 file3o con redirección: middle startline count < filenameo en una tubería: some_command | conteo de línea de inicio medio` ocat file* | middle startline count
Dennis Williamson
¿No debería ser el 'en su comando sed'? No puedo hacer que funcione con el backtick, pero funciona bien con la comilla simple.
Ian Hunter
@beanland: Sí, es un error tipográfico. Lo he arreglado Gracias.
Dennis Williamson
1
@kev: agregué alguna explicación a mi respuesta.
Dennis Williamson
28

Descubrí el siguiente uso de sed

sed -n '10000000,+20p'  filename

Espero que sea útil para alguien!

Dox
fuente
Es bueno saber que hay una alternativa al argumento de la última línea propuesta por Dennis: un recuento de líneas como segundo sed -nargumento que lo hace bastante legible.
user3123159
Un ejemplo de uso: extract_lines(){sed -n "$1,+$2p" <file>}que escribe en stdout.
user3123159
4

¡Es mi primera vez publicando aquí! De todos modos, este es fácil. Digamos que desea extraer la línea 8872 de su archivo llamado file.txt. Así es como lo haces:

cat -n file.txt | grep '^ * 8872'

Ahora la pregunta es encontrar 20 líneas después de esto. Para lograr esto haces

cat -n file.txt | grep -A 20 '^ * 8872'

Para ver líneas alrededor o antes, vea las banderas -B y -C en el manual grep.

Dennis
fuente
Si bien eso es técnicamente correcto y una forma interesante de hacerlo en un archivo de tamaño razonable, tengo curiosidad acerca de su eficacia al trabajar con archivos del tamaño que el póster pregunta.
Jenny D
Múltiples líneas: cat -n file.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Jeffrey Knight el
cat -n file.txt | grep '^ *1'produce todas las líneas que tienen 1 en su lado derecho. ¿Cómo dar salida a la línea 1 con esta técnica? Sé que puedo encabezar -n 1 .... pero ¿cómo usar grep?
Sean87
1

La respuesta de Dennis es el camino a seguir. Pero usando solo cabeza y cola, debajo de bash:

middle () {head -n $ [$ 1 + $ 2] | cola -n $ 2; }

Esto escanea las primeras líneas de $ 1 + $ 2 dos veces, por lo que es mucho peor que la respuesta de Dennis. Pero no necesitas recordar todas esas letras sed para usarlo ...

Charles Stewart
fuente
El uso $[...]está en desuso, al menos en Bash. Además, te falta un parámetro de archivo.
Dennis Williamson el
@Dennis: Ningún parámetro que falta: que está destinado a utilizar esto en la entrada estándar, de acuerdo middle 10 10 < /var/log/auth.log.
Charles Stewart
1

Use el siguiente comando para obtener el rango particular de líneas

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Aquí debug.log es mi archivo que consta de una falta de líneas y solía imprimir las líneas del número de línea 1220974 al 1513793 en un archivo test.log. Espero que sea útil para capturar el rango de líneas.

newbie13
fuente
La misma respuesta que serverfault.com/a/641252/140016 . Voto negativo
Deer Hunter
No es la misma respuesta. Esto debería ser más rápido para archivos grandes, ya que en realidad se cancela después de imprimir la última línea en lugar de continuar escaneando el archivo.
fóbico
0

Una versión ruby ​​oneliner.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Puede ser útil para alguien. Las soluciones con 'sed' proporcionadas por Dennis y Dox son muy buenas, incluso porque parece más rápido.

Shardan
fuente
0

Puedes usar 'nl'.

nl filename | grep <line_num>
Ajay
fuente
0

Por ejemplo, este awk imprimirá líneas entre 20 y 40

awk '{if ((NR> 20) && (NR <40)) print $ 0}' / etc / passwd

Hrvoje Špoljar
fuente
0

Si conoce los números de línea, digamos que desea obtener las líneas 1, 3 y 5 de un archivo, diga / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
Dagelf
fuente
0

Perl es rey:

perl -ne 'print if ($. == 10000000 .. $. == 10000020)' filename
Peter V. Mørch
fuente