我可以使用父文件夹的名称重命名文件夹中的文件吗?

这是一种独特的情况,可以通过一个例子更好地解释

示例1假设我有一个名为A的文件夹, B.mkvC.srt是文件夹中的两个文件,我想将这些文件重命名为A.mkvA.srt

实际的例子是,一个名为American Sniper (2014)的文件夹和American.Sniper.2014.1080p.BluRay.x264.YIFY.mkvAmerican.Sniper.2014.Subtitle.BluRay.ShaAnig.srt文件夹中的两个文件,想要将这些文件重命名为American Sniper (2014).mkvAmerican Sniper (2014).srt

例2

这应该是递归的,假设我有一个名为A的文件夹, BC是文件夹A中的两个子文件夹。 B和C的内容如下

例

应该转换为

结果

即使我在文件夹A上运行algorithm / script /命令

它应该做的第三件事是,它应该忽略隐藏文件

例

应该导致

结果

最后它应该一次在多个文件夹上工作

有多种方法可以解决这个问题。 请仔细阅读说明以获得最佳效果。

在这个答案中:

  1. Python方法
  2. find + bash方法
  3. 使用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 )。