Recursively rename files to ASCII Standard
I guess this would be better:
find . -type f -exec bash -c 'for f do d=${f%/*} b=${f##*/} nb=${b//[^A-Za-z0-9._-]/_}; [[ $b = "$nb" ]] || echo mv "$f" "$d/$nb"; done' _ {} +
find
will find all files (-type f
), pass them as positional arguments to this Bash snippet:
for f do
d=${f%/*} b=${f##*/} nb=${b//[^A-Za-z0-9._-]/_}
[[ $b = "$nb" ]] || echo mv "$f" "$d/$nb"
done
We split the filename into dirname d
and basename b
. We use parameter expansions to replace all the unwanted characters with underscores and save that expansion into variable nb
. We check that the expansions $b
and $nb
are distinct (so as to avoid an error with mv
), and if they are distinct, perform the renaming.
I left echo
so that nothing is actually performed, the commands are only echoed. Remove the echo
if it looks good.
Note that this can overwrite files, e.g., files a&b
and a_b
.
Recursively replacing non-standard charecters from folders and files on win10
How's this? Replacing specific characters, then anything not between space and tilde or tab, like the unicode snowman.
echo hi | set-content filä.txt,filé.txt,fil☃.txt # in script or ise
$pairs = ('ä','a'), ('é','e'), ('[^ -~\t]','_')
dir -r | where name -match '[^ -~\t]' | rename-item -newname {
$name = $_.name
foreach ($pair in $pairs) {
$name = $name -replace $pair
}
$name
} -whatif
What if: Performing the operation "Rename File" on target "Item:
C:\users\admin\foo\filä.txt Destination: C:\users\admin\foo\fila.txt".
What if: Performing the operation "Rename File" on target "Item:
C:\users\admin\foo\filé.txt Destination: C:\users\admin\foo\file.txt".
What if: Performing the operation "Rename File" on target "Item:
C:\users\admin\foo\fil☃.txt Destination: C:\users\admin\foo\fil_.txt".
How to recursively rename files and folder with iconv from Bash
I think the following does everything you want in one pass.
# Update: if this doesn't work, use read -d '' instead
find . -print0 | while IFS= read -d '$\000' f ;
do
orig_f="$f"
# Below is pure bash. You can replace with tr if you like
# f="$( echo $f | tr -d ,\' | tr "$'&'@- " "ya__" )"
f="${f// /_}" # Replace spaces with _
f="${f//\'}" # Remove single quote
f="${f//-/_}" # Replace - with _
f="${f//,}" # Remove commas
f="${f//&/y}" # Replace ampersand with y
f="${f//@/a}" # Replace at sign with a
f=$( iconv -f UTF8 -t ASCII//TRANSLIT <<< "$f" )
new_dir="$(dirname $f)"
new_f="$(basename $f)"
mkdir -p "$new_dir"
mv -i "$orig_f" "$new_dir/$new_f"
done
The find
command (no real options needed, other than -print0
to handle filenames with spaces) will send null-separated file names to the while
loop (and someone will correct my errors there, no doubt). A long list of assignments utilizing parameter expansion removes/replaces various characters; I include what I think is the equivalent pipeline using tr
as a comment. Then we run the filename through iconv
to deal with character set issues. Finally, we split the name into its path and filename components, since we may have to make a new directory before executing the mv
.
How to rename files recursively to hex base 16
There is below a bash script.
Change the values of dest_dir
and source_dir
Do not forget to make it executable with chmod +x flattener.sh
#!/bin/env bash
dest_dir="/var/tmp/flat-files"
source_dir="parent-dir"
if [ ! -d "${dest_dir}" ] ; then
mkdir -p "${dest_dir}"
fi
if [ ! -f "${dest_dir}"/index.dat ] ; then
printf "1" > "${dest_dir}"/index.dat || exit 1
fi
find "${source_dir}" -type f -print0 |\
xargs -0 bash -c ' \
for filename ; do \
index=$(cat '"${dest_dir}"'/index.dat) ;\
printf "%d" $((index+1)) > '"${dest_dir}"'/index.dat ;\
if [[ "${filename}" =~ [.] ]] ; then \
extension=."${filename##*.}" ;\
else \
extension="" ;\
fi ;\
mv -vu "${filename}" '"${dest_dir}"'/$(printf "%X" $index)"${extension}" ;\
done' flattener
Mass file rename in Linux
You can use a single find command:
find -type f -regextype posix-extended -regex '.*/[0-9]{1,3}' -exec mv -v {} {}.jpg \;
Change file names with find and iconv
The problem with using $()
in this way is that the subshell executes prior to executing the find
command, and not as part of -exec
. You can do it, but you'll need to invoke bash. Something like:
find dir/ -type f -exec bash -c 'mv "$1" "$(iconv -f UTF8 -t ASCII//TRANSLIT <<< $1)"' -- {} \;
Keep in mind this will also translit any special chars in directory names as well, which may cause the mv
to fail. If you only want to translit the filename, then you could:
find dir/ -type f -exec bash -c 'mv "$1" "${1%/*}/$(iconv -f UTF8 -t ASCII//TRANSLIT <<< ${1##*/})"' -- {} \;
which split the directory portion off and only translits the file name.
Renaming files with sed, escaping issues
Just for the records, look:
$ a='I am a Badly [named] (file) - PLEASE.rename_me.JPG'
$ # lowercase that
$ echo "${a,,}"
i am a badly [named] (file) - please.rename_me.jpg
$ # Cool! let's save that somewhere
$ b=${a,,}
$ # substitution 's/[_ ]/-/g:
$ echo "${b//[_ ]/-}"
i-am-a-badly-[named]-(file)---please.rename-me.jpg
$ # or better, yet:
$ echo "${b//[_[:space:]]/-}"
i-am-a-badly-[named]-(file)---please.rename-me.jpg
$ # Cool! let's save that somewhere
$ c=${b//[_[:space:]]/-}
$ # substitution 's/---/--/g' (??)
$ echo "${c//---/--}"
i-am-a-badly-[named]-(file)--please.rename-me.jpg
$ d=${c//---/--}
$ # substitution 's/()[]//g':
$ echo "${d//[()\[\]]/}"
i-am-a-badly-named-file--please.rename-me.jpg
$ e="${d//[()\[\]]/}"
$ # substitution 's/\./-/g':
$ echo "${e//\./-}"
i-am-a-badly-named-file--please-rename-me-jpg
$ f=${e//\./-}
$ # substitution 's/-\([^-]*\)$/\.\1/':
$ echo "${f%-*}.${f##*-}"
i-am-a-badly-named-file--please-rename-me.jpg
$ # Done!
Now, here's a 100% bash implementation of what you're trying to achieve:
#!/bin/bash
for a in "$@"; do
b=${a,,}
b=${b//[_[:space:]]/-}
b=${b//---/--}
b=${b//[()\[\]]/}
b=${b//\./-}
b=${b%-*}.${b##*-}
mv -i -- "$a" "$b"
done
yeah, done!
All this standard and known as shell parameter expansion.
Remark. For a more robust script, you could check whether a has an extension (read: a period in its name), otherwise the last substitution of the algorithm fails a little bit. For this, put the following line just below the for
statement:
[[ a != *.* ]] && { echo "Oh no, file \`$a' has no extension..."; continue; }
(and isn't the *.*
part of this line so cute?)
Related Topics
How to Sort File Names by Specific Part in Linux
Sed,Awk.Grep - Add a Line to the End of a Configuration Section If the Line Doesn't Already Exist
Find Files in Multiple Directories Taken from List in a File
Reading Input from Keyboard with X64 Linux Syscalls (Assembly)
Bash -C Variable Does Not Get Assigned
Find the Average of Fields in the Columns
Dotnetcore: Cross Platform Version of Getinvalidfilenamechars
Export_Symbol in Kernel Module | Undefined Symbol During Insmod
Libcurl Ssl Error After Fork()
Linux Equivalent of Windows Dll Forwarders or MACos Reexport_Library
Rust Linux Version Glibc Not Found - Compile for Different Glibc/Libc6 Version
Start Docker-Compose Automatically on Ec2 Startup
Torch7 Lua, Error Loading Module 'Libpaths' (Linux)
Sed Not Working [Unterminated 'S' Command]
Merge Two Files Using Awk in Linux