Home > Enterprise >  Make a list of all files in two folders then iterate through the combined list randomly
Make a list of all files in two folders then iterate through the combined list randomly

Time:10-04

I have two directories with photos that I want to manipulate to output a random order of the files each time a script is run. How would I create such a list?

d1=/home/Photos/*.jpg
d2=/mnt/JillsPC/home/Photos/*.jpg

# somehow make a combined list, files = d1   d2
# somehow randomise the file order
# during execution of the for;do;done loop, no file should be repeated

for f in $files; do
    echo $f   # full path to each file
done

CodePudding user response:

I wouldn't use variables if you don't have to. It's more natural if you chain a couple of commands together with pipes or process substitution. That way everything operates on streams of data without loading the entire list of names into memory all at once.

You can use shuf to randomly permute input lines, and find to list files one per line. Or, to be maximally safe, let's use \0 separators. Finally, a while loop with process substitution reads line by line into a variable.

while IFS= read -d $'\0' -r file; do
    echo "$file"
done < <(find /home/Photos/ /mnt/JillsPC/home/Photos/ -name '*.jpg' -print0 | shuf -z)

That said, if you do want to use some variables then you should use arrays. Arrays handle file names with whitespace and other special characters correctly, whereas regular string variables muck them all up.

d1=(/home/Photos/*.jpg)
d2=(/mnt/JillsPC/home/Photos/*.jpg)
files=("${d1[@]}" "${d2[@]}")

Iterating in order would be easy:

for file in "${files[@]}"; do
    echo "$file"
done

Shuffling is tricky though. shuf is still the best tool but it works best on a stream of data. We can use printf to print each file name with the trailing \0 we need to make shuf -z happy.

d1=(/home/Photos/*.jpg)
d2=(/mnt/JillsPC/home/Photos/*.jpg)
files=("${d1[@]}" "${d2[@]}")

while IFS= read -d $'\0' -r file; do
    echo "$file"
done < <(printf '%s\0' "${files[@]}" | shuf -z)

Further reading:

  1. How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?
  2. How can I find and safely handle file names containing newlines, spaces or both?
  3. I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?
  4. How can I randomize (shuffle) the order of lines in a file? Or select a random line from a file, or select a random file from a directory?
  • Related