1

I have assembled a bash script that 1) finds all image files in a certain directory ($DIR1) and moves them to another directory ($DIR2) then 2) finds all video files in the same $DIR1 and moves them to a third directory ($DIR3), and finally finds all remaining files in $DIR1 and moves them out to a fourth directory ($DIR4). The end result is that $DIR1 is empty when the script concludes.

#!/bin/bash 

if [ "$(ls -A $DIR1)" ]; then
        echo "moving all photo files to processing directory"
        find $DIR1 -name '*' -exec file {} \; | grep -o -P '^.+: \w+ image' | cut -d':' -f1 | xargs -I{} mv --backup=numbered {} $DIR2;
    else
        echo "directory is empty, skipping"
    fi

if [ "$(ls -A $DIR1)" ]; then
    echo "moving all video files to processing directory"
    find $DIR1 -type f -exec file -N -i -- {} + | sed -n 's!: video/[^:]*$!!p' | cut -d':' -f1 | xargs -I{} mv --backup=numbered {} $DIR3;
else
    echo "directory is empty, skipping"
fi

if [ "$(ls -A $DIR1)" ]; then
    echo "moving all remaining files to manual-review directory"
find $DIR1 -type f -print0 | xargs -0 mv --backup=numbered -t $DIR4;
else
    echo "no remaining files to move, skipping"
fi

This script appears to work as intended and I have run it regularly for a number of months with results as expected. With that said, I'd like help figuring out how to make it better, specifically 1) how can I tally the number of files moved each time and print it to STDOUT, and 2) how can I add some basic error checking to ensure the script behaves as intended?

1 Answer 1

1

Here is an attempt to improve the script. The big pipelines were removed in order to more easily add any error logic. It also makes it easier to add tally logic. Also, there is a slight possible speed improvement since we read the directory once instead of three times. In addition the cut was cut (and grep and sed) by using shell variable substring removal and case statements (as well as different file arguments).

As for error checking there is quotes around the external DIR1/2/3/4 variables with as well as a test that they are directories in the beginning. I use the technique for null terminating the filenames from the Bash FAQ (here: http://mywiki.wooledge.org/BashFAQ/020). This will allow spaces in filenames should that occur. There are certainly more corner cases that one could add, like what do you want to do if mv fails or file crashes.

#!/bin/bash 

if [[ ! -d "$DIR1" || ! -d "$DIR2" || ! -d "$DIR3" || ! -d "$DIR3" ]]; then
    echo "missing environment variables or variables aren't directories" >&2
    exit 1
fi

num_images=0
num_videos=0
num_misc=0

echo STARTING
while IFS= read -r -d $'\0' file; do
   mime=$( file -b --mime-type "$file" )
   case "${mime%%/*}" in
      image)
         output="$DIR2"
         ((num_images++))
        ;;
      video)
         output="$DIR3"
         ((num_videos++))
        ;;
      *)
         output="$DIR4"
         ((num_misc++))
        ;;
    esac
    mv "$file" "$output"
done < <( find "$DIR1" -type f -print0 )

echo FINISHED: moved $num_images images, $num_videos videos, $num_misc other files
5
  • 1
    let evaluates its arguments as arithmetic. The arithmetic compound command should almost always be preferred: ((num_images++)) wiki.bash-hackers.org/commands/builtin/let Oct 18, 2022 at 1:39
  • 1
    @GillesQuenot updated as per your suggestion.
    – toppk
    Oct 18, 2022 at 1:45
  • You could add >&2 to the first echo error command to improve a bit more :) Oct 18, 2022 at 1:49
  • @GillesQuenot thanks. I was going to properly pluralize the output, but I thought that would be a distraction.
    – toppk
    Oct 18, 2022 at 1:52
  • This is very helpful, thank you! I'll do some testing this evening with these suggestions.
    – Xixor
    Oct 18, 2022 at 22:51

You must log in to answer this question.

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