W skrypcie powłoki bash, pisanie pętli for, która iteruje wartości ciągu

24

W bash wiem, że można napisać forpętlę, w której jakaś zmienna sterująca pętli iiteruje po określonych liczbach całkowitych. Na przykład mogę napisać skrypt powłoki bash, który wypisuje liczby całkowite od 1 do 10:

#!/bin/bash

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

Czy zamiast tego można iterować zmienną sterującą pętli, która jest łańcuchem, jeśli podam listę łańcuchów? Załóżmy na przykład, że mam ciąg fnamereprezentujący nazwę pliku. Chcę wywołać zestaw poleceń dla każdej nazwy pliku. Na przykład mógłbym chcieć wydrukować zawartość fnameprzy użyciu takiego polecenia:

#!/bin/bash

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

Innymi słowy, przy pierwszej iteracji fnamepowinien mieć wartość fname="a.txt", podczas gdy przy drugiej iteracji fnamepowinien mieć wartość fname="b.txt"i tak dalej. Niestety wydaje się, że powyższa składnia nie jest całkiem poprawna. Chciałbym uzyskać wynik:

a.txt

b.txt

c.txt

ale gdy wypróbuję powyższy kod, otrzymuję ten wynik:

{a.txt,

b.txt,

c.txt}

Czy możesz mi pomóc ustalić poprawną składnię, abym mógł iteracyjnie zmieniać wartość / zawartość zmiennej fname? Dziękuję za Twój czas.

Andrzej
źródło
5
Usuń {}, nie potrzebujesz niczego, aby zapętlić listę (rozdzieloną spacjami)
Mat
3
@Mat pomocą usunięcia {} i na ,s. Alternatywą jest usunięcie spacji. Więc albo "a.txt" "b.txt" "c.txt"albo {"a.txt","b.txt","c.txt"}. Ale wolę {a..c}.txtzamiast tego.
manatwork

Odpowiedzi:

39

Prawidłowa składnia jest następująca:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done
Ali Gangji
źródło
9
Zakładając tablicę nazw fnames=( a.txt b.txt c.txt ), możesz użyć składni for f in ${fnames[@]}; do echo $f; done.
1
Czy to prawda for fname in a.txt b.txt c.txti for fname in "a.txt" "b.txt" "c.txt"dają identyczne wyniki?
Andrew
Andrew, tak, to prawda.
Przyniosą
1
Oczywiście powinieneś używać for f in "${fnames[@]}"; do echo $f; done(z cudzysłowami ${fnames[@]}), jeśli fnameswartości mogą zawierać spacje. I powinieneś użyć "$f", zwłaszcza jeśli robisz coś bardziej zaawansowanego niż echo(np. catLub cp). (I nawet jeśli tylko robisz echo, powinieneś użyć printfzamiast tego.)
Scott
Myślę, że @ user13742 powinien również zostać odnotowany w odpowiedzi.
Fallenreaper
1

Wydaje mi się, że powinieneś po prostu zrobić ...

printf %s.txt\\n a b c
mikeserv
źródło
Podoba mi się to, mimo że OP prawdopodobnie miał na myśli coś innego niż echo, zanim skończył.
Elder Geek
0

Jak zauważył user13742 w komentarzach , możemy skorzystać z tablic w bashi 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

I działa tak:

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

Jednak niektóre powłoki, takie jak dash( /bin/shna Ubuntu), nie obsługują tablic. W takim przypadku możemy skorzystać ze struktury dokumentu tutaj:<<

#!/bin/sh

while IFS= read -r line
do
    echo "$line"
done << EOL
one.txt
two.txt
with space.txt
EOL
Sergiy Kolodyazhnyy
źródło