Bash Script: Auto-Rename Long Filenames

I’ve created a script using AI that, when run, looks for filenames longer than a given value, then renames and reduces them to the same given value. The script then monitors the directory for new files and reruns the renaming function on the new files only.

Additionally, for specific file types, videos in this case, it also moves the files older than a certain age (in minutes) to another directory.

Example Filename: ThisFileHasOver"40"Characters:WhichIsKindaRidiculouslyLongAndItEvenHasADateAndTimeAtTheEndOfIt_2024-07-23-21-14-41.jpeg

Renamed Filename: ThisFileHasOver40…_2024-07-23-21-14-41.jpeg

For my purposes, it keeps the last 20 characters and truncates the middle, while reducing the filename to 40 characters, excluding the file extension. It also removes most punctuation.

It is specific to media files, but with a little editing, it can be used for any files you wish.
All you would need to do is add the file extensions you want to the list(s).

I created it because some website media files download with ridiculously long filenames, which can sometimes cause issues with moving files or saving files in programs like GIMP.

Disclaimer: I’ve tested it multiple times on 5 duplicated folders with over 1500 images and videos. However, I am not a programmer nor a regular scripter, so this script is provided as is. If you decide to try it, you should first test it on a duplicated folder yourself before employing it on any other directory. Even if it works 99.9997% of the time and on one occassion it destroys your home or root directory (it cannot get to root unless you explicitly run it as root in a root directory, but still), I am not responsible. Use at your own risk! :vulcan_salute:

The Script

#!/bin/bash

# Configuration variables
MONITOR_DIR="/path/to/monitor/directory/"  # Directory to monitor
DEST_DIR="/path/to/destination/directory/"  # Directory to move files to
MIN_AGE=5940  # Minimum age of files in seconds before moving (99 minutes)
CHECK_INTERVAL=60  # How often to check the directory in seconds (1 minute)

# List of image and video file extensions
IMAGE_EXTENSIONS=("jpg" "jpeg" "png" "gif" "bmp" "jpe" "webp" "avif" "exr" "psd" "hdr" "jxl" "apng" "tif" "tiff" "tga" "raw" "heic" "heif" "svg" "eps")
VIDEO_EXTENSIONS=("mp4" "avi" "mov" "mkv" "wmv" "webm" "m4v" "flv")

# Function to truncate filenames
truncate_filename() {
    local filename="$1"
    local extension="${filename##*.}"
    local base="${filename%.*}"

    if [[ ${#base} -gt 40 ]]; then
        local last20="${base: -20}"
        echo "${base:0:17}...${last20}.${extension}"
    else
        echo "$filename"
    fi
}

# Function to get a unique filename
get_unique_filename() {
    local base_path="$1"
    local filename="$2"
    local extension="${filename##*.}"
    local base="${filename%.*}"
    local counter=1
    local new_filename="$filename"

    while [[ -e "$base_path/$new_filename" ]]; do
        new_filename="${base}_${counter}.${extension}"
        ((counter++))
    done

    echo "$new_filename"
}

# Function to move files
move_files() {
    for file in "$MONITOR_DIR"/*; do
        if [[ -f "$file" ]]; then
            filename=$(basename "$file")
            extension="${filename##*.}"

            # Check if the file is a video and meets the conditions
            if [[ " ${VIDEO_EXTENSIONS[@]} " =~ " ${extension,,} " ]]; then
                # Check if the file is older than MIN_AGE seconds
                if [[ $(($(date +%s) - $(stat -c %Y "$file"))) -ge $MIN_AGE ]]; then
                    # Get a unique filename for the destination
                    new_filename=$(get_unique_filename "$DEST_DIR" "$filename")

                    # Move the file
                    mv "$file" "$DEST_DIR/$new_filename"
                    echo "Moved $filename to $DEST_DIR/$new_filename"
                fi
            fi
        fi
    done
}

# Function to monitor and rename files
monitor_directory() {
    while true; do
        for file in "$MONITOR_DIR"/*; do
            if [[ -f "$file" ]]; then
                filename=$(basename "$file")
                extension="${filename##*.}"
                base="${filename%.*}"

                # Check if the file is an image or video and meets the conditions
                if [[ " ${IMAGE_EXTENSIONS[@]} " =~ " ${extension,,} " || " ${VIDEO_EXTENSIONS[@]} " =~ " ${extension,,} " ]]; then
                    if [[ ${#filename} -gt 40 && ! "$filename" =~ \.\.\. ]]; then
                        # Check if the file is not older than MIN_AGE seconds
                        if [[ $(($(date +%s) - $(stat -c %Y "$file"))) -le $MIN_AGE ]]; then
                            new_filename=$(truncate_filename "$filename")
                            if [[ "$new_filename" != "$filename" ]]; then
                                mv "$file" "$MONITOR_DIR/$new_filename"
                                echo "Renamed $filename to $new_filename"
                            fi
                        fi
                    fi
                fi
            fi
        done

        # Call the move_files function
        move_files

        # Sleep for CHECK_INTERVAL seconds
        sleep $CHECK_INTERVAL
    done
}

# Start monitoring the directory
monitor_directory

A More Detailed Explanation (With Instructions):

Certainly! Here’s a detailed explanation and usage instructions for the script:

Script Explanation

This Bash script is designed to monitor a specific directory for image and video files, perform two main operations:

  1. Rename long filenames
  2. Move video files to a destination directory

Key Components:

  1. Configuration Variables:

    • MONITOR_DIR: The directory to be monitored
    • DEST_DIR: The directory where video files will be moved
    • IMAGE_EXTENSIONS and VIDEO_EXTENSIONS: Lists of file extensions to process
    • MIN_AGE: Minimum age (in minutes) of files before they’re moved
    • CHECK_INTERVAL: How often the script checks the directory (in minutes)
  2. Main Functions:

    • truncate_filename(): Shortens long filenames
    • get_unique_filename(): Ensures unique filenames in the destination directory
    • move_files(): Moves video files to the destination directory
    • monitor_directory(): Main loop that oversees renaming and moving operations

Workflow:

  1. The script continuously monitors the specified directory.
  2. It renames files longer than 40 characters, truncating them with “…” in the middle.
  3. Video files older than the specified minimum age are moved to the destination directory.
  4. If a file with the same name exists in the destination, the script adds a numeric suffix to ensure uniqueness.

Usage Instructions

  1. Setup:

    • Save the script with a .sh extension (e.g., media_manager.sh).
    • Make the script executable:
      chmod +x media_manager.sh
      
  2. Configuration:

    • Open the script in a text editor.
    • Modify the following variables according to your needs:
      • MONITOR_DIR: Set this to the directory you want to monitor
      • DEST_DIR: Set this to where you want to move the video files
      • MIN_AGE: Adjust if you want to change how old files should be before moving
      • CHECK_INTERVAL: Change if you want to alter how often the script checks for files
  3. Running the Script:

    • Open a terminal and navigate to the directory containing the script.
    • Run the script:
      ./media_manager.sh
      
    • The script will run continuously until stopped.
  4. Stopping the Script:

    • To stop the script, press Ctrl+C in the terminal where it’s running.
  5. Running in the Background:

    • To run the script in the background, use:
      nohup ./media_manager.sh &
      
    • This will allow the script to continue running even if you close the terminal.
  6. Logging (Optional):

    • To log the script’s output, you can redirect it to a file:
      ./media_manager.sh > logfile.txt 2>&1 &
      
    • This will create a log file named logfile.txt in the same directory.
  7. Automating Startup:

    • To run the script automatically at system startup, you can either add the bash script to your startup applications or add it to your crontab.
      crontab -e
      
    • Add the following line:
      @reboot /path/to/media_manager.sh
      

Remember to replace /path/to/media_manager.sh with the actual path to your script.

Notes:

  • Ensure you have write permissions for both the monitored and destination directories.
  • Regularly check the log file (if you’ve set up logging) to monitor the script’s activity.
  • Be cautious when setting the MIN_AGE too low, as it might move files that are still in use.

This script provides an automated way to manage your files, keeping your directories organized by renaming long filenames and moving video files to a specified location.

1 Like

It’s working as expected now — meaning it’s not renaming older filenames with an appended number. However, I haven’t actually changed anything in the script.

The only difference is that I created an empty folder, set the script to monitor it, then copied some typical files into it. It renames only the media file types specified in the script and no longer adds a number to the end no matter how many new files are added.

So, for now, the best way to employ this script, so that it does what is expected and nothing more, is to start it up on an empty folder.

I guess I can change my auto-download directory in Firefox to a different directory until I can figure out how to get it to work on a directory with existing files. :person_shrugging:

Okay. So, I got the script completely re-written with a sleep function included. The structure is very different from the first, but it does exactly what I want it to now.

The solution was to add a function that ignores files with 3 consecutive dots “” in their filename. I also added a function that ignores files older than 99 seconds.
Critical thinking at work. :wink:

On a related note, I ended up getting another script written which runs before the rename script and simply moves the same file types from one directory to another. That is, from the Downloads folder to another folder where media files will get renamed. This is more of a failsafe to ensure the rename script doesn’t mess with, or worse delete, the wrong files.

Again, this is not part of my skill set, so I’m taking the necessary steps to limit issues.

The Move Script

#!/bin/bash

# Configuration
SOURCE_DIR="path/to/source/directory/"  # Directory to monitor
TARGET_DIR="path/to/destination/directory/"  # Directory to move files to
FILE_TYPES=("jpg" "jpeg" "png" "gif" "bmp" "jpe" "webp" "avif" "exr" "psd" "hdr" "jxl" "apng" "tif" "tiff" "tga" "raw" "heic" "heif" "svg" "eps" "mp4" "avi" "mov" "mkv" "wmv" "webm" "m4v" "flv")  # List of file types to monitor
MINUTES_NEWER=60  # Move files newer than this many minutes

# Function to move files
move_files() {
    current_time=$(date +%s)
    time_limit=$((current_time - MINUTES_NEWER * 60))

    for file in "$SOURCE_DIR"/*; do
        if [[ -f "$file" ]]; then
            file_extension="${file##*.}"
            if [[ " ${FILE_TYPES[@]} " =~ " ${file_extension} " ]]; then
                file_mod_time=$(stat -c %Y "$file")

                if [[ $file_mod_time -gt $time_limit ]]; then
                    base_name=$(basename "$file")
                    target_file="$TARGET_DIR/$base_name"
                    counter=1

                    # Handle duplicate filenames
                    while [[ -e "$target_file" ]]; do
                        target_file="$TARGET_DIR/${base_name%.*}_$counter.${base_name##*.}"
                        ((counter++))
                    done

                    mv "$file" "$target_file"
                    echo "Moved: $file to $target_file"
                fi
            fi
        fi
    done
}

# Function to run the move_files function every 30 minutes
run_periodically() {
    while true; do
        move_files
        sleep 1800  # Sleep for 30 minutes
    done
}

# Start monitoring
run_periodically

I’ve made a “final” update to the move script. I got a similar script written and noticed that it would delete files with duplicate filenames (even if the actual file is different). So, I decided to check if this would happen with my first move script… and it did.

Though it would never cause issues for me since the rename script pretty much ensures that there wouldn’t be a duplicate, I’ve updated it anyway in case someone else uses it (without a similar rename script).

1 Like

A lot of clever script writing here. Thanks for sharing. :+1:

Unfortunately, I don’t have terribly long file names that need to be shortened… but, hey… perhaps a script that turns short file names into really long ones?!

After which, I would definitely need the first script after all. :wink:

1 Like

Yeah. This is definitely a kind of niche script for people who download a good number of files from the net.

But at least now I no longer need to get warnings about a file not being able to save or move to an external drive, nor do I need to manually rename them.

It ends up being more efficient than selecting all the files while in Dolphin or XnViewMP, pressing F2, then thinking of a good naming pattern, before finally clicking “Rename/Start”.

1 Like

You know, it’s kind of inspiring … I have to download a slew of files for meetings on some nonprofit boards I volunteer on and for some reason, staff members like to leave spaces between words and dates in filenames.

The problem for me is that my nnn plugins used to open these files in libreoffice chokes on the spaces. So I have to manually strip out all the spaces on each file before being able to open it.

So you’ve inspired me to think about writing a script to strip out the spaces - although now that I think about it, there’s bound to be little utility for that already out there somewhere :thinking:

1 Like

Yeah. That little utility is krename, but depending on the number of files, krename may take a while to actually open. A script quickly analyses a folder based on given criteria, then runs. But krename is really great for files you want to give a little more attention to.

You could also use regex in combination with the mv command. Complex, though, would have to make a script for it. :sweat_smile:

And there are others: https://wiki.archlinux.org/title/List_of_applications/Utilities#Batch_renamers

I just prefer krename over the rest. It’s just as good as what XnViewMP has, allowing you to use regex and other criteria. The UI has many options — duh, it’s a KDE tool. :person_shrugging:

PS: Krename is a KDE tool, but it can be installed even if you don’t use KDE.

1 Like

Okay, my arm has been properly twisted - I will checkout krename.

As an i3 devotee I do try to stick with text-based apps but this one looks interesting enough.

An alternative would be for me to waste hours tweaking some scratch-written shell script relying on some iterative use of rename ' ' '_' * … but life is too short.

1 Like

The Thunar Bulk Renamer is pretty good too, in case you’d like to stick to GTK.

PS: Remember that the scripts above were written with AI. I read and understood them, but I can’t do one from scratch. :wink:

Also, looking at the Krename pages on kde.org and Flathub, I realise that they aren’t even advertising it properly. There is so much that that tool can do.

Excellent suggestions … as luck would have it, Thunar Bulk Renamer is already somehow on my EOS/i3 setup. Must have snuck in as an EOS default w/ Thunar … despite my using nnn as my daily file mgr - with an occasional flirtation with nemo if need be.

Apparently, all I had to do was click my heels 3x … as I’m already home. Thanks again!

1 Like

Here’s a nice simple one:

#!/bin/bash

# Directory where the files are located
DIR="/path/to/directory"

# Loop through all files in the directory
for FILE in "$DIR"/*; do
  # Check if the file name contains a space
  if [[ "$FILE" == *" "* ]]; then
    # Replace spaces with underscores
    NEW_FILE="${FILE// /_}"
    # Rename the file
    mv "$FILE" "$NEW_FILE"
  fi
done

Actually if you want, you could do this with a one-liner from the terminal like this:

for FILE in /path/to/directory/*; do mv "$FILE" "${FILE// /_}"; done
3 Likes

@BluishHumility Now I need to spend time with this and check out the finer points. Thx!

1 Like

A truly final update has been made to the rename script, which combines it with the move script.

Now that I fully trust the script is not deleting any files, I’ve fully employed it to work on my Downloads directory. This means it no longer moves media files from the Downloads directory before renaming.

Instead, it renames all media files in the Downloads directory, then every 99 minutes, it moves only video files to another directory, while performing another rename if a duplicate filename is found in the destination directory.

The full explanation and usage has also been updated accordingly. Read that for details.
Like the AI keeps saying, “Happy scripting!” :grin:

PS: If someone prefers one of the older iterations of the script, just click the “Edit History” button at the top of the original post. They are still available.


1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.