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

  • 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 '16 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 '16 at 16:47
  • I want to iterate files of 2 directories 1 by 1 – Muhammad Danish Feb 12 '16 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 – Muhammad Danish Feb 12 '16 at 16:49

4 Answers4

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
thrig
  • 34,938
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})
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
Kusalananda
  • 333,661
  • 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 – Gordon Davisson May 23 '19 at 07:33
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
linux.cnf
  • 129