Linux rename files as dirname
Try this,
for i in */*; do mv $i $(dirname $i)/$(dirname $i).${i##*.}; done
For
loop iterates over each file in directory one by one. and mv
statement renames the each file in directory one by one.
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.
How do I rename all folders and files to lowercase on Linux?
A concise version using the "rename"
command:
find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
This avoids problems with directories being renamed before files and trying to move files into non-existing directories (e.g. "A/A"
into "a/a"
).
Or, a more verbose version without using "rename"
.
for SRC in `find my_root_dir -depth`
do
DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
if [ "${SRC}" != "${DST}" ]
then
[ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
fi
done
P.S.
The latter allows more flexibility with the move command (for example, "svn mv"
).
How to rename with prefix/suffix?
In Bash and zsh you can do this with Brace Expansion. This simply expands a list of items in braces. For example:
# echo {vanilla,chocolate,strawberry}-ice-cream
vanilla-ice-cream chocolate-ice-cream strawberry-ice-cream
So you can do your rename as follows:
mv {,new.}original.filename
as this expands to:
mv original.filename new.original.filename
Rename files in several subdirectories
You can do this with one line with:
find . -name *.ctl -exec sh -c 'mv "$1" `dirname "$1"`/name.ctl' x {} \;
The x
just allows the filename to be positional character 1 rather than 0 which (in my opinion) wrong to use as a parameter.
Batch renaming a single file (always same file name) within a folder to the folder name (100s of folders)
Suppose you have a folder with content along these lines:
$ tree -N
.
|-- name 1.textbundle
| `-- text.md
|-- name 2.something
| `-- text.md
|-- name 3.textbundle
| `-- text.md
|-- name4.textbundle
| `-- text.md
`-- name5.textbundle
`-- text.md
5 directories, 5 files
Then a script like the following, could be used to rename those text.md
files within those *.textbundle
directories, along the lines of how you suggested:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage: $0 <directory>" 1>&2
exit 1
fi
if [ ! -d "$1" ]; then
echo "$0: '$1' is not a directory" 1>&2
exit 1
fi
find . -type d -name '*.textbundle' | \
while read dirname; do
base=$(basename "$dirname" .textbundle)
echo "$dirname => $base:"
pushd "$dirname" > /dev/null
if [ -e "text.md" ]; then
mv "text.md" "$base.md"
echo " Moved $dirname/text.md => $dirname/$base.md"
fi
popd > /dev/null
done
It requires one parameter, the base directory of where you keep all these *.textbundle
directories. So if that is in ~/Documents/
for instance, you would run it like <scriptname> ~/Documents
.
Running it from the start the above example directory, it would look like this:
$ <script-name> .
./name 1.textbundle => name 1:
Moved ./name 1.textbundle/text.md => ./name 1.textbundle/name 1.md
./name5.textbundle => name5:
Moved ./name5.textbundle/text.md => ./name5.textbundle/name5.md
./name 3.textbundle => name 3:
Moved ./name 3.textbundle/text.md => ./name 3.textbundle/name 3.md
./name4.textbundle => name4:
Moved ./name4.textbundle/text.md => ./name4.textbundle/name4.md
If you run it again, it won't do any accidental damage, it just won't rename any files:
$ <script-name> .
./name 1.textbundle => name 1:
./name5.textbundle => name5:
./name 3.textbundle => name 3:
./name4.textbundle => name4:
The following script moves all those files back, in case you change your mind:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage: $0 <directory>" 1>&2
exit 1
fi
if [ ! -d "$1" ]; then
echo "$0: '$1' is not a directory" 1>&2
exit 1
fi
find . -type d -name '*.textbundle' | \
while read dirname; do
base=$(basename "$dirname" .textbundle)
echo "$dirname => $base:"
pushd "$dirname" > /dev/null
if [ -e "$base.md" ]; then
mv "$base.md" "text.md"
echo " Moved $dirname/$base.md => $dirname/text.md"
fi
popd > /dev/null
done
It too takes the base directory as the (only) parameter.
$ <revert-script-name> .
./name 1.textbundle => name 1:
Moved ./name 1.textbundle/name 1.md => ./name 1.textbundle/text.md
./name5.textbundle => name5:
Moved ./name5.textbundle/name5.md => ./name5.textbundle/text.md
./name 3.textbundle => name 3:
Moved ./name 3.textbundle/name 3.md => ./name 3.textbundle/text.md
./name4.textbundle => name4:
Moved ./name4.textbundle/name4.md => ./name4.textbundle/text.md
And it too will skip the renaming if it doesn't find an appropriate *.md
file.
Both scripts use find
to find directory names of the required name pattern, and then visit each one of them and look for an appropriate *.md
file, renaming it when found. basename
is used to take the directory name and simplify it the last part of the path, minus the .textbundle
extension.
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 intodirname
filename
andextension
.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.
Related Topics
Find Port Number of Ibm Mq Queue Manager
Why Does Printf Still Work with Rax Lower Than The Number of Fp Args in Xmm Registers
How to Sort Files in Paste Command
Remove The Lines Starting with a Character in Shell
Restoring System Directories Permissions
How to Create a File of Size More Than 2Gb in Linux/Unix
"The Launch Timed Out and Was Terminated" Error with Bumblebee on Linux
Multi-Architectural Binary Rpm and The Noarch
Running a Program Through Ssh Fails with "Error Opening Terminal: Unknown."
Why Is This Int $0X10 Bios Int Not Working on Linux
Grep,How to Search for Exact Pattern
Cvs Error: Failed to Create Lock Directory... Permission Denied
Getting Cache Details in Arm Processors - Linux
Linux Tcp Connect with Select() Fails at Testserver
Linux Grep/Sed Certain Lines - Space Removal
"Sudo" Fails with "Sudo Requires a Tty" When Executed from Putty Command Line