Anybody here familiar with bash scripts?

I have a small bash script. It is meant to run in a directory that has image files and video files. It then creates two subdirectories Vids and Pics and is supposed to move all the files into those two directories appropriately.

But when I run it the directories get created but no files are either copied or moved. What did I do wrong? Any bash experts here?

#!/bin/bash

echo "This script will check for the existence of 'Vids' and 'Pics' subdirectories and create them if they do not exist. It will then move all image files into 'Pics' and all video files into 'Vids'. Do you wish to proceed? (y/n)"
read proceed

if [ $proceed == "y" ]; then
  if [ ! -d "Vids" ]; then
    mkdir Vids
  fi
  if [ ! -d "Pics" ]; then
    mkdir Pics
  fi
  find . -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -exec mv {} Pics/ \;
  find . -name "*.mp4" -o -name "*.avi" -o -name "*.mkv" -o -name "*.wmv" -exec mv {} Vids/ \;
  echo "Image files have been moved to 'Pics' and video files have been moved to 'Vids'."
else
  echo "Exiting script."
fi

I don’t use find much but I think this should do it. :crossed_fingers:

#!/bin/bash

echo "This script will check for the existence of 'Vids' and 'Pics' subdirectories and create them if they do not exist. It will then move all image files into 'Pics' and all video files into 'Vids'. Do you wish to proceed? (y/n)"
read proceed

if [ $proceed == "y" ]; then
  if [ ! -d "Vids" ]; then
    mkdir Vids
  fi
  if [ ! -d "Pics" ]; then
    mkdir Pics
  fi
  find . \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" \) -exec mv "{}" Pics/ \;
  find . \( -name "*.mp4" -o -name "*.avi" -o -name "*.mkv" -o -name "*.wmv" \) -exec mv "{}" Vids/ \;
  echo "Image files have been moved to 'Pics' and video files have been moved to 'Vids'."
else
  echo "Exiting script."
fi

The script could do with improving, for instance you say you’ve moved files even if no files were found/moved.

Personally I would have simply not bothered with find, but use move (mv), if the files were there they would be moved to the correct directory, otherwise move (mv) would simply fail, and the script would continue to the next instruction.

1 Like

It was the lack of \( \) on the find command that was tripping you up.

A couple of other tips:

  1. Consider putting -maxdepth 1 on your find.
  2. The Google Shell Style Guide has some good standards, especially around quoting.
  3. Should check the return code/exit status of commands, even mkdir.
  4. Add options to read -n 1 -p 'Enter [Y|n]: '
    Change if [ "${proceed:-n}" == 'y' ]
    For help type help read on the command line.
    I normally use the double bracket keyword.
    For help type help [[ or man bash. The command type -a [[ shows it is a keyword.
  5. Alternative: shopt -s extglob ; mv *(*.jpg|*.png|*.gif) dir
  6. To debug a script use set -xv at the beginning of your script.
3 Likes

Script pasted to shellcheck.net confirms error identified in previous post
ShellCheck: SC2146 – This action ignores everything before the `-o`. Use `\( \)` to group.

Still working on some of the suggestions here. Thanks everyone. I’ll get back to you when I get a minute to try fixing it.

Another possibility is to use rsync

  • It is able to create missing directories on the way
  • it is able to move instead of copy (standard is copy)
  • it is easy to use (but you have to read the documentation :wink: )

Hi @Wayne636,

Could you try to add maxdetph -1 type -f to the find command and remove the parentheses, using -iname instead of -name which allows the ignore case to the filename?
find . -maxdepth 1 -type f -iname “^.jpg” -o -iname “.jpeg" -o -iname ".png” -o -iname “*.gif” -exec mv { } Pics/ ;

The same command for the video files.

Hope it help,
Regards

The parentheses are required if you use -o. Otherwise the -exec will only apply to the last -iname.

I had to replace “ with " for it to work.

^.jpg should be *.jpg, and you missed some other *s.

The ; should be \;, as it’s a bash special char it needs to be escaped. Without the backslash you’ll get this:

find: missing argument to `-exec'

This should work.

find . -maxdepth 1 -type f  \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" \) -exec mv {} Pics/ \;

Hi @dmt,
I agree with you, they are some misstyped characters.
However, the folllowing script runs well when I executed after creating some files.

#!/bin/bash
# Filename: rename.sh
# Description: Rename jpg and png files

count=1
for img in `find . -maxdepth 1 -type f -iname '*.png' -o -iname '*.jpg'`
do
	new=image-$count.${img##*.}
	echo "Renaming $img to $new"
	mv "$img" "$new"
	let count++
done

You can create the files like this:

touch hack.jpg new.jpg next.png

Then, you run the script.

Hope it help, regards

Hi @j8a :slight_smile:

If your point is that the find command works without brackets then that’s because the default action is to print the filename, this applies to all matches (each -iname).

When you apply an action such as -exec it no longer uses the default.

# if jpg do something elif png do something-else
find . -iname "*.jpg" -exec echo {} jpg \; -o -iname "*.png" -exec echo {} png \;

# if jpg do nothing elif png do something
find . -iname "*.jpg"  -o -iname "*.png" -exec echo {} png \;

# if jpg or png do something
find . \( -iname "*.jpg"  -o -iname "*.png" \) -exec echo {} both \;

Regards :slight_smile: