У сценарії оболонки bash написання циклу for для повторення значень рядка


24

В bash, я знаю, що можна написати forцикл, в якому деяка змінна керування циклом iперебирає задані цілі числа. Наприклад, я можу написати скрипт bash shell, який друкує цілі числа між 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
Видаліть {}, вам не потрібно нічого перебирати на (обмежений пробілом) список
матч

3
@Mat засіб видалення {} і на ,с. Альтернативою є видалення пробілів. Так "a.txt" "b.txt" "c.txt"або {"a.txt","b.txt","c.txt"}. Але я віддаю перевагу {a..c}.txtзамість цього.
манатура

Відповіді:


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"дає однакові результати?
Андрій

Ендрю, так це правда. Вони дадуть однакові результати
Ali Gangji

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

Мені це подобається, навіть якщо ОП, швидше за все, мав на увазі щось, крім відлуння, перш ніж він зробив.
Старійшина Гік

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.