在 bash/terminal 中批量重命名文件夹及其文件
Mass rename folders and their files in bash/terminal
我正在尝试使用命令 "mv" 批量重命名文件夹及其各自的文件,但进展不顺利,我希望将文件夹重命名为:
1/
2/
3/
等等
每个文件夹中的文件在此过程中也应重命名,同时保留其文件扩展名。
1.png
2.gif
3.jpg
等等
非常感谢您的帮助
是单级目录吗?或者你有子目录吗?
是这样的吗?
之前:
.
├── folderE
│ ├── conf.properties
│ ├── omg.avi
│ └── test-diff.diff
├── folder_a
│ ├── first.png
│ ├── main.jpg
│ └── second.gif
├── folder_d
│ ├── another.zip
│ └── one.mpeg
└── with space
└── file with spaces too.mov
命令:
countDir=1
for dir in *; do
cd "$dir";
countFile=1
for file in *; do
mv "$file" $countFile.${file#*.}
((countFile++))
done
cd ..
mv "$dir" $countDir
((countDir++))
done
或者,一行相同:
countDir=1; for dir in *; do cd "$dir"; countFile=1; for file in *; do mv "$file" $countFile.${file#*.}; ((countFile++)); done; cd ..; mv "$dir" $countDir; ((countDir++)); done
之后:
.
├── 1
│ ├── 1.properties
│ ├── 2.avi
│ └── 3.diff
├── 2
│ ├── 1.png
│ ├── 2.jpg
│ └── 3.gif
├── 3
│ ├── 1.zip
│ └── 2.mpeg
└── 4
└── 1.mov
重要提示:请记住,这只是一个快速而肮脏的解决方案,不会检查 files/directoris 已经命名为“1”、“2”等
编辑:使用函数而不是递归调用同一脚本的更好方法。
希望我能得到所有极端情况。这递归地下降到目录以处理深度嵌套的目录树。
警告:由于脚本会注意不覆盖现有文件,因此在某些特殊情况下编号可能会出现空白——如果目录中有文件 0.txt
并且处理的是第一个文件该目录中有一个 .txt
文件,它将被移动到 1.txt
。如果处理的第一个文件是 0.txt
,也会发生这种情况,因此 运行 脚本两次将更改编号,然后 运行 再次将其改回。
代码如下:
#!/bin/bash
handle_directory() {
local counter=0
for i in *; do
# if the file is a directory (but not a symlink),
# handle it before moving
if [ -d "$i" ] && ! [ -h "$i" ]; then
cd "$i"
handle_directory
cd ..
fi
# extract suffix
suffix="${i##*.}"
if [ "$suffix" != "$i" ]; then
extension=".$suffix"
else
# If there is no filename extension, the counter
# is the whole filename. Without this, we'd get
# 0.Makefile and suchlike.
extension=""
fi
# find a filename that isn't already taken
# this may lead to gaps in the numbering.
while dest="$counter$extension" && [ -e "$dest" ]; do
let ++counter
done
echo mv "$i" "$dest"
let ++counter
done
}
# if a parameter was given, go there to handle it.
# otherwise handle the local directory.
if ! [ -z "" ] && ! cd ""; then
echo "Could not chdir to directory "
exit -1
fi
handle_directory
总体思路是对相关目录树进行深度优先搜索。像任何树一样,目录树最好递归处理,其功能本质上可以归结为:遍历该目录下的所有东西,如果是目录,则下降并处理它,然后找到合适的文件名并重命名该东西是否是不是目录。
用过的东西:
local counter=0 # declares a function-local variable counter and initializes it
# to 0. crucially, that it is local means that every invocation
# of handle_directory has its own counter.
[ -d "$i" ] # tests if "$i" is a directory
[ -h "$i" ] # tests if "$i" is a symlink
! [ ... ] # negates the test. ! [ -h "$i" ] is true if "$i" is NOT a symlink
"${i##*.}" # a bashism that cuts off the longest prefix that matches the pattern
[ -e "$dest" ] # tests if "$dest" exists
# the first parameter with which the script is called
[ -z "" ] # tests if "" is an empty string
! cd "" # descends into the directory "". true if that failed.
阅读联机帮助页以进一步理解:
man test
man bash # that's the big one.
我正在尝试使用命令 "mv" 批量重命名文件夹及其各自的文件,但进展不顺利,我希望将文件夹重命名为:
1/
2/
3/
等等
每个文件夹中的文件在此过程中也应重命名,同时保留其文件扩展名。
1.png
2.gif
3.jpg
等等
非常感谢您的帮助
是单级目录吗?或者你有子目录吗?
是这样的吗?
之前:
.
├── folderE
│ ├── conf.properties
│ ├── omg.avi
│ └── test-diff.diff
├── folder_a
│ ├── first.png
│ ├── main.jpg
│ └── second.gif
├── folder_d
│ ├── another.zip
│ └── one.mpeg
└── with space
└── file with spaces too.mov
命令:
countDir=1
for dir in *; do
cd "$dir";
countFile=1
for file in *; do
mv "$file" $countFile.${file#*.}
((countFile++))
done
cd ..
mv "$dir" $countDir
((countDir++))
done
或者,一行相同:
countDir=1; for dir in *; do cd "$dir"; countFile=1; for file in *; do mv "$file" $countFile.${file#*.}; ((countFile++)); done; cd ..; mv "$dir" $countDir; ((countDir++)); done
之后:
.
├── 1
│ ├── 1.properties
│ ├── 2.avi
│ └── 3.diff
├── 2
│ ├── 1.png
│ ├── 2.jpg
│ └── 3.gif
├── 3
│ ├── 1.zip
│ └── 2.mpeg
└── 4
└── 1.mov
重要提示:请记住,这只是一个快速而肮脏的解决方案,不会检查 files/directoris 已经命名为“1”、“2”等
编辑:使用函数而不是递归调用同一脚本的更好方法。
希望我能得到所有极端情况。这递归地下降到目录以处理深度嵌套的目录树。
警告:由于脚本会注意不覆盖现有文件,因此在某些特殊情况下编号可能会出现空白——如果目录中有文件 0.txt
并且处理的是第一个文件该目录中有一个 .txt
文件,它将被移动到 1.txt
。如果处理的第一个文件是 0.txt
,也会发生这种情况,因此 运行 脚本两次将更改编号,然后 运行 再次将其改回。
代码如下:
#!/bin/bash
handle_directory() {
local counter=0
for i in *; do
# if the file is a directory (but not a symlink),
# handle it before moving
if [ -d "$i" ] && ! [ -h "$i" ]; then
cd "$i"
handle_directory
cd ..
fi
# extract suffix
suffix="${i##*.}"
if [ "$suffix" != "$i" ]; then
extension=".$suffix"
else
# If there is no filename extension, the counter
# is the whole filename. Without this, we'd get
# 0.Makefile and suchlike.
extension=""
fi
# find a filename that isn't already taken
# this may lead to gaps in the numbering.
while dest="$counter$extension" && [ -e "$dest" ]; do
let ++counter
done
echo mv "$i" "$dest"
let ++counter
done
}
# if a parameter was given, go there to handle it.
# otherwise handle the local directory.
if ! [ -z "" ] && ! cd ""; then
echo "Could not chdir to directory "
exit -1
fi
handle_directory
总体思路是对相关目录树进行深度优先搜索。像任何树一样,目录树最好递归处理,其功能本质上可以归结为:遍历该目录下的所有东西,如果是目录,则下降并处理它,然后找到合适的文件名并重命名该东西是否是不是目录。
用过的东西:
local counter=0 # declares a function-local variable counter and initializes it
# to 0. crucially, that it is local means that every invocation
# of handle_directory has its own counter.
[ -d "$i" ] # tests if "$i" is a directory
[ -h "$i" ] # tests if "$i" is a symlink
! [ ... ] # negates the test. ! [ -h "$i" ] is true if "$i" is NOT a symlink
"${i##*.}" # a bashism that cuts off the longest prefix that matches the pattern
[ -e "$dest" ] # tests if "$dest" exists
# the first parameter with which the script is called
[ -z "" ] # tests if "" is an empty string
! cd "" # descends into the directory "". true if that failed.
阅读联机帮助页以进一步理解:
man test
man bash # that's the big one.