Obtenga una línea específica del archivo de texto usando solo un script de shell

100

Estoy tratando de obtener una línea específica de un archivo de texto.

Hasta ahora, en línea solo he visto cosas como sed, (solo puedo usar sh -not bash o sed ni nada por el estilo). Necesito hacer esto solo usando un script de shell básico.

cat file | while read line
    do
       #do something
    done

Sé cómo iterar a través de las líneas, como se muestra arriba, pero ¿qué pasa si solo necesito obtener el contenido de una línea en particular?

GangstaGraham
fuente
sabes el numero de linea?
Mehul Rathod
1
Entonces puedes contar.
Ignacio Vazquez-Abrams
sí, el número de línea es 5 @MehulRathod
GangstaGraham
3
¿Por qué está catbien pero sedno? Eso no tiene sentido.
William Pursell
5
Porque nadie puede decir que no cat. ¡Oh ... lindo cat!

Respuestas:

204

sed:

sed '5!d' file

awk:

awk 'NR==5' file
Kent
fuente
¿Qué pasa con el comando sh? No puedo usar sed, awk. Debería dejar esto más claro en la pregunta.
GangstaGraham
@GangstaGraham dijiste que sabes cómo iterar a través de líneas, ¿qué tal si agregas un contador? si el contador alcanza su número de línea objetivo, obtenga la línea y rompa el ciclo. ayuda?
Kent
4
@KanagaveluSugumar leyó la página de información de sed. 5!dsignifica eliminar todas las líneas excepto 5. shell var es posible, necesita comillas dobles.
Kent
13
Sugeriría agregar otra variante: sed -n 5pesto parece más lógico de recordar para los principiantes, porque -nsignifica "sin salida por defecto" y psignifica "imprimir", y no hay una mención potencialmente confusa de eliminar (cuando la gente habla de archivos, eliminar líneas tiende a significa algo diferente).
Josip Rodin
1
@JosipRodin tienes razón, también -n '5p'funciona para este problema. La diferencia aquí es 5!dque puede agregar -ipara volver a escribir el cambio en el archivo. sin embargo, -n 5ptengo que sed -n '5p' f > f2&& mv f2 fvolver a hacerlo , para esta pregunta, estoy de acuerdo con su opinión.
Kent
21

Suponiendo que linees una variable que contiene su número de línea requerido, si puede usar heady tail, entonces es bastante simple:

head -n $line file | tail -1

Si no, esto debería funcionar:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done
micromosis
fuente
Esta -eqcomparación es para enteros, por lo que quiere un número de línea, no contenido de línea ( $line). Esto debe solucionarse definiendo, por ejemplo, want=5antes del bucle y luego utilizando la -eqcomparación en $want. [movido de una edición rechazada]
Josip Rodin
1
@JosipRodin Hice una sugerencia de edición independiente basada en su comentario, ya que estoy de acuerdo con él. Con suerte, esta vez no será rechazada.
Victor Zamanian
15

Podrías usar sed -n 5p file .

También puede obtener un rango, por ejemplo, sed -n 5,10p file.

Nomas Prime
fuente
11

Mejor método de rendimiento

sed '5q;d' file

Porque seddeja de leer cualquier línea después de la quinta.

Actualizar el experimento del Sr. Roger Dueck

Instalé wcanadian-insane (6.6MB) y comparé sed -n 1p / usr / share / dict / words y sed '1q; d' / usr / share / dict / words usando el comando time; el primero tomó 0.043s, el segundo solo 0.002s, por lo que usar 'q' es definitivamente una mejora del rendimiento.

fe en nuestra
fuente
1
Esto también se escribe comúnmente:sed -n 5q
William Pursell
3
Me gusta esta solución porque seddeja de leer las líneas después de la quinta.
Anthony Geoghegan
1
He instalado wcanadian-loco (6,6 MB) y se compararon sed -n 1p /usr/share/dict/wordsy sed '1q;d' /usr/share/dict/wordsutilizando el timecomando; el primero tomó 0.043s, el segundo solo 0.002s, por lo que usar 'q' es definitivamente una mejora del rendimiento.
Roger Dueck
5

Si, por ejemplo, desea obtener las líneas 10 a 20 de un archivo, puede utilizar cada uno de estos dos métodos:

head -n 20 york.txt | tail -11

o

sed -n '10,20p' york.txt 

p en el comando anterior significa impresión.

Esto es lo que verá: ingrese la descripción de la imagen aquí

Mona Jalal
fuente
2

La forma estándar de hacer este tipo de cosas es utilizar herramientas externas. No permitir el uso de herramientas externas mientras se escribe un script de shell es absurdo. Sin embargo, si realmente no desea utilizar herramientas externas, puede imprimir la línea 5 con:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Tenga en cuenta que esto imprimirá la línea lógica 5. Es decir, si input-filecontiene continuaciones de línea, se contarán como una sola línea. Puede cambiar este comportamiento agregando -ral comando de lectura. (Que es probablemente el comportamiento deseado).

William Pursell
fuente
1
$((++i))parece ser un bashismo; si el OP está restringido en el uso de herramientas externas, no asumiría que tendrán acceso a más de un simple/bin/sh
Josip Rodin
@JosipRodin No, es una función POSIX (pero el soporte para ++incrementos está específicamente marcado como opcional).
tripleee
@tripleee no funciona con el tablero moderno como / bin / sh, por lo que no confiaría en él.
Josip Rodin
Pero una solución simple como $((i+=1))funciona en Dash también.
tripleee
$(($i+1))es la solución simple en la que estaba pensando.
Josip Rodin
1

En paralelo con la respuesta de William Pursell , aquí hay una construcción simple que debería funcionar incluso en el shell Bourne v7 original (y, por lo tanto, también en lugares donde Bash no está disponible).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Nótese también la optimización para breaksalir del ciclo cuando hemos obtenido la línea que estábamos buscando.

triples
fuente
0

No me gustó particularmente ninguna de las respuestas.

Así es como lo hice.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"
cpp_guy_who_does_gfx
fuente
-1

¡Fácil con perl! Si 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
seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'pero smartmatch es experimental y se desaconseja su uso
Sorin
Ninguna de las otras soluciones es tan concisa ni permite tanta flexibilidad. (¿Por qué parece que todo lo que ahorra tiempo y facilita las cosas es "desanimado" por "gente inteligente", se supone que todos debemos mirar las pantallas todo el día?)
dagelf
-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"
Oder
fuente
3
¿Podría describir un poco al menos por qué este trabajo para que sea más claro para la persona que hizo la pregunta?
ted
Entonces, el primer grep selecciona todas las líneas agregando números de línea al comienzo. Luego, el segundo grep selecciona una línea específica haciendo coincidir el número de línea al inicio. Y finalmente, el número de línea se recorta del inicio de línea en eco.
Oder
Esto es complejo e ineficiente en comparación con lo sed -n 5pque, por supuesto, aún se puede optimizar a algo comosed -n '5!d;p;q'
tripleee