我可以使用父文件夹的名称重命名文件夹中的文件吗?
这是一种独特的情况,可以通过一个例子更好地解释
示例1假设我有一个名为A
的文件夹, B.mkv
和C.srt
是文件夹中的两个文件,我想将这些文件重命名为A.mkv
和A.srt
。
实际的例子是,一个名为American Sniper (2014)
的文件夹和American.Sniper.2014.1080p.BluRay.x264.YIFY.mkv
和American.Sniper.2014.Subtitle.BluRay.ShaAnig.srt
文件夹中的两个文件,想要将这些文件重命名为American Sniper (2014).mkv
和American Sniper (2014).srt
例2
这应该是递归的,假设我有一个名为A
的文件夹, B
和C
是文件夹A
中的两个子文件夹。 B和C的内容如下
应该转换为
即使我在文件夹A
上运行algorithm / script /命令
它应该做的第三件事是,它应该忽略隐藏文件
例
应该导致
最后它应该一次在多个文件夹上工作
有多种方法可以解决这个问题。 请仔细阅读说明以获得最佳效果。
在这个答案中:
- Python方法
-
find
+bash
方法 - 使用globstar的bash-only方法
1. Python解决方案
Python是一种非常强大的系统管理语言,遍历目录树可以通过os.walk()
函数完成。 在下面给出的脚本中,我们正是这样做的 – 我们通过确定每个文件的完整路径,文件的扩展名以及通过os.rename()
函数重命名来查找所有文件并对每个文件进行操作。
脚本内容
#!/usr/bin/env python3 import os,sys def get_all_files(treeroot): for dir,subdirs,files in os.walk(treeroot): for f in files: if f in __file__: continue fullpath = os.path.realpath( os.path.join(dir,f) ) extension = fullpath.split('.')[-1] newpath = os.path.join(dir, dir + '.' + extension) print('Move ' + fullpath + ' to ' + newpath ) # os.rename(fullpath,newpath) def main(): top_dir="." # If directory not given, assume cwd if len(sys.argv) == 2: top_dir=sys.argv[1] get_all_files(top_dir) if __name__ == '__main__' : main()
注意:非常重要的是在# os.rename(fullpath,newpath)
之前实际重命名需要删除#
的文件。
设置脚本
脚本的所有标准规则都适用: – 将其保存为最顶层目录中的chmod +x ./add_location_name.py
– 使用chmod +x ./add_location_name.py
生成可执行文件 – 使用./add_location_name.py
运行
测试脚本
这是一个在实践中如何工作的例子。 我创建了另外两个目录, Movie A (2016)
和Movie B (2016)
。 在他们内部他们都有两个文件。 我们的脚本位于同一目录中:
$ tree . ├── add_location_name.py ├── Movie A (2014) │ ├── filea.mkv │ └── fileb.srt └── Movie B (2016) ├── filea.mkv └── fileb.srt
所以当我们运行脚本时,我们将看到以下输出:
$ ./add_location_name.py Move /home/xieerqi/testdir/Movie A (2014)/fileb.srt to ./Movie A (2014)/./Movie A (2014).srt Move /home/xieerqi/testdir/Movie A (2014)/filea.mkv to ./Movie A (2014)/./Movie A (2014).mkv Move /home/xieerqi/testdir/Movie B (2016)/fileb.srt to ./Movie B (2016)/./Movie B (2016).srt Move /home/xieerqi/testdir/Movie B (2016)/filea.mkv to ./Movie B (2016)/./Movie B (2016).mkv
2.通过find命令和-exec标志解决方案
find
命令在很多方面都很有用,特别是在多级目录树上执行操作时。 在这种特殊情况下,我们可以使用它来过滤掉所有文件,并对它们执行重命名操作。
首先,让我给你解决方案:
find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}" \;
看起来又长又吓人,对吧? 但不要担心,让我们来看看它是如何工作的。
解剖命令
首先,您需要认识到有两件事情发生: find
命令定位所有文件, bash
部分实际上是重命名部分。 在外面,我们看到的是简单的命令:
find -type f -exec \;
这只会找到所有文件(没有目录或符号链接),并在找到文件时调用其他命令。 在这种情况下,我们拥有的特定命令是bash
。
那么什么呢? bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}"
部分吗? 好吧,首先要认识到结构: bash -c 'command1;command2' arg0 arg1
。 每当使用-c
标志时,第一个命令行参数arg0
将被设置为$0
,shell名称,因此它是无关紧要的,但"{}"
很重要。 这是文件名的占位符, find
将作为参数传递给bash
。
在bash命令的内部,我们提取文件路径fp=$(dirname "$1")
,目录名称fn=$(basename "$fp")
,文件扩展名或前缀px="${1##*.}"
。 所有这一切都非常好,因为我们从最顶层的目录运行命令(非常重要!)。 最后, mv "$1" "$fp"/"$fn"."$px"' sh "{}"
将重命名find
的原始文件给我们新的文件名我们用"$fp"/"$fn"."$px"
构建的"$fp"/"$fn"."$px"
使用所有这些变量。
操作示例
该命令的测试在与以前相同的目录中执行:
$ tree . ├── Movie A (2014) │ ├── filea.mkv │ └── fileb.srt └── Movie B (2016) ├── filea.mkv └── fileb.srt 2 directories, 4 files
如果我们运行命令,使用echo
而不是mv
我们可以看到每个文件名分别被重命名。
$ find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";echo "$1" "$fp"/"$fn"."$px"' sh "> ./Movie A (2014)/fileb.srt ./Movie A (2014)/Movie A (2014).srt ./Movie A (2014)/filea.mkv ./Movie A (2014)/Movie A (2014).mkv ./Movie B (2016)/fileb.srt ./Movie B (2016)/Movie B (2016).srt ./Movie B (2016)/filea.mkv ./Movie B (2016)/Movie B (2016).mkv
请记住 :上面的命令仅使用echo
进行测试。 当你使用mv
时没有输出,所以命令是静默的。
3.Simpler方法:Bash和glob star
以上两种方法使用递归树遍历。 因为在您的示例中,目录树(电影目录和文件)中只有两个级别,我们可以简化以前的shell命令,如下所示:
for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done
同样,同样的想法 – 当你确定它正常工作时用mv
替换echo
。
测试结果是一样的:
$ tree . ├── add_location_name.py ├── Movie A (2014) │ ├── filea.mkv │ └── fileb.srt └── Movie B (2016) ├── filea.mkv └── fileb.srt 2 directories, 5 files $ for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done Movie A (2014)/filea.mkv Movie A (2014)/Movie A (2014).mkv Movie A (2014)/fileb.srt Movie A (2014)/Movie A (2014).srt Movie B (2016)/filea.mkv Movie B (2016)/Movie B (2016).mkv Movie B (2016)/fileb.srt Movie B (2016)/Movie B (2016).srt
特定
$ tree . └── A ├── B │ ├── somefile │ ├── T.txt │ ├── X.srt │ └── Z.mkv └── C ├── somefile ├── T.txt ├── W.mkv └── Y.srt 3 directories, 8 files
然后
$ find A -type f \( -name '*.mkv' -o -name '*.srt' \) -not -name '.*' -exec sh -c ' for f; do dir="${f%/*}" ; ext="${f##*.}" ; new="${dir##*/}" echo mv -- "$f" "${dir}/${new}.${ext}" done' sh {} + mv -- A/C/Y.srt A/C/C.srt mv -- A/C/W.mkv A/C/C.mkv mv -- A/B/Z.mkv A/B/B.mkv mv -- A/B/X.srt A/B/B.srt
(一旦你确定它会做你想做的事情,就删除echo
)。