0

I have two directories with sequential content (i.e., the contents' filenames are 001.jpg, 002.jpg, 003.jpg, and so on up to a maximum of 1000 files). I want to merge them into a new directory in alternating order like so:

Dir1
├── 001.jpg
├── 002.jpg
├── 003.jpg
└── 004.jpg
Dir2
├── 002.jpg
├── 003.jpg
├── 004.jpg
└── 005.jpg

OutputDir
├── 001.jpg (001.jpg from Dir1)
├── 002.jpg (002.jpg from Dir2)
├── 003.jpg (002.jpg from Dir1)
├── 004.jpg (003.jpg from Dir2)
├── 005.jpg (003.jpg from Dir1)
├── 006.jpg (004.jpg from Dir2)
├── 007.jpg (004.jpg from Dir1)
└── 008.jpg (005.jpg from Dir2)

This is what I have but it relies on both dirs having the same names:

cp Dir1/* OutputDir/
cp --backup=existing --suffix=.2 Dir2/* OutputDir/
cd OutputDir
# next line from here: https://stackoverflow.com/questions/3211595/renaming-files-in-a-folder-to-sequential-numbers
ls | cat -n | while read n f; do mv "$f" "$n.jpg"; done

I'm hoping for a solution that doesn't rely on the file names of both directories to be the same.

munsu
  • 1,914
  • 19
  • 24

2 Answers2

2

Here's one way.

#! /bin/bash
a=(Dir1/*)
b=(Dir2/*)
for ((i=0;i<${#a[@]};++i)); do
  mv "${a[i]}" "OutputDir/$(printf '%03d.jpg' "$((2*i+1))")"
  mv "${b[i]}" "OutputDir/$(printf '%03d.jpg' "$((2*i+2))")"
done

It does assume the same number of files in Dir1 and Dir2, but your example implies that's true.

Gene
  • 46,253
  • 4
  • 58
  • 96
  • You may want to remove the *command substitution* with `ls` entirely and just allow *globbing* to fill the arrays. But, both you and ghoti get the vote for nearly simultaneous answers `:)` I was still puzzling over the logic of which file gets the next sequential number logic (or lack thereof). – David C. Rankin Jul 19 '17 at 03:05
  • Thanks very much. Done. – Gene Jul 19 '17 at 03:25
  • I forgot to indicate but yes both directories have the same number of files. – munsu Jul 19 '17 at 03:55
  • @RobinAnupol No problem. Note I added quotes iaw best practice. They're important if the filenames contain characters the shell would reinterpret. – Gene Jul 19 '17 at 04:13
2

Assuming the two input directories have the same number of files in each, you might be able to simply step through array indices, like this:

#!/usr/bin/env bash

a=(Dir1/*.jpg)
b=(Dir2/*.jpg)
n=1

for i in "${!a[@]}"; do
    mv "${a[$i]}" "OutputDir/$( printf '%03d' $n ).jpg"
    ((n++))
    mv "${b[$i]}" "OutputDir/$( printf '%03d' $n ).jpg"
    ((n++))
done

If there's the possibility they'll have different numbers of files, you'll need to define the behaviour for that and write something to handle it. Perhaps this:

#!/usr/bin/env bash

a=(Dir1/*.jpg)
b=(Dir2/*.jpg)

if [[ ${#a[@]} -gt ${#b[@]} ]]; then
        c=( ${!a[@]} )
else
        c=( ${!b[@]} )
fi

n=1

for i in "${c[@]}"; do
    echo "i=$i"
    [[ -f "${a[$i]}" ]] &&
      mv -v "${a[$i]}" "OutputDir/$( printf '%03d' $n ).jpg" && ((n++))
    [[ -f "${b[$i]}" ]] &&
      mv -v "${b[$i]}" "OutputDir/$( printf '%03d' $n ).jpg" && ((n++))
done

With this solution, if one directory has more files than the other, its files should be appended sequentially to OutputDir following the same counter. Note that ((n++)) only executes if a move is successful, which should insure that your output files are sequential without breaks.

The dependency here is that array indices must be sequential, without breaks.

ghoti
  • 45,319
  • 8
  • 65
  • 104