Herramienta bash para obtener la enésima línea de un archivo


606

¿Hay una forma "canónica" de hacer eso? He estado usando lo head -n | tail -1que hace el truco, pero me he estado preguntando si hay una herramienta Bash que extraiga específicamente una línea (o un rango de líneas) de un archivo.

Por "canónico" me refiero a un programa cuya función principal es hacer eso.


10
La "forma de Unix" es encadenar herramientas que hacen bien su trabajo respectivo. Así que creo que ya encontraste un método muy adecuado. Otros métodos incluyen awky, sedy estoy seguro, que a alguien también se le ocurre una línea de Perl o algo así;)
0xC0000022L

3
El doble comando sugiere que la head | tailsolución es subóptima. Se han sugerido otras soluciones más óptimas.
Jonathan Leffler

¿Ha ejecutado algún punto de referencia sobre qué solución es la más rápida para un caso promedio?
Marcin

55
Puntos de referencia (para un rango) en la línea cat X a la línea Y en un archivo enorme en Unix y Linux . (cc @ Marcin, en caso de que aún te preguntes después de más de dos años)
Kevin

66
La head | tailsolución no funciona si consulta una línea que no existe en la entrada: imprimirá la última línea.
jarno

Respuestas:


803

heady la tubería tailserá lenta para un archivo enorme. Sugeriría sedasí:

sed 'NUMq;d' file

¿Dónde NUMestá el número de la línea que desea imprimir? así, por ejemplo, sed '10q;d' filepara imprimir la décima línea de file.

Explicación:

NUMqse cerrará inmediatamente cuando el número de línea sea NUM.

deliminará la línea en lugar de imprimirla; esto se inhibe en la última línea porque qhace que el resto del script se omita al salir.

Si tiene NUMuna variable, querrá usar comillas dobles en lugar de simples:

sed "${NUM}q;d" file

44
Para los que preguntan, esta solución parece cerca de 6 a 9 veces más rápido que el sed -n 'NUMp'y sed 'NUM!d'soluciones propuestas a continuación.
Skippy le Grand Gourou

75
Creo que tail -n+NUM file | head -n1es probable que sea igual o más rápido. Al menos, fue (significativamente) más rápido en mi sistema cuando lo probé con NUM siendo 250000 en un archivo con medio millón de líneas. YMMV, pero realmente no veo por qué lo haría.
rici

2
@rici (revisión del comentario anterior) En Linux (Ubuntu 12.04, Fedora 20), el uso cates más rápido (casi el doble de rápido), pero solo si el archivo aún no se ha almacenado en caché . Una vez que el archivo se almacena en caché , el uso directo del argumento del nombre de archivo es más rápido (aproximadamente 1/3 más rápido), mientras que el catrendimiento se mantiene igual. Curiosamente, en OS X 10.9.3 nada de esto parece hacer ninguna diferencia: cat/ no cat, archivo en caché o no. @anubhava: es un placer.
mklement0

2
@SkippyleGrandGourou: Dada la naturaleza específica de esta optimización , incluso sus rangos de números no tienen sentido como una declaración general . La única conclusión general es esta: (a) esta optimización se puede aplicar de forma segura a todas las entradas, (b) los efectos variarán de ninguno a dramáticos , dependiendo del índice de la línea buscada en relación con el número de líneas generales.
mklement0

17
sed 'NUMqgenerará los primeros NUMarchivos y ;deliminará todos menos la última línea.
anubhava

304
sed -n '2p' < file.txt

imprimirá la segunda línea

sed -n '2011p' < file.txt

Línea 2011

sed -n '10,33p' < file.txt

línea 10 hasta línea 33

sed -n '1p;3p' < file.txt

1ra y 3ra línea

y así...

Para agregar líneas con sed, puede verificar esto:

sed: inserte una línea en una posición determinada


66
@RafaelBarbosa <en este caso no es necesario. Simplemente, es mi preferencia usar redireccionamientos, porque yo solía usar redireccionamientos como sed -n '100p' < <(some_command), por lo tanto, sintaxis universal :). NO es menos efectivo, porque la redirección se realiza con shell cuando se bifurca, así que ... es solo una preferencia ... (y sí, es un carácter más) :)
jm666

1
@ jm666 En realidad son 2 caracteres más largos, ya que normalmente pondría el '<' así como un espacio adicional '' después de <en oposición a solo un espacio si no hubiera utilizado el <:)
rasen58

2
@ rasen58 el espacio también es un personaje? :) / está bien, es broma - tienes razón / :)
jm666

1
@duhaime, por supuesto, si alguien necesita hacer optimizaciones. Pero en mi humilde opinión para los problemas "comunes" está bien y la diferencia es imperceptible. Además, el head/ tailno significa resuelve el sed -n '1p;3p'escenario - aka imprimir más filas no adyacentes ...
jm666

1
@duhaime, por supuesto, la nota es correcta y necesaria. :)
jm666

93

Tengo una situación única en la que puedo comparar las soluciones propuestas en esta página, por lo que escribo esta respuesta como una consolidación de las soluciones propuestas con tiempos de ejecución incluidos para cada una.

Preparar

Tengo un archivo de datos de texto ASCII de 3.261 gigabytes con un par clave-valor por fila. El archivo contiene 3,339,550,320 filas en total y desafía la apertura en cualquier editor que haya probado, incluido mi Vim de acceso. Necesito subconjuntar este archivo para investigar algunos de los valores que descubrí que solo comienzan alrededor de la fila ~ 500,000,000.

Debido a que el archivo tiene tantas filas:

  • Necesito extraer solo un subconjunto de las filas para hacer algo útil con los datos.
  • Leer cada fila que conduzca a los valores que me interesan llevará mucho tiempo.
  • Si la solución lee más allá de las filas que me interesan y continúa leyendo el resto del archivo, perderá tiempo leyendo casi 3 mil millones de filas irrelevantes y tomará 6 veces más de lo necesario.

Mi mejor escenario es una solución que extrae solo una sola línea del archivo sin leer ninguna de las otras filas del archivo, pero no puedo pensar en cómo podría lograr esto en Bash.

A los fines de mi cordura, no voy a tratar de leer las 500,000,000 líneas completas que necesitaría para mi propio problema. En cambio, intentaré extraer la fila 50,000,000 de 3,339,550,320 (lo que significa que leer el archivo completo tomará 60 veces más de lo necesario).

Usaré el timeincorporado para comparar cada comando.

Base

Primero veamos cómo la head tailsolución:

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

La línea de base para la fila 50 millones es 00: 01: 15.321, si hubiera ido directamente a la fila 500 millones probablemente sería ~ 12.5 minutos.

cortar

Dudo de esto, pero vale la pena intentarlo:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

Este tomó 00: 05: 12.156 para ejecutarse, ¡lo cual es mucho más lento que la línea de base! No estoy seguro de si leyó todo el archivo o solo hasta 50 millones de líneas antes de detenerse, pero independientemente de esto, no parece una solución viable para el problema.

AWK

Solo ejecuté la solución con el exitporque no iba a esperar a que se ejecute el archivo completo:

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

Este código se ejecutó en 00: 01: 16.583, que es solo ~ 1 segundo más lento, pero aún no es una mejora en la línea de base. ¡A este ritmo, si se hubiera excluido el comando de salida, probablemente habría tomado alrededor de ~ 76 minutos leer el archivo completo!

Perl

También ejecuté la solución Perl existente:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

Este código se ejecutó en 00: 01: 13.146, que es ~ 2 segundos más rápido que la línea de base. Si lo ejecutara en los 500,000,000 completos, probablemente tomaría ~ 12 minutos.

sed

La respuesta principal en el tablero, aquí está mi resultado:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

Este código se ejecutó en 00: 01: 12.705, que es 3 segundos más rápido que la línea de base y ~ 0.4 segundos más rápido que Perl. Si lo hubiera ejecutado en las 500,000,000 filas completas, probablemente hubiera tomado ~ 12 minutos.

archivo de mapa

Tengo bash 3.1 y, por lo tanto, no puedo probar la solución mapfile.

Conclusión

Parece que, en su mayor parte, es difícil mejorar la head tailsolución. En el mejor de los casos, la sedsolución proporciona un aumento de ~ 3% en la eficiencia.

(porcentajes calculados con la fórmula % = (runtime/baseline - 1) * 100)

Fila 50,000,000

  1. 00: 01: 12.705 (-00: 00: 02.616 = -3.47%) sed
  2. 00: 01: 13.146 (-00: 00: 02.175 = -2.89%) perl
  3. 00: 01: 15.321 (+00: 00: 00.000 = + 0.00%) head|tail
  4. 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%) awk
  5. 00: 05: 12.156 (+00: 03: 56.835 = + 314.43%) cut

Fila 500,000,000

  1. 00: 12: 07.050 (-00: 00: 26.160) sed
  2. 00: 12: 11.460 (-00: 00: 21.750) perl
  3. 00: 12: 33.210 (+00: 00: 00.000) head|tail
  4. 00: 12: 45.830 (+00: 00: 12.620) awk
  5. 00: 52: 01.560 (+00: 40: 31.650) cut

Fila 3,338,559,320

  1. 01: 20: 54.599 (-00: 03: 05.327) sed
  2. 01: 21: 24.045 (-00: 02: 25.227) perl
  3. 01: 23: 49.273 (+00: 00: 00.000) head|tail
  4. 01: 25: 13.548 (+00: 02: 35.735) awk
  5. 05: 47: 23.026 (+04: 24: 26.246) cut

44
Me pregunto cuánto tiempo tardaría en capturar todo el archivo en / dev / null. (¿Qué pasa si esto era sólo un punto de referencia en el disco duro?)
sanmai

Siento una necesidad perversa de inclinarme ante su propiedad de un diccionario de archivos de texto de 3+ conciertos. Cualquiera que sea la razón, esto abarca la textualidad :)
Stabledog

51

Con awkesto es bastante rápido:

awk 'NR == num_line' file

Cuando esto es así, el comportamiento predeterminado de awklas que se realiza: {print $0}.


Versiones alternativas

Si su archivo es enorme, será mejor que exitlea la línea requerida. De esta forma ahorra tiempo de CPU. Vea la comparación de tiempo al final de la respuesta .

awk 'NR == num_line {print; exit}' file

Si desea dar el número de línea de una variable bash, puede usar:

awk 'NR == n' n=$num file
awk -v n=$num 'NR == n' file   # equivalent

Vea cuánto tiempo se ahorra usando exit, especialmente si la línea se encuentra en la primera parte del archivo:

# Let's create a 10M lines file
for ((i=0; i<100000; i++)); do echo "bla bla"; done > 100Klines
for ((i=0; i<100; i++)); do cat 100Klines; done > 10Mlines

$ time awk 'NR == 1234567 {print}' 10Mlines
bla bla

real    0m1.303s
user    0m1.246s
sys 0m0.042s
$ time awk 'NR == 1234567 {print; exit}' 10Mlines
bla bla

real    0m0.198s
user    0m0.178s
sys 0m0.013s

Entonces, la diferencia es 0.198s contra 1.303s, alrededor de 6 veces más rápido.


Este método siempre será más lento porque awk intenta dividir el campo. La sobrecarga de la división del campo se puede reducirawk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
kvantour

El poder real de awk en este método surge cuando se quiere n1 línea de concatenación de archivo1, archivo2 de n2, n3 o archivo3 ... awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3. Con GNU awk esto se puede acelerar usando awk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3.
Kvantour

@kvantour de hecho, el próximo archivo de GNU awk es ideal para tales cosas. ¿Cómo es posible FS=RSevitar la división del campo?
Fedorqui 'SO deja de dañar'

1
FS=RSno evita la división de campos, pero sólo analiza los $ 0 queridos y sólo se asigna un campo porque no hay RSen$0
kvantour

@kvantour He estado haciendo algunas pruebas FS=RSy no vi diferencias en los horarios. ¿Qué tal si hago una pregunta al respecto para que pueda expandirse? ¡Gracias!
fedorqui 'SO deja de dañar'

29

Según mis pruebas, en términos de rendimiento y legibilidad, mi recomendación es:

tail -n+N | head -1

Nes el número de línea que quieres. Por ejemplo, tail -n+7 input.txt | head -1imprimirá la séptima línea del archivo.

tail -n+Nimprimirá todo a partir de la línea Ny head -1hará que se detenga después de una línea.


La alternativa head -N | tail -1es quizás un poco más legible. Por ejemplo, esto imprimirá la 7ma línea:

head -7 input.txt | tail -1

Cuando se trata de rendimiento, no hay mucha diferencia para los tamaños más pequeños, pero será superado por tail | head(desde arriba) cuando los archivos se vuelvan enormes.

sed 'NUMq;d'Es interesante saber cuál es el mejor votado , pero diría que será entendido por menos personas fuera de la caja que la solución cabeza / cola y también es más lento que la cola / cabeza.

En mis pruebas, ambas versiones de colas / cabezas obtuvieron mejores resultados de forma sed 'NUMq;d'consistente. Eso está en línea con los otros puntos de referencia que se publicaron. Es difícil encontrar un caso en el que las colas / cabezas fueran realmente malas. Tampoco es sorprendente, ya que estas son operaciones que cabría esperar que estén muy optimizadas en un sistema Unix moderno.

Para tener una idea sobre las diferencias de rendimiento, estos son los números que obtengo para un archivo enorme (9.3G):

  • tail -n+N | head -1: 3.7 segundos
  • head -N | tail -1: 4.6 segundos
  • sed Nq;d: 18.8 segundos

Los resultados pueden diferir, pero el rendimiento head | taily tail | head, en general, es comparable para entradas más pequeñas, y sedsiempre es más lento en un factor significativo (alrededor de 5 veces más o menos).

Para reproducir mi punto de referencia, puede intentar lo siguiente, pero tenga en cuenta que creará un archivo 9.3G en el directorio de trabajo actual:

#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3

seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
    time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time sed $pos'q;d' $file
done
/bin/rm $file

Aquí está el resultado de una ejecución en mi máquina (ThinkPad X1 Carbon con un SSD y 16G de memoria). Supongo que en la ejecución final todo vendrá del caché, no del disco:

*** head -N | tail -1 ***
500000000

real    0m9,800s
user    0m7,328s
sys     0m4,081s
500000000

real    0m4,231s
user    0m5,415s
sys     0m2,789s
500000000

real    0m4,636s
user    0m5,935s
sys     0m2,684s
-------------------------

*** tail -n+N | head -1 ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000

real    0m6,452s
user    0m3,367s
sys     0m1,498s
500000000

real    0m3,890s
user    0m2,921s
sys     0m0,952s
500000000

real    0m3,763s
user    0m3,004s
sys     0m0,760s
-------------------------

*** sed Nq;d ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000

real    0m23,675s
user    0m21,557s
sys     0m1,523s
500000000

real    0m20,328s
user    0m18,971s
sys     0m1,308s
500000000

real    0m19,835s
user    0m18,830s
sys     0m1,004s

1
¿El rendimiento es diferente entre head | tailvs tail | head? ¿O depende de qué línea se está imprimiendo (principio del archivo vs final del archivo)?
wisbucky

1
@wisbucky No tengo cifras difíciles, pero una desventaja de usar primero la cola seguida de una "cabeza -1" es que necesitas saber la longitud total de antemano. Si no lo sabe, primero deberá contarlo, lo que será una pérdida de rendimiento. Otra desventaja es que es menos intuitivo de usar. Por ejemplo, si tiene el número del 1 al 10 y desea obtener la tercera línea, deberá usar "tail -8 | head -1". Es más propenso a errores que "head -3 | tail -1".
Philipp Claßen

lo siento, debería haber incluido un ejemplo para que quede claro. head -5 | tail -1vs tail -n+5 | head -1. En realidad, encontré otra respuesta que hizo una comparación de prueba y resultó tail | headser más rápida. stackoverflow.com/a/48189289
wisbucky

1
@wisbucky ¡Gracias por mencionarlo! Hice algunas pruebas y tengo que estar de acuerdo en que siempre fue un poco más rápido, independientemente de la posición de la línea de lo que vi. Dado eso, cambié mi respuesta y también incluí el punto de referencia en caso de que alguien quiera reproducirlo.
Philipp Claßen

27

¡Guau, todas las posibilidades!

Prueba esto:

sed -n "${lineNum}p" $file

o uno de estos dependiendo de su versión de Awk:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

( Puede que tenga que probar el comando nawkogawk ).

¿Existe alguna herramienta que solo imprima esa línea en particular? No es una de las herramientas estándar. Sin embargo, sedes probablemente el más cercano y sencillo de usar.



21

Esta pregunta está etiquetada como Bash, aquí está la forma de hacer Bash (≥4): usar mapfilecon la opción -s(omitir) y -n(contar).

Si necesita obtener la línea 42 de un archivo file:

mapfile -s 41 -n 1 ary < file

En este punto, tendrá una matriz arycuyos campos contienen las líneas de file(incluida la nueva línea final), donde hemos omitido las primeras 41 líneas ( -s 41) y nos hemos detenido después de leer una línea ( -n 1). Así que esa es realmente la línea 42. Para imprimirlo:

printf '%s' "${ary[0]}"

Si necesita un rango de líneas, diga el rango 42–666 (inclusive), y diga que no quiere hacer los cálculos usted mismo, e imprímalos en stdout:

mapfile -s $((42-1)) -n $((666-42+1)) ary < file
printf '%s' "${ary[@]}"

Si también necesita procesar estas líneas, no es realmente conveniente almacenar la nueva línea final. En este caso, use la -topción (recortar):

mapfile -t -s $((42-1)) -n $((666-42+1)) ary < file
# do stuff
printf '%s\n' "${ary[@]}"

Puede hacer que una función haga eso por usted:

print_file_range() {
    # $1-$2 is the range of file $3 to be printed to stdout
    local ary
    mapfile -s $(($1-1)) -n $(($2-$1+1)) ary < "$3"
    printf '%s' "${ary[@]}"
}

¡Sin comandos externos, solo Bash incorporado!


11

También puede usar sed print y salir:

sed -n '10{p;q;}' file   # print line 10

66
La -nopción deshabilita la acción predeterminada para imprimir cada línea, como seguramente lo habría descubierto con un vistazo rápido a la página del manual.
tripleee

En GNU, sed todas las sedrespuestas tienen la misma velocidad. Por lo tanto (para GNU sed ) esta es la mejor sedrespuesta, ya que ahorraría tiempo para archivos grandes y valores pequeños de enésima línea .
agc

7

También puedes usar Perl para esto:

perl -wnl -e '$.== NUM && print && exit;' some.file

6

La solución más rápida para archivos grandes es siempre tail | head, siempre que las dos distancias:

  • desde el inicio del archivo hasta la línea de inicio. Vamos a llamarloS
  • la distancia desde la última línea hasta el final del archivo. SeE

son conocidos. Entonces, podríamos usar esto:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

howmany es solo el recuento de líneas requerido.

Algunos detalles más en https://unix.stackexchange.com/a/216614/79743


1
Aclare las unidades de Sy E(es decir, bytes, caracteres o líneas).
agc

6

Todas las respuestas anteriores responden directamente a la pregunta. Pero aquí hay una solución menos directa, pero una idea potencialmente más importante, para provocar el pensamiento.

Como las longitudes de línea son arbitrarias, todos los bytes del archivo antes de la enésima línea deben leerse. Si tiene un archivo enorme o necesita repetir esta tarea muchas veces, y este proceso lleva mucho tiempo, entonces debe pensar seriamente si debería almacenar sus datos de una manera diferente en primer lugar.

La solución real es tener un índice, por ejemplo, al comienzo del archivo, que indique las posiciones donde comienzan las líneas. Puede usar un formato de base de datos o simplemente agregar una tabla al comienzo del archivo. Alternativamente, cree un archivo de índice separado para acompañar su archivo de texto grande.

por ejemplo, puede crear una lista de posiciones de caracteres para líneas nuevas:

awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx

luego lea con tail, que en realidad seekestá directamente en el punto apropiado del archivo.

Por ejemplo, para obtener la línea 1000:

tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1
  • Es posible que esto no funcione con caracteres de 2 bytes / multibyte, ya que awk "reconoce los caracteres" pero la cola no.
  • No he probado esto contra un archivo grande.
  • También vea esta respuesta .
  • Alternativamente, divida su archivo en archivos más pequeños.

5

Como seguimiento a la muy útil respuesta de evaluación comparativa de CaffeineConnoisseur ... Tenía curiosidad por saber qué tan rápido se comparó el método 'mapfile' con otros (ya que no se probó), así que probé una comparación de velocidad rápida y sucia. Tengo bash 4 a mano. Lancé una prueba del método "cola | cabeza" (en lugar de cabeza | cola) mencionado en uno de los comentarios en la respuesta superior mientras estaba en ello, ya que la gente canta sus alabanzas. No tengo nada del tamaño del archivo de prueba utilizado; lo mejor que pude encontrar a corto plazo fue un archivo de pedigrí de 14M (líneas largas separadas por espacios en blanco, un poco menos de 12000 líneas).

Versión corta: mapfile aparece más rápido que el método de corte, pero más lento que todo lo demás, por lo que lo llamaría un fracaso. cola | head, OTOH, parece que podría ser el más rápido, aunque con un archivo de este tamaño la diferencia no es tan sustancial en comparación con sed.

$ time head -11000 [filename] | tail -1
[output redacted]

real    0m0.117s

$ time cut -f11000 -d$'\n' [filename]
[output redacted]

real    0m1.081s

$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]

real    0m0.058s

$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]

real    0m0.085s

$ time sed "11000q;d" [filename]
[output redacted]

real    0m0.031s

$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]

real    0m0.309s

$ time tail -n+11000 [filename] | head -n1
[output redacted]

real    0m0.028s

¡Espero que esto ayude!


4

Usando lo que otros mencionaron, quería que esta fuera una función rápida y elegante en mi shell de bash.

Crea un archivo: ~/.functions

Añádele los contenidos:

getline() { line=$1 sed $line'q;d' $2 }

Luego agregue esto a su ~/.bash_profile:

source ~/.functions

Ahora, cuando abre una nueva ventana de bash, puede llamar a la función de la siguiente manera:

getline 441 myfile.txt


3

Si tienes varias líneas delimitadas por \ n (normalmente una nueva línea). También puedes usar 'cortar':

echo "$data" | cut -f2 -d$'\n'

Obtendrá la segunda línea del archivo. -f3te da la 3ra línea.


1
También se puede usar para mostrar varias líneas: cat FILE | cut -f2,5 -d$'\n'mostrará las líneas 2 y 5 del ARCHIVO. (Pero no preservará el orden.)
Andriy Makukha

2

Para imprimir la enésima línea usando sed con una variable como número de línea:

a=4
sed -e $a'q:d' file

Aquí la bandera '-e' es para agregar script al comando que se ejecutará.


2
Los dos puntos son un error de sintaxis y deben ser un punto y coma.
tripleee

2

Muchas buenas respuestas ya. Yo personalmente voy con awk. Para mayor comodidad, si usa bash, simplemente agregue lo siguiente a su ~/.bash_profile. Y, la próxima vez que inicie sesión (o si obtiene su .bash_profile después de esta actualización), tendrá una nueva función ingeniosa "enésima" disponible para canalizar sus archivos.

Ejecute esto o póngalo en su ~ / .bash_profile (si usa bash) y vuelva a abrir bash (o ejecutar source ~/.bach_profile)

# print just the nth piped in line nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; }

Luego, para usarlo, simplemente páselo a través de él. P.ej,:

$ yes line | cat -n | nth 5 5 line


1

Después de echar un vistazo a la respuesta superior y al punto de referencia , he implementado una pequeña función auxiliar:

function nth {
    if (( ${#} < 1 || ${#} > 2 )); then
        echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
        return 1
    fi
    if (( ${#} > 1 )); then
        sed "$1q;d" $2
    else
        sed "$1q;d"
    fi
}

Básicamente puedes usarlo de dos maneras:

nth 42 myfile.txt
do_stuff | nth 42

0

Puse algunas de las respuestas anteriores en un breve script de bash que puede poner en un archivo llamado get.shy vincular /usr/local/bin/get(o cualquier otro nombre que prefiera).

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

Asegúrese de que sea ejecutable con

$ chmod +x get

Enlázalo para que esté disponible en el PATHcon

$ ln -s get.sh /usr/local/bin/get

¡Disfruta responsablemente!

PAGS

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.