I have created a simple script that i want to use to rename files based on a pattern.
The script uses find and sed to do its work
Everything is basically working, except when I use the $ to tell sed to only match the last occurrence it fails to match anything, so no renaming is performed. If I remove the $ renaming works but all occurrences are renamed which is not what I want, I specifically want to target the last occurrence.
I've followed the docs, tutorials on youtube, searched stack overflow, stack exchange, etc, non of the things i have found work or are even that relevant to my issue, which specifically is tied to the $ address char not working for me.
I've tested the script below, it demonstrates using sed with and without $ char so you can see the issue im having. The logic is organized into 2 functions, searchReplace() and searchReplaceLastMatchOnly(). The searchReplaceLastMatchOnly() function is what I need working, but it fails to match anything.
My test dir is structured as follows:
./bar
./bar/baz
./bar/foobar.txt
./bar/baz/foobar.txt
Running script in test dir as follows:
./script.sh -d "." -s "bar" -r "Bar" -p "*.txt" -e "txt"
Should change:
./bar/foobar.txt to ./bar/fooBar.txt
and:
./bar/baz/foobar.txt to ./bar/baz/fooBar.txt
--- Actual Results ---
Using searchReplace
:
Renaming ./bar/baz/foobar.txt to ./Bar/baz/fooBar.txt
Renaming ./bar/foobar.txt to ./Bar/fooBar.txt
Using searchReplaceLastMatchOnly
:
Renaming ./bar/baz/foobar.txt to ./bar/baz/foobar.txt
Renaming ./bar/foobar.txt to ./bar/foobar.txt
Here is the complete script:
# Search and replace using sed.
# $1 target The string to process
# $2 The string to serach for
# $3 The string that will repalce the search string
# Usage Example:
# result=searchReplace "targetToSearch" "Search" "Replace"
# printf "%s" "$result" # should output "targetToReplace"
function searchReplace() {
# spaces are set as defaults
target=${1:- }
search=${2:- }
replace=${3:- }
result="$(printf "%s" "$target" | sed "s/${search}/${replace}/g")"
printf "%s" "$result"
}
# Prints via printf with wrapping newline(\n) chars.
# Note: If no arguments are supplied, pretty print
# will simpy print a default string followed by
# a newline character (\n).
# Usage Exmple:
# txt="Text"
# prettyPrint "$txt"
function prettyPrint() {
# Set default text to print in case no arguments were passed
# (at the moment this is an empty string)
text=${1:-}
[[ -z $text ]] && printf "\n" || printf "\n%s\n" "$text"
#
}
# Get option values
while getopts "d:p:s:r:e:" opt; do
case $opt in
d) dirPath="$OPTARG" ;;
p) pattern="$OPTARG" ;;
s) search="$OPTARG" ;;
r) replace="$OPTARG" ;;
e) fileExt="$OPTARG" ;;
*)
prettyPrint "Error: Invalid flag $opt"
exit 1
;;
esac
done
# Defaults #
dirPath=${dirPath:-.}
pattern=${pattern:-*}
search=${search:- }
replace=${replace:- }
fileExt=${fileExt:-txt}
prettyPrint "Using searchReplace:"
find "$dirPath" -type f -name "$pattern" | while IFS= read -r original; do
modified="$(searchReplace "$original" "$search" "$replace")"
prettyPrint "Renaming $original to $modified"
#mv "$original" "$modified" | This is the goal...no using till renaming is working.
done
# The dev directory is structured as follows:
# .
# ./bar/fooBar.txt
# ./bar/baz/fooBar.txt
#
# This script when run as follows:
#
# ./script.sh -d "." -s "bar" -r "Bar" -p "*.txt" -e "txt"
#
# Should rename:
# ./bar/foobar.txt to ./bar/fooBar.txt
#
# and also rename:
# ./bar/baz/foobar.txt to ./bar/baz/fooBar.txt
#
# However when I run this script it renames as follows:
#
# ./bar/baz/foobar.txt to ./Bar/baz/fooBar.txt
#
# ./bar/foobar.txt to ./Bar/fooBar.txt
#
# As you can see the ./bar directory is also renamed to Bar, i just want the last
# occurence of bar to be renamed to Bar.
#
# I tried modifying the sed command in the searchReplace() function
# from sed "s/${search}/${replace}/g" to sed "s/${search}$/${replace}/g"
# as i read that the $ should tell sed to match only the last occurence,
# but it doesn't work.
# Below is the modified searchReplace() function that uses the $
#
function searchReplaceLastMatchOnly() {
# spaces are set as defaults
target=${1:- }
search=${2:- }
replace=${3:- }
result="$(printf "%s" "$target" | sed "s/${search}$/${replace}/g")"
printf "%s" "$result"
}
prettyPrint "Using searchReplaceLastMatchOnly:"
# here it is running
find "$dirPath" -type f -name "$pattern" | while IFS= read -r original; do
modified="$(searchReplaceLastMatchOnly "$original" "$search" "$replace")"
prettyPrint "Renaming $original to $modified"
#mv "$original" "$modified" | This is the goal...no using till renaming is working.
done