В сценарии оболочки bash, написание цикла for, который перебирает строковые значения

24

В bash я знаю, что можно написать forцикл, в котором некоторая переменная управления цикла iперебирает заданные целые числа. Например, я могу написать скрипт оболочки bash, который печатает целые числа от 1 до 10:

#!/bin/bash

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

Можно ли вместо этого перебрать переменную управления циклом, которая является строкой, если я предоставлю список строк? Например, предположим, что у меня есть строка, fnameкоторая представляет имя файла. Я хочу вызвать набор команд для каждого имени файла. Например, я мог бы хотеть напечатать содержание fnameиспользования команды как это:

#!/bin/bash

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

Другими словами, на первой итерации fnameдолжно иметь значение fname="a.txt", а на второй итерации fnameдолжно иметь значение fname="b.txt"и так далее. К сожалению, похоже, что приведенный выше синтаксис не совсем корректен. Я хотел бы получить вывод:

a.txt

b.txt

c.txt

но когда я пробую приведенный выше код, я получаю этот вывод:

{A.txt,

b.txt,

c.txt}

Не могли бы вы помочь мне определить правильный синтаксис, чтобы я мог итеративно изменять значение / содержимое переменной fname? Спасибо за ваше время.

Эндрю
источник
5
Удалите {}, вам не нужно ничего зацикливать (разделенный пробелами) список
Mat
3
@Mat средство удаления {} и на ,с. Альтернатива состоит в том, чтобы удалить пробелы. Так что либо "a.txt" "b.txt" "c.txt"или {"a.txt","b.txt","c.txt"}. Но я предпочитаю {a..c}.txtвместо этого.
manatwork

Ответы:

39

Правильный синтаксис выглядит следующим образом:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done
Али Ганджи
источник
9
Также, предполагая массив имен, fnames=( a.txt b.txt c.txt )вы можете использовать синтаксис for f in ${fnames[@]}; do echo $f; done.
1
Правда ли это for fname in a.txt b.txt c.txtи for fname in "a.txt" "b.txt" "c.txt"дают одинаковые результаты?
Андрей
Андрей, да это правда. Они дадут одинаковые результаты
Али Ганджи
1
Конечно, вы должны использовать for f in "${fnames[@]}"; do echo $f; done(с кавычками ${fnames[@]}), если fnamesзначения могут содержать пробелы. И вы должны использовать "$f", особенно если вы делаете что-то более сложное, чем echo(например, catили cp). (И даже если вы только делаете echo, вы должны использовать printfвместо этого.)
Скотт
Я думаю, что @ user13742 также следует отметить в ответе.
Fallenreaper
1

Мне кажется, вы должны просто сделать ...

printf %s.txt\\n a b c
mikeserv
источник
Мне это нравится, хотя ОП, скорее всего, имел в виду что-то, кроме эха, до того, как он закончил.
Старейшина Гик
0

Как отметил пользователь 13742 в комментариях , мы можем использовать массивы в bashи 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

И работает как так:

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

Однако некоторые оболочки, такие как dash( /bin/shв Ubuntu), не поддерживают массивы. В таком случае мы могли бы прибегнуть к использованию здесь-документа структуры:<<

#!/bin/sh

while IFS= read -r line
do
    echo "$line"
done << EOL
one.txt
two.txt
with space.txt
EOL
Сергей Колодяжный
источник