“But… why?”
I know the default file manager comes with a bulk rename tool, but it was bugging me just how slow it would get sometimes compared to if I used Nemo instead. Except I ran into an issue - I can’t bulk rename files/folders the same way. There is technically a way to (sort of) get Nemo to use Thunar’s bulk rename, but it just felt too clunky.
DISCLAIMER: All I did was constantly prompt ChatGPT with different constraints and requirements until it spit out code that worked for me
As of posting this, this is what the script features:
-
Regardless of case sensitivity, identify any string in a file or folder name (doesn’t discriminate and will operate regardless of file or folder) and replace said string with whatever string you input. I imagine you can have it only rename folders, or only rename files, but for my use case I prefer it rename anything in said directory
-
Will rename parts of words (ex. if there is a file named sledgehammer.jpg and the text you want to replace is sledge, sledgehammer.jpg will be renamed to [new text]hammer.jpg). This shouldn’t be too big of an issue, as in most cases of bulk renaming, the files being renamed will have a string of characters in common, which ends up being the target text that gets replaced
-
If you hit enter without adding text, it will simply remove the text
-
If the file name contained spaces, replacing a word won’t replace the space between said word and the next word in a file/folder’s name
-
You can choose to append text to the beginning or end of said filenames (this can technically just be done with the replace text option, but I realized that too late and it’s easier to just have that as an option instead of saying “well it’s intuitive if you play around with it”)
What the script cannot do:
- Currently there is no option for a user to select specific files in a directory. In rare cases, you may have to move specific files into a separate folder and set that as the directory for the script to run with. I was messing with something like this, but it got me nowhere so I just scrapped the whole idea for now:
rename_specific_files() {
local files=("$@")
for file in "${files[@]}"; do
if [ -e "$file" ]; then
# Check if filename has spaces
if [[ $file == *" "* ]]; then
# Reverse the order of the filename after the space
dir=$(dirname "$file")
filename=$(basename "$file")
first_part="${filename%% *}"
second_part="${filename#* }"
new_name="$dir/$second_part $first_part"
else
new_name="$file"
fi
read -rp "Enter the new name for \"$file\": " -i "$new_name" new_name
mv "$file" "$new_name"
else
echo "File \"$file\" not found."
fi
done
}
The issue being that if the selected file(s) have spaces in the filenames, it just doesn’t handle it right. I don’t have enough experience coding to understand how to fix this on my own. If anyone wants to add to this, feel free. It would definitely make the script a bit more versatile
Anyways, without further ado, here is the full bash script as it stands:
#!/bin/bash
# Function to add text to filenames
add_text() {
local dir=$1
local text=$2
local position=$3
cd "$dir" || exit 1
for entry in *; do
if [ -d "$entry" ]; then
mv "$entry" "$text$entry"
elif [ -f "$entry" ]; then
if [ "$position" == "beginning" ]; then
mv "$entry" "$text$entry"
elif [ "$position" == "end" ]; then
extension="${entry##*.}"
filename="${entry%.*}"
mv "$entry" "$filename$text.$extension"
fi
fi
done
cd - > /dev/null || exit 1
}
# Function to replace text in filenames
replace_text() {
local dir=$1
local old_text=$2
local new_text=$3
cd "$dir" || exit 1
for entry in *; do
if [ -d "$entry" ]; then
new_name=$(echo "$entry" | sed "s/$old_text/$new_text/gI")
mv "$entry" "$new_name"
elif [ -f "$entry" ]; then
new_name=$(echo "$entry" | sed "s/$old_text/$new_text/gI")
mv "$entry" "$new_name"
fi
done
cd - > /dev/null || exit 1
}
# Main script
echo "Bulk renaming script"
# Choose directory through file-picker
directory=$(zenity --file-selection --directory --title="Select Directory")
if [ $? -ne 0 ]; then
echo "No directory selected. Exiting..."
exit 1
fi
# Change to the selected directory
cd "$directory" || exit 1
echo "1. Add text to filenames"
echo "2. Replace text in filenames"
read -rp "Choose an option (1/2): " option
case $option in
1)
read -rp "Enter the text to add: " text
read -rp "Should it be appended to the beginning or end? (beginning/end): " position
echo "Adding \"$text\" to filenames and folder names..."
add_text "$(pwd)" "$text" "$position"
;;
2)
read -rp "Enter the text to replace: " old_text
read -rp "Enter the new text: " new_text
echo "Replacing \"$old_text\" with \"$new_text\" in filenames and folder names..."
replace_text "$(pwd)" "$old_text" "$new_text"
;;
*)
echo "Invalid option, exiting..."
exit 1
;;
esac
echo "Done"
SECOND DISCLAIMER: While this does technically solve my stated problem, there is a good chance that a lot of this code is un-optimized and hacky. This is a glaring downside when using GPT to write code and make scripts. While it doesn’t bother me personally as long as it gets the job done, I would recommend only using this as a reference if you have more experience than me with bash scripts.
A lot of my scripts will loosely follow this schematic: prompt user with options to narrow down exactly what functions to use depending on what the user wants (within the realm of the script’s capability), and only run code based on the responses to those prompts