1. Python 解决方案

1. Python 解决方案

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

例 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

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

例子

应该转换为

结果

即使我在文件夹上运行算法/脚本/命令A

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

例子

例子

应该导致

结果

最后它应该可以同时处理多个文件夹

答案1

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

在这个答案中:

  1. Python 方法
  2. find+bash方法
  3. 使用 globstar 的仅 bash 方法

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)

设置脚本

所有脚本的标准规则均适用: - 将其保存为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 <COMMAND> \;

这只会查找所有文件(没有目录或符号链接),并且每当找到文件时都会调用其他命令。在本例中,我们拥有的特定命令是bash

那么 bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}"part 是做什么的?首先,先了解一下结构:bash -c 'command1;command2' arg0 arg1。每当-c使用 flag 时,第一个命令行参数arg0将设置为$0shell 名称,因此它无关紧要,但"{}"很重要。这是 filename 的引号占位符,find将作为参数传递给bash

在 bash 命令内部,我们提取文件路径fp=$(dirname "$1")、目录名称fn=$(basename "$fp")和文件扩展名或前缀px="${1##*.}"。所有这些都运行得非常好,因为我们从最顶层目录运行命令(非常重要!)。最后,将使用所有这些变量mv "$1" "$fp"/"$fn"."$px"' sh "{}"将原始文件重命名find为我们构建的新文件名"$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.更简单的方法:Bash 和 glob star

上述两种方法使用递归树遍历。由于在您的示例中目录树中只有两个级别(电影目录和文件),我们可以简化之前的 shell 命令,如下所示:

for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done

再次,同样的想法-当您确定它正常工作时,请echo替换。mv

测试结果是一样的:

$ 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

答案2

鉴于

$ 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后你当然它将会做你想做的事。

相关内容