Bash foreach loop examples for Linux / Unix

I used foreach on a Unix operating system and csh shell loop. Now, I am a Linux operating system. How do I use foreach loop in bash shell running on Linux?

Tutorial details
Difficulty level Easy
Root privileges No
Requirements None
Est. reading time 2 minutes
The C shell (csh) or the improved version, tcsh is a Unix shell from the late 1970s. The csh foreach loop syntax is as follows:
foreach n ( 1 2 3 4 5 )
  #command1
  #command2
end

Linux foreach tcsh example
However, bash lacks foreach syntax; instead, you can use bash while loop or bash for loop syntax as described below.

Bash foreach loop examples

Let us say you want to convert the following csh forloop example:

foreach i ( * )
echo "Working $i filename ..."
end


Above csh or tcsh foreach displayed a list of files using loop. Here is similar code in a bash shell:

for i in *
do
  echo "Working on $i file..."
done


However, one can find and safely handle file names containing newlines, spaces, special characters using the find command

## tested on GNU/Linux find ##
## and bsd/find only ##
find /dir/ -print0 | xargs -r0 command
find /tmp/test -print0 | xargs -I {} -r0 echo "Working on "{}" file ..."
find /tmp/test -type f -print0 | xargs -I {} -r0 echo "Working on '{}' file ..."
Working on '/tmp/test' file ...
Working on '/tmp/test/hosts.deny' file ...
Working on '/tmp/test/My Resume.pdf' file ...
Working on '/tmp/test/hostname' file ...
Working on '/tmp/test/hosts.allow' file ...
Working on '/tmp/test/Another   file     name.txt' file ...
Working on '/tmp/test/resolv.conf' file ...
Working on '/tmp/test/hosts' file ...
Working on '/tmp/test/host.conf' file ...

Howto use foreach in bash shell

Say you have a file named lists.txt as follows:
cat lists.txt
Sample outputs:

/nfs/db1.dat
/nfs/db2.dat
/nfs/share/sales.db
/nfs/share/acct.db 
/nfs/private/users.db
/nfs/private/payroll.db

Each in there is a file located on your Unix or Linux server. Here is how to read lists.txt file and work on each file listed in lists.txt:

for n in $(cat lists.txt )
do
    echo "Working on $n file name now"
    # do something on $n below, say count line numbers
    # wc -l "$n"
done

Although for loop seems easy to use for reading the file, it has some problems. Don’t try to use “for” to read file line by line in Linux or Unix. Instead, use while loop as follows:

#!/bin/bash
## path to input file 
input="lists.txt"
 
## Let us read a file line-by-line using while loop ##
while IFS= read -r line
do
  printf 'Working on %s file...\n' "$line"
done < "$input"

🐧 Get the latest tutorials on Linux, Open Source & DevOps via RSS feed or Weekly email newsletter.

🐧 3 comments so far... add one


CategoryList of Unix and Linux commands
Disk space analyzersdf ncdu pydf
File Managementcat cp mkdir tree
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network UtilitiesNetHogs dig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
3 comments… add one
  • Jay Aug 18, 2020 @ 19:05

    How would one do that example command on a single line command in MacOS’ terminal? Tried remove the line breaks and tabs and then it becomes non-functional. Tried adding some \newline s even and it still didn’t work.

  • Rob Apr 15, 2021 @ 18:17

    My issue when going from tcsh to bash is that if you use a command in the loop that is capable of *optionally* taking STDIN *if* it is present, the loop stops if you are using the contents of the file as the loop items. tcsh does not have this problem.

    Let’s say we have a command called cat.pl that can operate on a given file name, but if STDIN is present, will take STDIN instead and ignore the supplied files. In that instance, this tcsh loop has no problem what-so-ever:

    foreach f ( `cat filelist.txt` )
      optin $f
    end
    

    However, if you want to do that in bash, using a while loop as suggested, there’s a loose canon of an input stream available to the commands inside the loop to consume, so:

    while IFS= read -r line
    do
      cat.pl "$line"
    done < filelist.txt
    

    Then cat.pl output all except the first line of filelist.txt and the loop only executes 1 iteration.

    This caught me by surprise when I first started learning bash, trying to do what I normally would do in tcsh. And for the life of me, I cannot figure out 1 use-case why you would supply the loop items of the standard input to the commands inside the loop.

    Personally, to me, having used tcsh for so long, it seems sloppy to allow the stream of input to the loop controls to be available to the commands inside the loop.

    Sure, bash has a lot of extras that make it better, but this one issue has always bothered me. I would be interested in hearing how you deal with this situation. All of the scripts I have written over the years while being a tcsh user optionally take STDIN if it is present and/or takes explicitly supplied files.

    • 🐧 Vivek Gite Apr 16, 2021 @ 3:57

      Instead of:

      while IFS= read -r line
      do
        cat.pl "$line"
      done < filelist.txt
      

      Try:

      while IFS= read -r line
      do
        echo "$line"
      done < "${1:-/dev/stdin}"
      

      Now we can run it as to read list from filelist.txt

      ./script filelist.txt

      This will read from STDIN

      ./script 

      I hope this helps!

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum