28

In bash, I know that it is possible to write a for loop in which some loop control variable i iterates over specified integers. For example, I can write a bash shell script that prints the integers between 1 and 10:

#!/bin/bash

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

Is it possible to instead iterate over a loop control variable that is a string, if I provide a list of strings? For example, suppose that I have a string fname that represents a file name. I want to call a set of commands for each file name. For example, I might want to print the contents of fname using a command like this:

#!/bin/bash

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

In other words, on the first iteration, fname should have the value fname="a.txt", while on the second iteration, fname should have the value fname="b.txt", and so on. Unfortunately, it seems that the above syntax is not quite correct. I would like to obtain the output:

a.txt

b.txt

c.txt

but when I try the above code, I obtain this output:

{a.txt,

b.txt,

c.txt}

Can you please help me determine the correct syntax, so that I can iteratively change the value/contents of the variable fname? Thank you for your time.

Andrew
  • 16,855
  • 6
    Remove the {}, you don't need anything to loop over a (space-delimited) list – Mat Sep 08 '12 at 15:54
  • 4
    @Mat means remove the {} and the ,s. The alternative is to remove the spaces. So either "a.txt" "b.txt" "c.txt" or {"a.txt","b.txt","c.txt"}. But I prefer {a..c}.txt instead. – manatwork Sep 08 '12 at 16:20

3 Answers3

44

The correct syntax is as follows:

#!/bin/bash

for fname in a.txt b.txt c.txt
do
  echo $fname
done
Michael Mrozek
  • 93,103
  • 40
  • 240
  • 233
Ali Gangji
  • 589
  • 5
  • 4
  • 9
    Also, assuming an array of names fnames=( a.txt b.txt c.txt ) you can use the syntax for f in ${fnames[@]}; do echo $f; done. –  Sep 08 '12 at 19:12
  • 1
    Is it true that for fname in a.txt b.txt c.txt and for fname in "a.txt" "b.txt" "c.txt" yield identical results? – Andrew Sep 08 '12 at 19:39
  • Andrew, yes it is true. They will yield identical results – Ali Gangji Sep 08 '12 at 20:29
  • 1
    Of course you should use for f in "${fnames[@]}"; do echo $f; done (with quotes around ${fnames[@]}) if the fnames values might contain whitespace.  And you should use "$f", especially if you're doing anything more sophisticated than echo (e.g., cat or cp). (And even if you're only doing echo, you should use printf instead.) – Scott - Слава Україні Apr 23 '16 at 21:02
  • I think @user13742 should be also noted in the answer. – Fallenreaper Jan 13 '18 at 04:53
  • There's pretty much never a reason to not use double quotes around a variable in bash. If you want a string to split on IFS, you should probably be using an array instead. – BallpointBen Aug 15 '18 at 19:49
0

Seems to me you should just do...

printf %s.txt\\n a b c
mikeserv
  • 58,310
0

As noted by user13742 in the comments, we can make use of arrays in bash and 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

And works as so:

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

However, some shells such as dash ( /bin/sh on Ubuntu ) don't support arrays. In such case we could resort to using here-document structure: <<

#!/bin/sh

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