4

Suppose I have 2 seperate loops

for file1 in `ls Dir1/` ; do
    echo $file1
done

for file2 in `ls Dir2/` ; do
    echo $file2
done

I want Single loop to iterate both Directories pseudocode

for file1 , file2 in `ls Dir1` , `ls Dir2`
do
    echo $file1
    echo file2
done

Is it possible

4
  • you cant do that, but you could use a function that takes the dir as a parameter and iterates through its files.
    – jai_s
    Feb 12, 2016 at 16:44
  • if you explain what actually you are trying to do, instead of a code which does nothing significant, may be you can get a different suggestion than looping through two directories in a single for loop. Just a thought.
    – MelBurslan
    Feb 12, 2016 at 16:47
  • I want to iterate files of 2 directories 1 by 1 Feb 12, 2016 at 16:48
  • dir1 has many files dir2 has many files I want to work on file1 from dir1 and file1 from dir2 and so on Feb 12, 2016 at 16:49

4 Answers 4

3

A while loop with suitable input can do the job, assuming no newline characters in the file names.

paste -d/ <(ls /var) <(ls /usr) |
  while IFS=/ read -r e u; do
    printf '%s\n' "$e $u"
  done
2
2
shopt -s nullglob

dir1names=( Dir1/* )
dir2names=( Dir2/* )

while [ "${#dir1names[@]}" -gt 0 ] &&
      [ "${#dir2names[@]}" -gt 0 ]
do

    printf '%s\n' "${dir1names[0]##*/}"
    printf '%s\n' "${dir2names[0]##*/}"

    dir1names=( "${dir1names[@]:1}" )
    dir2names=( "${dir2names[@]:1}" )
done

This uses bash to get all the pathnames from each directory into two arrays. It then prints the filename portion of these pathnames (like ls would do), alternating between the arrays, and deletes the printed entires from the arrays. It stops when one of the arrays is completely empty.

The nullglob shell option makes non-matched globbing patterns expand to nothing rather than remain unexpanded.

Testing:

$ tree
.
|-- Dir1
|   |-- file-1
|   |-- file-2
|   |-- file-3
|   `-- file-4
|-- Dir2
|   |-- otherfile-1
|   |-- otherfile-2
|   |-- otherfile-3
|   |-- otherfile-4
|   |-- otherfile-5
|   `-- otherfile-6
`-- script.sh

2 directories, 11 files
$ bash script.sh
file-1
otherfile-1
file-2
otherfile-2
file-3
otherfile-3
file-4
otherfile-4

If this is actually about pairing up files according to their names, so that a file in one directory are paired with the corresponding file in another (as is sometimes done in e.g. bioinformatics applications, it seems), then you are better off just iterating over one of the sets and then constructing the name of the corresponding files from the other set.

Assuming the files are called something_R1_more and something_R2_more where something and more is identifying a particular R1-R2 pair of files.

for r1 in dir1/*_R1_*; do
    r2=${r1##*/}              # dir1/something_R1_more --> something_R1_more
    r2=dir2/${r2/_R1_/_R2_}   # something_R1_more      --> dir2/something_R2_more

    if [ ! -f "$r2" ]; then
        printf '%s not found\n' "$r2" >&2
        exit 1
    fi

    # process "$r1" and "$r2" here
done
1
  • Rather than deleting elements from arrays, I'd recommend iterating over the array index(es). E.g. calculate the minimum of ${! dir1names[@]} and ${! dir2names[@]}, then use for ((i=0; i<minlength; i++)); do ... something with ${dir1names[i]} and ${dir2names[i]}; done May 23, 2019 at 7:33
2

You generally don't want to parse the output of ls.

Here, with zsh (you're already using zsh syntax, not bash as you're not quoting your variables and using command substitution without disabling globbing), you could do:

dir1_file_names=(dir1/*(N:t))
dir2_file_names=(dir2/*(N:t))
for f1 f2 (${dir1_file_names:^dir2_file_names})
  printf '%s\n' "f1: $f1, f2: $f2"

zsh can loop with several variables. ${a:^b} is an array zipping operator. If one of the arrays have fewer elements than the other, it will be as if they were truncated to the length of the smallest.

See also its array intersection and subtraction operators if it's about comparing the file names in the two directories:

file_names_in_both_dir1_and_dir2=(${dir1_file_names:*dir2_file_names})
file_names_only_in_dir1=(${dir1_file_name:|dir2_file_names})
file_names_only_in_dir2=(${dir2_file_name:|dir1_file_names})
1
  • Sometimes using zsh almost feels like cheating
    – wardw
    Sep 4, 2021 at 16:34
0

The output of multiple variables can be saved in single variable as mentioned below one. Check if it works for you

#!/bin/bash
cd /
for file1 in $(ls /usr/ ; echo "::::::NEXT:::::::" ; ls /sys/)
do
    echo $file1
done

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .