Linux Rename Files as Dirname

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 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.



Related Topics



Leave a reply



Submit