¿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
Me sale este resultado 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 $p
que simplemente enviarlo 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.
cat /proc/version
salida:
Linux version 2.6.18.2-34-default (geeko@buildhost) (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
Respuestas:
Una forma de hacerlo es:
Como se señaló en los comentarios, esto tiene los efectos secundarios de recortar los espacios en blanco iniciales, interpretar las secuencias de barra invertida y omitir la última línea si falta un salto de línea de terminación. Si estas son preocupaciones, puede hacer:
Excepcionalmente, si el cuerpo del bucle puede leer desde la entrada estándar , puede abrir el archivo usando un descriptor de archivo diferente:
Aquí, 10 es solo un número arbitrario (diferente de 0, 1, 2).
fuente
while read p || [[ -n $p ]]; do ...
y la variante de una línea:
Estas opciones omitirán la última línea del archivo si no hay avance de línea final.
Puede evitar esto de la siguiente manera:
fuente
Opción 1a: Bucle While: línea única a la vez: redirección de entrada
Opción 1b: Bucle While: línea única a la vez:
abra el archivo, lea desde un descriptor de archivo (en este caso, el descriptor de archivo # 4).
fuente
done < $filename
pordone 4<$filename
(que es útil si desea leer el nombre del archivo de un parámetro de comando, en cuyo caso puede simplemente reemplazarlo$filename
por$1
)tail -n +2 myfile.txt | grep 'somepattern' | cut -f3
, mientras ejecuto comandos ssh dentro del ciclo (consume stdin); La opción 2 aquí parece ser la única manera?Esto no es mejor que otras respuestas, pero es una forma más de hacer el trabajo en un archivo sin espacios (ver comentarios). Me parece que a menudo necesito frases para buscar en las listas de los archivos de texto sin el paso adicional de usar archivos de script separados.
Este formato me permite ponerlo todo en una línea de comandos. Cambie la porción "echo $ word" a lo que quiera y puede emitir múltiples comandos separados por punto y coma. El siguiente ejemplo utiliza los contenidos del archivo como argumentos en otros dos scripts que puede haber escrito.
O si tiene la intención de usar esto como un editor de flujo (aprender sed) puede volcar la salida a otro archivo de la siguiente manera.
Los he usado como se escribió anteriormente porque he usado archivos de texto donde los he creado con una palabra por línea. (Ver comentarios) Si tiene espacios en los que no desea dividir sus palabras / líneas, se vuelve un poco más feo, pero el mismo comando aún funciona de la siguiente manera:
Esto solo le dice al shell que se divida solo en nuevas líneas, no en espacios, luego regresa el entorno a lo que era anteriormente. En este punto, es posible que desee considerar poner todo en un script de shell en lugar de exprimirlo todo en una sola línea.
¡La mejor de las suertes!
fuente
for
hace que los tokens / líneas de entrada estén sujetos a expansiones de shell, lo que generalmente no es deseable; intente esto:for l in $(echo '* b c'); do echo "[$l]"; done
como verá, el*
- aunque originalmente era un literal citado - se expande a los archivos en el directorio actual.for
iterar líneas de archivo es una mala idea. Además, el aspecto de expansión mencionado por @ mklement0 (a pesar de que eso probablemente se puede eludir al incluir comillas escapadas, lo que nuevamente hace que las cosas sean más complejas y menos legibles).Algunas cosas más no cubiertas por otras respuestas:
Leer desde un archivo delimitado
Lectura de la salida de otro comando, usando la sustitución del proceso
Este enfoque es mejor que
command ... | while read -r line; do ...
porque el ciclo while aquí se ejecuta en el shell actual en lugar de un subshell como en el caso de este último. Ver la publicación relacionada No se recuerda una variable modificada dentro de un ciclo while .Lectura de una entrada delimitada nula, por ejemplo
find ... -print0
Lectura relacionada: BashFAQ / 020 - ¿Cómo puedo encontrar y manejar con seguridad los nombres de archivo que contienen líneas nuevas, espacios o ambos?
Leer desde más de un archivo a la vez
Basado en la respuesta de @ chepner aquí :
-u
es una extensión bash. Por compatibilidad POSIX, cada llamada se vería algo asíread -r X <&3
.Lectura de un archivo completo en una matriz (versiones Bash anteriores a la 4)
Si el archivo termina con una línea incompleta (falta una nueva línea al final), entonces:
Lectura de un archivo completo en una matriz (versiones Bash 4x y posteriores)
o
Y entonces
Más acerca de los comandos
read
yreadarray
comandos integrados de shell - GNUMás sobre
IFS
- WikipediaArtículos Relacionados:
fuente
command < input_filename.txt
siempre puede hacerinput_generating_command | command
ocommand < <(input_generating_command)
Use un ciclo while, como este:
Notas:
Si no configura
IFS
correctamente, perderá la sangría.Casi siempre debe usar la opción -r con lectura.
No lea líneas con
for
fuente
-r
opción?Note #2
es un enlace donde se describe en detalle ...-u
opción, ¿estás hablando de otro ejemplo-u
?Supongamos que tiene este archivo:
Hay cuatro elementos que alterarán el significado de la salida del archivo leída por muchas soluciones de Bash:
Si desea que el archivo de texto línea por línea incluya líneas en blanco y 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 que
cat
devuelve):1) Pierda la última línea y los espacios iniciales y finales:
(Si lo hace
while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
, conserva los espacios iniciales y finales pero aún pierde la última línea si no termina con CR)2) El uso de la sustitución de procesos con
cat
will lee todo el archivo de una vez y pierde el significado de las líneas individuales:(Si se quita la
"
de$(cat /tmp/test.txt)
leer el archivo de la palabra por palabra en lugar de un solo trago. También probablemente no lo que se pretende ...)La forma más sólida y sencilla de leer un archivo línea por línea y preservar todo el espaciado es:
Si desea eliminar los espacios iniciales y comerciales, elimine la
IFS=
parte:(Un archivo de texto sin una terminación
\n
, aunque bastante común, se considera roto bajo POSIX. Si puede contar con el seguimiento\n
no necesita|| [[ -n $line ]]
en elwhile
bucle).Más en las preguntas frecuentes de BASH
fuente
Si no desea que su lectura se rompa por el carácter de nueva línea, use -
Luego ejecute el script con el nombre del archivo como parámetro.
fuente
fuente
Aquí está mi ejemplo de la vida real: cómo hacer un bucle en las líneas de otra salida del programa, verificar las subcadenas, descartar comillas dobles de la variable, usar esa variable fuera del bucle. Supongo que muchos hacen estas preguntas tarde o temprano.
Declarar variable fuera del ciclo, establecer el valor y usarlo fuera del ciclo requiere la sintaxis <<< "$ (...)" . La aplicación debe ejecutarse dentro de un contexto de la consola actual. Las comillas alrededor del comando mantienen nuevas líneas de flujo de salida.
La coincidencia de bucle para las subcadenas lee el nombre = par de valores , divide la parte derecha del último = carácter, suelta la primera comilla, suelta la última comilla, tenemos un valor limpio para usar en otro lugar.
fuente
Esto llega bastante tarde, pero con la idea de que puede ayudar a alguien, agrego la respuesta. Además, esta puede no ser la mejor manera.
head
El comando se puede usar con-n
argumentos para leer n líneas desde el inicio del archivo y del mismo modotail
se puede usar el comando para leer desde abajo. Ahora, para obtener la enésima línea del archivo, encabezamos n líneas , canalizamos los datos para seguir solo 1 línea de los datos canalizados.fuente
sed
ohead
+tail
es increíblemente ineficiente, y por supuesto plantea la pregunta de por qué no simplemente usa una de las otras soluciones aquí. Si necesita saber el número de línea, agregue un contador a suwhile read -r
ciclo o usenl -ba
para agregar un prefijo de número de línea a cada línea antes del ciclo.Me gusta usar en
xargs
lugar dewhile
.xargs
es potente y amigable con la línea de comandoscat peptides.txt | xargs -I % sh -c "echo %"
Con
xargs
, también puede agregar verbosidad-t
y validación con-p
fuente
@ Peter: Esto podría funcionar para usted
Esto devolvería la salida
fuente