Em um script do shell bash, escrever um loop for que itere sobre os valores da string


24

No bash, eu sei que é possível escrever um forloop no qual alguma variável de controle de loop iitera sobre números inteiros especificados. Por exemplo, eu posso escrever um script de shell bash que imprima os números inteiros entre 1 e 10:

#!/bin/bash

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

É possível, em vez disso, iterar sobre uma variável de controle de loop que é uma string, se eu fornecer uma lista de strings? Por exemplo, suponha que eu tenha uma sequência fnameque represente um nome de arquivo. Eu quero chamar um conjunto de comandos para cada nome de arquivo. Por exemplo, talvez eu queira imprimir o conteúdo do fnameuso de um comando como este:

#!/bin/bash

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

Em outras palavras, na primeira iteração, fnamedeve ter o valor fname="a.txt", enquanto na segunda iteração, fnamedeve ter o valor fname="b.txt", e assim por diante. Infelizmente, parece que a sintaxe acima não está correta. Eu gostaria de obter a saída:

a.txt

b.txt

c.txt

mas quando tento o código acima, obtenho esta saída:

{a.txt,

b.txt,

c.txt}

Você pode me ajudar a determinar a sintaxe correta, para que eu possa alterar iterativamente o valor / conteúdo da variável fname? Obrigado pelo seu tempo.


5
Remova o {}, você não precisa de nada para fazer um loop sobre uma lista (delimitada por espaço) #
Mat Mat

3
@Mat meios remover o {} e o ,s. A alternativa é remover os espaços. Então, "a.txt" "b.txt" "c.txt"ou {"a.txt","b.txt","c.txt"}. Mas eu prefiro {a..c}.txt.
manatwork 8/09/12

Respostas:


39

A sintaxe correta é a seguinte:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done

9
Além disso, assumindo uma matriz de nomes, fnames=( a.txt b.txt c.txt )você pode usar a sintaxe for f in ${fnames[@]}; do echo $f; done.

1
É verdade for fname in a.txt b.txt c.txte for fname in "a.txt" "b.txt" "c.txt"produz resultados idênticos?
Andrew Andrew

Andrew, sim, é verdade. Eles produzirão resultados idênticos
Ali Gangji 08/09/12

1
Claro que você deve usar for f in "${fnames[@]}"; do echo $f; done(com aspas ${fnames[@]}) se os fnamesvalores puderem conter espaços em branco. E você deve usar "$f", especialmente se estiver fazendo algo mais sofisticado do que echo(por exemplo, catou cp). (E mesmo se você está fazendo única echo, você deve usar printfem seu lugar.)
Scott

Acho que @ user13742 também deve ser observado na resposta.
Fallenreaper

1

Parece-me que você deveria fazer ...

printf %s.txt\\n a b c

Gosto disso, embora o OP provavelmente tenha algo em mente para fazer além de eco antes de ele terminar.
Elder Geek

0

Conforme observado por user13742 nos comentários , podemos fazer uso de matrizes em bashe 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

E funciona da seguinte maneira:

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

No entanto, alguns shells como dash( /bin/shno Ubuntu) não suportam matrizes. Nesse caso, poderíamos recorrer ao uso da estrutura do documento aqui:<<

#!/bin/sh

while IFS= read -r line
do
    echo "$line"
done << EOL
one.txt
two.txt
with space.txt
EOL
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.