En un script de shell bash, escribir un bucle for que itera sobre valores de cadena

24

En bash, sé que es posible escribir un forbucle en el que alguna variable de control de bucle iitera sobre enteros específicos. Por ejemplo, puedo escribir un script de shell bash que imprima los enteros entre 1 y 10:

#!/bin/bash

for i in {1..10}
do
 echo $i
done

¿Es posible en cambio iterar sobre una variable de control de bucle que es una cadena, si proporciono una lista de cadenas? Por ejemplo, suponga que tengo una cadena fnameque representa un nombre de archivo. Quiero llamar a un conjunto de comandos para cada nombre de archivo. Por ejemplo, podría querer imprimir el contenido del fnameuso de un comando como este:

#!/bin/bash

for fname in {"a.txt", "b.txt", "c.txt"}
do
 echo $fname
done

En otras palabras, en la primera iteración, fnamedebe tener el valor fname="a.txt", mientras que en la segunda iteración, fnamedebe tener el valor fname="b.txt", y así sucesivamente. Desafortunadamente, parece que la sintaxis anterior no es del todo correcta. Me gustaría obtener el resultado:

a.txt

b.txt

c.txt

pero cuando pruebo el código anterior, obtengo esta salida:

{a.txt,

b.txt,

c.txt}

¿Puede ayudarme a determinar la sintaxis correcta, para que pueda cambiar iterativamente el valor / contenido de la variable fname? Gracias por tu tiempo.

Andrés
fuente
55
Elimine el {}, no necesita nada para recorrer una lista (delimitada por espacios)
Mat
3
@ Mat significa eliminar el {} y el ,s. La alternativa es eliminar los espacios. Así que, o "a.txt" "b.txt" "c.txt"o {"a.txt","b.txt","c.txt"}. Pero prefiero en su {a..c}.txtlugar.
manatwork

Respuestas:

39

La sintaxis correcta es la siguiente:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done
Ali Gangji
fuente
99
Además, suponiendo una matriz de nombres fnames=( a.txt b.txt c.txt ), puede usar la sintaxis for f in ${fnames[@]}; do echo $f; done.
1
¿Es cierto eso for fname in a.txt b.txt c.txty for fname in "a.txt" "b.txt" "c.txt"arrojar resultados idénticos?
Andrew
Andrew, sí, es verdad.
Darán
1
Por supuesto, debe usar for f in "${fnames[@]}"; do echo $f; done(con comillas ${fnames[@]}) si los fnamesvalores pueden contener espacios en blanco. Y debe usar "$f", especialmente si está haciendo algo más sofisticado que echo(por ejemplo, cato cp). (E incluso si solo lo está haciendo echo, debería usarlo printfen su lugar.)
Scott
Creo que @ user13742 también debería tenerse en cuenta en la respuesta.
Fallenreaper el
1

Me parece que deberías hacer ...

printf %s.txt\\n a b c
mikeserv
fuente
Me gusta esto a pesar de que el OP probablemente tenía algo en mente que hacer además del eco antes de terminar.
Élder Geek
0

Como señaló el usuario 13742 en los comentarios , podemos hacer uso de matrices en bashy ksh:

#!/usr/bin/env bash

files_list=( "a.txt" "b.txt" "c and space.txt" )

for i in "${files_list[@]}"
do
    echo "$i"
    # do something else here,maybe
done

Y funciona así:

$ ./iterate_files_array.sh                                                      
a.txt
b.txt
c and space.txt

Sin embargo, algunos shells como dash( /bin/shen Ubuntu) no admiten matrices. En tal caso, podríamos recurrir al uso de la estructura del documento aquí:<<

#!/bin/sh

while IFS= read -r line
do
    echo "$line"
done << EOL
one.txt
two.txt
with space.txt
EOL
Sergiy Kolodyazhnyy
fuente