Bash Script to Rename All Files in a Directory

Rename all files in directory from $filename_h to $filename_half?

Just use bash, no need to call external commands.

for file in *_h.png
do
mv "$file" "${file/_h.png/_half.png}"
done

Do not add #!/bin/sh

For those that need that one-liner:

for file in *.png; do mv "$file" "${file/_h.png/_half.png}"; done

Renaming files in a folder to sequential numbers

Try to use a loop, let, and printf for the padding:

a=1
for i in *.jpg; do
new=$(printf "%04d.jpg" "$a") #04 pad to length of 4
mv -i -- "$i" "$new"
let a=a+1
done

using the -i flag prevents automatically overwriting existing files, and using -- prevents mv from interpreting filenames with dashes as options.

Recursive bash script to rename files and folders with specific rules on Mac/Linux

Here is a working solution I have come up with so far which needs to be optimised and cleaned up, its rudimentary and I can use optimisation tips:

Solution 1 - GitHub - WIP

#!/bin/bash

set -e

################################
## WORKING
##
## TODO: optimise
##
## WIP: [optimisation 1:]
## Instead of each function looping and going through all files and folders
## have one function to do the loop and pass the files and folders as arguments
## to all the other functions.
## Continue developing function `rename_files_and_folders_dirs_1`.
##
## [optimisation 2:]
## Expand features to take user input.
## For example call the script with flags/arguments/parameters/options
## which invoke different functions passing different arguments
## such as tags to remove or tags to add.
## Note: This could likely make [optimisation 1:] redundant, so need to choose
## witch path to follow.
##
## References
## https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
## https://stackoverflow.com/questions/15012631/rename-files-and-directories-recursively-under-ubuntu-bash
## https://superuser.com/questions/213134/recursively-rename-files-change-extension-in-linux
## https://stackoverflow.com/questions/6509650/extract-directory-from-path
## https://stackoverflow.com/questions/6121091/get-file-directory-path-from-file-path/6121114
## https://stackoverflow.com/questions/13210880/replace-one-substring-for-another-string-in-shell-script
## https://tldp.org/LDP/abs/html/string-manipulation.html
## https://stackoverflow.com/questions/16623835/remove-a-fixed-prefix-suffix-from-a-string-in-bash
## https://unix.stackexchange.com/questions/311758/remove-specific-word-in-variable
## https://unix.stackexchange.com/questions/56810/adding-text-to-filename-before-extension
## https://stackoverflow.com/questions/45799657/bash-adding-a-string-to-file-name
## https://linuxize.com/post/bash-functions/
## https://bash.cyberciti.biz/guide/Pass_arguments_into_a_function
## https://stackoverflow.com/questions/6212219/passing-parameters-to-a-bash-function
## https://linuxacademy.com/blog/linux/conditions-in-bash-scripting-if-statements/
################################

declare -a STRINGS_TO_REPLACE
STRINGS_TO_REPLACE=("tag1" "tag2")

STRING_TO_ADD_IF_NOT_PRESENT="tag3"

################################

rename_files_remove_old_tags_arguments() {
filepathnodot="${1#.}"
# echo "$filepathnodot"

justfilenamenopath="${1##*/}"
# echo "$justfilenamenopath"

justpathnofile=${1%/*}
# echo "$justpathnofile"

for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$justfilenamenopath" == *"$current_string"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$1" &&
newfilename=$(echo "$justfilenamenopath" | sed "s/$current_string//g")
mv -v "$1" "$justpathnofile/$newfilename"
break;
fi
done
}

rename_files_remove_old_tags_arguments

################################

rename_files_remove_old_tags() {
while IFS= read -r -d '' n; do

filepathnodot="${n#.}"
# echo "$filepathnodot"

justfilenamenopath="${n##*/}"
# echo "$justfilenamenopath"

justpathnofile=${n%/*}
# echo "$justpathnofile"

for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$justfilenamenopath" == *"$current_string"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$n" &&
newfilename=$(echo "$justfilenamenopath" | sed "s/$current_string//g")
mv -v "$n" "$justpathnofile/$newfilename"
break;
fi
done
done < <(find . \( -type f -name "[!.]*" \) -print0)
}

rename_files_remove_old_tags

################################

rename_folders_dirs_remove_old_tags() {
while IFS= read -r -d '' n; do
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$n" == *"$current_string"* ]];
then
# echo "Will rename $n"
test -e "$n" &&
newfilename=$(echo "$n" | sed "s/$current_string//g")
mv -v "$n" "$newfilename"
break;
fi
done
done < <(find . \( -type d -name "[!.]*" \) -print0)
}

rename_folders_dirs_remove_old_tags

################################

rename_files_add_new_tags() {
while IFS= read -r -d '' n; do

filepathnodot="${n#.}"
# echo "$filepathnodot"

justfilenamenopath="${n##*/}"
# echo "$justfilenamenopath"

justpathnofile=${n%/*}
# echo "$justpathnofile"

if [[ ! "$justfilenamenopath" == *"$STRING_TO_ADD_IF_NOT_PRESENT"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$n" &&
newfilename="${justfilenamenopath%.*} $STRING_TO_ADD_IF_NOT_PRESENT.${justfilenamenopath##*.}"
mv -v "$n" "$justpathnofile/$newfilename"
fi
done < <(find . \( -type f -name "[!.]*" \) -print0)
}

rename_files_add_new_tags

################################

rename_folders_dirs_add_new_tags() {
while IFS= read -r -d '' n; do

filepathnodot="${n#.}"
# echo "$filepathnodot"

justpathnofile=${n%/*}
# echo "$justpathnofile"

if [[ ! "$n" == *"$STRING_TO_ADD_IF_NOT_PRESENT"* ]];
then
test -e "$n" &&
newfilename="$n $STRING_TO_ADD_IF_NOT_PRESENT"
mv -v "$n" "$newfilename"
fi
done < <(find . \( -type d -name "[!.]*" \) -print0)
}

rename_folders_dirs_add_new_tags

################################
################################
################################

rename_files_and_folders_dirs_1 () {
while IFS= read -r -d '' n; do
if [[ -f $n ]];
then
# echo "FILE <<< $n"
rename_files_remove_old_tags_arguments "$n"
# rename_files_add_new_tags "$n"
elif [[ -d "$n" ]];
then
echo "DIR >>> $n"
# rename_folders_dirs_remove_old_tags "$n"
# rename_folders_dirs_add_new_tags "$n"
fi
done < <(find . \( -name "[!.]*" \) -print0)
}

# rename_files_and_folders_dirs_1

################################
################################
################################

How to rename all files in a directory and subdirectories?

I'm not sure about what you really wanna do but the following construct can surely help you:

#!/bin/bash
shopt -s globstar

for path in ./**
do
[[ -d "$path" ]] && continue

[[ $path =~ ^(.*/)([^/]+)(\.[^/]*)$|^(.*/)(.+)$ ]]

dirpath="${BASH_REMATCH[1]}${BASH_REMATCH[4]}"
filename="${BASH_REMATCH[2]}${BASH_REMATCH[5]}"
extension="${BASH_REMATCH[3]}"

echo mv "$path" "$dirpath${filename// /_}$extension"
done
notes:

  • shopt -s globstar allows the glob ** to match any path length.
  • ./** is for making sure that there is at least one / in the resulting paths. It simplifies greatly the splitting of a path into its different components.
  • [[ -d "$path" ]] && continue means to skip paths that are directories.
  • [[ $path =~ ... ]] is a bash way of using a regex for splitting the path into dirname filename and extension.
  • echo ... Now that you have the different components of the filepath at hand, you can do whatever you want.


Update

As a workaround for older bash you can define a function and call it in find:

#!/bin/bash

doit() {
local path dirpath filename extension
for path
do
[[ $path =~ ^(.*/)([^/]+)(\.[^/]*)$|^(.*/)(.+)$ ]]

dirpath="${BASH_REMATCH[1]}${BASH_REMATCH[4]}"
filename="${BASH_REMATCH[2]}${BASH_REMATCH[5]}"
extension="${BASH_REMATCH[3]}"

echo mv "$path" "$dirpath${filename// /_}$extension"
done
}
export -f doit

find . -type f -exec bash -c 'doit "$0" "$@"' {} +

Just be aware that if you use an external variable inside the function (like NEWNAME in your code), you'll also have to export it: export NEWNAME="newFile".

BTW, it's not safe to capitalise your shell variables.

Rename multiple files in bash

How about

rename 's/(.*).js/_$1/' *.js

Check the syntax for rename on your system.

The above command will rename A.js to _A & so on.

If you want to retain the extension, below should help:

rename 's/(.*)/_$1/' *.js

bash script to rename all files in a directory?

So far all the answers given either:

  1. Require some non-portable tool
  2. Break horribly with filenames containing spaces or newlines
  3. Is not recursive, i.e. does not descend into sub-directories

These two scripts solve all of those problems.

Bash 2.X/3.X

#!/bin/bash

while IFS= read -r -d $'\0' file; do
dirname="${file%/*}/"
basename="${file:${#dirname}}"
echo mv "$file" "$dirname${basename%.*}_$basename"
done < <(find . -type f -print0)

Bash 4.X

#!/bin/bash

shopt -s globstar
for file in ./**; do
if [[ -f "$file" ]]; then
dirname="${file%/*}/"
basename="${file:${#dirname}}"
echo mv "$file" "$dirname${basename%.*}_$basename"
fi
done

Be sure to remove the echo from whichever script you choose once you are satisfied with it's output and run it again

Edit

Fixed problem in previous version that did not properly handle path names.

Find multiple files and rename them in Linux

You can use find to find all matching files recursively:

$ find . -iname "*dbg*" -exec rename _dbg.txt .txt '{}' \;

EDIT: what the '{}' and \; are?

The -exec argument makes find execute rename for every matching file found. '{}' will be replaced with the path name of the file. The last token, \; is there only to mark the end of the exec expression.

All that is described nicely in the man page for find:

 -exec utility [argument ...] ;
True if the program named utility returns a zero value as its
exit status. Optional arguments may be passed to the utility.
The expression must be terminated by a semicolon (``;''). If you
invoke find from a shell you may need to quote the semicolon if
the shell would otherwise treat it as a control operator. If the
string ``{}'' appears anywhere in the utility name or the argu-
ments it is replaced by the pathname of the current file.
Utility will be executed from the directory from which find was
executed. Utility and arguments are not subject to the further
expansion of shell patterns and constructs.


Related Topics



Leave a reply



Submit