linux - scripts - Recorriendo el contenido de un archivo en Bash




scripts bash ejemplos (8)

@Peter: Esto podría funcionar para ti-

echo "Start!";for p in $(cat ./pep); do
echo $p
done

Esto devolvería la salida

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

¿Cómo itero a través de cada línea de un archivo de texto con Bash ?

Con este script:

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

Obtengo esta salida en la pantalla:

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(Más tarde, quiero hacer algo más complicado con $p que simplemente salir a la pantalla).

La variable de entorno SHELL es (de env):

SHELL=/bin/bash

/bin/bash --version salida:

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

Salida cat /proc/version :

Linux version 2.6.18.2-34-default ([email protected]) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

El archivo peptides.txt contiene:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

Aquí está mi ejemplo de la vida real: cómo hacer un bucle en las líneas de otra salida del programa, verificar subcadenas, eliminar comillas dobles de la variable, usar esa variable fuera del bucle. Supongo que muchos están haciendo estas preguntas tarde o temprano.

##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
  if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
    echo ParseFPS $line
    FPS=parse
  fi
  if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
    echo ParseFPS $line
    FPS=${line##*=}
    FPS="${FPS%\"}"
    FPS="${FPS#\"}"
  fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then 
  echo ParseFPS Unknown frame rate
fi
echo Found $FPS

Declare la variable fuera del bucle, establezca el valor y utilícelo fuera del bucle se requiere la sintaxis de <<< "(...)" . La aplicación debe ejecutarse dentro de un contexto de la consola actual. Las citas alrededor del comando mantienen nuevas líneas de flujo de salida.

La coincidencia de bucle para las subcadenas luego lee el par nombre = valor , divide la parte del lado derecho del último carácter = , deja caer la primera cita, elimina la última cita, tenemos un valor limpio para ser usado en otros lugares.


Si no desea que su lectura sea interrumpida por el carácter de nueva línea, use -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < "$1"

Luego ejecute el script con el nombre del archivo como parámetro.


Supongamos que tiene este archivo:

$ cat /tmp/test.txt
Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR

Hay cuatro elementos que alterarán el significado de la salida del archivo leído por muchas soluciones Bash:

  1. La línea en blanco 4;
  2. Espacios iniciales o finales en dos líneas;
  3. Mantener el significado de líneas individuales (es decir, cada línea es un registro);
  4. La línea 6 no terminó con un CR.

Si desea el archivo de texto línea por línea, incluidas las líneas en blanco y las líneas de terminación sin CR, debe usar un bucle while y debe tener una prueba alternativa para la línea final.

Estos son los métodos que pueden cambiar el archivo (en comparación con lo cat devuelve cat ):

1) Perder la última línea y espacios iniciales y finales:

$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'

(Si lo haces while IFS= read -rp; do printf "%s\n" "'$p'"; done </tmp/test.txt en while IFS= read -rp; do printf "%s\n" "'$p'"; done </tmp/test.txt lugar, conservas los espacios al while IFS= read -rp; do printf "%s\n" "'$p'"; done </tmp/test.txt y al final pero aún pierdes la última línea si no se termina con CR)

2) El uso de la sustitución de procesos con cat leerá todo el archivo de una vez y perderá el significado de las líneas individuales:

$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR'

(Si elimina el " from $(cat /tmp/test.txt) , lea el archivo palabra por palabra en lugar de un solo trago. Probablemente no sea lo que se pretende ...)

La forma más robusta y sencilla de leer un archivo línea por línea y conservar todo el espacio es:

$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'    Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space    '
'Line 6 has no ending CR'

Si desea eliminar los espacios iniciales y comerciales, elimine la parte IFS= :

$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'

(Un archivo de texto sin un \n terminación, aunque bastante común, se considera roto bajo POSIX. Si puede contar con el \n final no necesita || [[ -n $line ]] en el bucle while).

Más en las preguntas frecuentes de BASH


Unas cuantas cosas más no cubiertas por otras respuestas:

Leyendo de un archivo delimitado

# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
  # process the fields
  # if the line has less than three fields, the missing fields will be set to an empty string
  # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt

Lectura de la salida de otro comando, utilizando la sustitución de procesos.

while read -r line; do
  # process the line
done < <(command ...)

Este enfoque es mejor que el command ... | while read -r line; do ... command ... | while read -r line; do ... command ... | while read -r line; do ... porque el bucle while aquí se ejecuta en el shell actual en lugar de una subshell como en el caso de este último. Consulte la publicación relacionada. Una variable modificada dentro de un bucle while no se recuerda .

Lectura de una entrada delimitada por nulos, por ejemplo, find ... -print0

while read -r -d '' line; do
  # logic
  # use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)

Lectura relacionada: BashFAQ / 020 - ¿Cómo puedo encontrar y manejar con seguridad nombres de archivos que contengan líneas nuevas, espacios o ambos?

Lectura de más de un archivo a la vez

while read -u 3 -r line1 && read -u 4 -r line2; do
  # process the lines
  # note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt

Basado en @chepner's respuesta @chepner's here :

-u es una extensión de bash. Para la compatibilidad con POSIX, cada llamada se vería algo así como read -r X <&3 .

Leyendo un archivo completo en una matriz (Bash versiones anteriores a 4)

while read -r line; do
    my_array+=("$line")
done < my_file

Si el archivo termina con una línea incompleta (falta una nueva línea al final), entonces:

while read -r line || [[ $line ]]; do
    my_array+=("$line")
done < my_file

Leyendo un archivo completo en una matriz (versiones Bash 4x y posteriores)

readarray -t my_array < my_file

o

mapfile -t my_array < my_file

Y entonces

for line in "${my_array[@]}"; do
  # process the lines
done

Artículos Relacionados:

  • Creando una matriz de un archivo de texto en Bash
  • ¿Cuál es la diferencia entre los enfoques para leer un archivo que tiene una sola línea?
  • Bash mientras lee el bucle extremadamente lento en comparación con el gato, ¿por qué?


#!/bin/bash
#
# Change the file name from "test" to desired input file 
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
    echo $x
done

cat peptides.txt | while read line
do
   # do something with $line here
done




io