基于查找的批量文件重命名

7
我有一个文件夹,里面装满了像这样的图像文件:

  • 1500000704_full.jpg
  • 1500000705_full.jpg
  • 1500000711_full.jpg
  • 1500000712_full.jpg
  • 1500000714_full.jpg
  • 1500000744_full.jpg
  • 1500000745_full.jpg
  • 1500000802_full.jpg
  • 1500000803_full.jpg

我需要根据文本文件中的查找结果来重命名这些文件。文本文件中的条目如下:

  • SH103239 1500000704
  • SH103240 1500000705
  • SH103241 1500000711
  • SH103242 1500000712
  • SH103243 1500000714
  • SH103244 1500000744
  • SH103245 1500000745
  • SH103252 1500000802
  • SH103253 1500000803
  • SH103254 1500000804

因此,我希望将图像文件重命名为:

  • SH103239_full.jpg
  • SH103240_full.jpg
  • SH103241_full.jpg
  • SH103242_full.jpg
  • SH103243_full.jpg
  • SH103244_full.jpg
  • SH103245_full.jpg
  • SH103252_full.jpg
  • SH103253_full.jpg
  • SH103254_full.jpg

有什么简单的命令或脚本可以帮我完成这个任务吗?因为文件很多,手动更改不可行。我使用的是Ubuntu系统,但根据工具的需要,我可以切换到Windows系统。最好是使用Bash脚本,以便我能够学习更多,或者使用简单的Perl或Python。

谢谢

编辑:不得不更改文件名


查找文件中的条目数量与图像文件数量是否相同? - Dennis Williamson
文件中的条目数量不足以容纳所有图像。 - bcrawl
那么遍历条目比遍历文件更有效率。 - Dennis Williamson
9个回答

9
这里是一个简单的Python 2脚本,用于重命名文件。
#!/usr/bin/env python

import os

# A dict with keys being the old filenames and values being the new filenames
mapping = {}

# Read through the mapping file line-by-line and populate 'mapping'
with open('mapping.txt') as mapping_file:
    for line in mapping_file:
        # Split the line along whitespace
        # Note: this fails if your filenames have whitespace
        new_name, old_name = line.split()
        mapping[old_name] = new_name

suffix = '_full'

# List the files in the current directory
for filename in os.listdir('.'):
    root, extension = os.path.splitext(filename)
    if not root.endswith(suffix):
        # File doesn't end with this suffix; ignore it
        continue
    # Strip off the number of characters that make up suffix
    stripped_root = root[:-len(suffix)]
    if stripped_root in mapping:
        os.rename(filename, ''.join(mapping[stripped_root] + suffix + extension))

脚本中有一些本不应该硬编码的内容。这些包括映射文件的名称(mapping.txt)和文件名后缀(_full)。这些可以通过参数传递并使用sys.argv进行解释。


你好,能否告诉我如何运行这个脚本?当我运行它时,什么都没有发生。我的mapping.txt文件与上面的原始帖子相同。有任何提示将会非常棒。 - bcrawl
对不起,不用管了。我正在运行另一个脚本。这个效果很好。谢谢。 - bcrawl
3
不,"简单又好用"的方法是 perl -lane 'rename("$F[1].jpg", "$F[0].jpg")' mapping.txt。唉! - tchrist
嗨Wesley,感谢你提供的脚本。由于图像文件名以“_full”结尾,你能帮我调整一下脚本吗?脚本运行时假设我的映射文件具有相同的文件名...我已经编辑了主贴以展示我的意思...抱歉我没有表达清楚。 - bcrawl
这应该就能满足你的要求了。试试看吧。 - Wesley
太好了!非常感谢!!!你帮我省下了好几个小时的手动工作。我保证很快会学习并掌握如何编写这样的脚本。在此期间,再次感谢这个了不起的社区。 - bcrawl

5
这将解决您的问题:
#!/usr/bin/perl
while (<DATA>) {
    my($new, $old) = split;
    rename("$old.jpg", "$new.jpg")
        || die "can't rename "$old.jpg", "$new.jpg": $!";
}
__END__
SH103239 1500000704
SH103240 1500000705
SH103241 1500000711
SH103242 1500000712
SH103243 1500000714
SH103244 1500000744
SH103245 1500000745
SH103252 1500000802
SH103253 1500000803
SH103254 1500000804

使用ARGV代替DATA可以从特定输入文件中读取行。

通常对于大规模重命名操作,我更倾向于使用以下方式:

#!/usr/bin/perl
# rename script by Larry Wall
#
# eg:
#      rename 's/\.orig$//'  *.orig
#      rename 'y/A-Z/a-z/ unless /^Make/'  *
#      rename '$_ .= ".bad"'  *.f
#      rename 'print "$_: "; s/foo/bar/ if <STDIN> =~ /^y/i'  *
#      find /tmp -name '*~' -print | rename 's/^(.+)~$/.#$1/'

($op = shift) || die "Usage: rename expr [files]\n";

chomp(@ARGV = <STDIN>) unless @ARGV;

for (@ARGV) {
    $was = $_;
    eval $op;
    die if $@;  # means eval `failed'
    rename($was,$_) unless $was eq $_;
}

我有一个更全面的版本,但这应该就足够了。


我们能否使用您拥有的更全面的版本。这对其他用户也可能很有用,可以了解您的脚本还能做什么,例如mkdir等。谢谢。 - ihightower

2
#!/bin/bash

for FILE in *.jpg; do
    OLD=${FILE%.*}  # Strip off extension.
    NEW=$(awk -v "OLD=$OLD" '$2==OLD {print $1}' map.txt)
    mv "$OLD.jpg" "$NEW.jpg"
done

2
使用生成器重写Wesley的代码:

以下是代码示例:

import os, os.path

with open('mapping.txt') as mapping_file:
    mapping = dict(line.strip().split() for line in mapping_file)

rootextiter = ((filename, os.path.splitext(filename)) for filename in os.listdir('.'))
mappediter = (
    (filename, os.path.join(mapping[root], extension))
    for filename, root, extension in rootextiter
    if root in mapping
)
for oldname, newname in mappediter:
    os.rename(oldname, newname)

可能我还没有用 Python 很长时间(四五年!),但这对我来说完全无法阅读。 - Graeme Perrow
@Graeme Perrow:读了David Beazley关于生成器的文章,改变了我的人生。http://www.dabeaz.com/generators/ - hughdbrown

2
这在Bash中非常简单,假设每个文件都有一个查找文件条目并且每个文件都有一个查找条目。
#!/bin/bash
while read -r to from
do
    if [ -e "${from}_full.jpg" ]
    then
        mv "${from}_full.jpg" "${to}_full.jpg"
    fi
done < lookupfile.txt

如果查找文件的条目比文件数量多得多,则这种方法可能效率低下。反之,如果情况相反,那么迭代文件的方法可能效率低下。然而,如果两者数量接近,则这可能是最好的方法,因为它实际上不需要进行任何查找。
如果您更喜欢一个纯Bash的查找版本:
#!/bin/bash
while read -r to from
do
    lookup[from]=$to
done < lookupfile.txt

for file in *.jpg
do
    base=${file%*_full.jpg}
    mv "$file" "${lookup[base]}_full.jpg"
done

2

我修改了Wesley的代码以适应我的特定情况。我有一个映射文件"sort.txt",其中包含不同的.pdf文件和数字,以指示我希望它们以基于网站DOM操纵的输出为基础的顺序排列。我想将所有这些单独的pdf文件合并成一个单独的pdf文件,但我希望保留它们在网站上的顺序。因此,我想根据导航菜单中它们的树形位置附加编号。

1054 spellchecking.pdf
1055 using-macros-in-the-editor.pdf
1056 binding-macros-with-keyboard-shortcuts.pdf
1057 editing-macros.pdf
1058 etc........

以下是我写的代码:

import os, sys

# A dict with keys being the old filenames and values being the new filenames
mapping = {}

# Read through the mapping file line-by-line and populate 'mapping'
with open('sort.txt') as mapping_file:
    for line in mapping_file:

        # Split the line along whitespace
        # Note: this fails if your filenames have whitespace
        new_name, old_name = line.split()
        mapping[old_name] = new_name


# List the files in the current directory
for filename in os.listdir('.'):
    root, extension = os.path.splitext(filename)

    #rename, put number first to allow for sorting by name and 
    #then append original filename +e extension
    if filename in mapping:
        print "yay" #to make coding fun
        os.rename(filename, mapping[filename] + filename + extension)

我没有像_full这样的后缀,所以我不需要那段代码。除此之外,它是相同的代码,我从来没有真正接触过Python,所以这对我来说是一个很好的学习经验。


1

读取文本文件,使用当前文件名创建哈希,例如files['1500000704'] = 'SH103239'等等。然后遍历当前目录中的文件,从哈希表中获取新的文件名,并将其重命名。


使用字符串作为下标来访问数组? - tchrist
请参考@Wesley的答案,其中包含了我还没有写出来的类似代码。 - Graeme Perrow

0

这是一个有趣的小技巧:

paste -d " " lookupfile.txt lookupfile.txt | cut -d " " -f 2,3 | sed "s/\([ ]\|$\)/_full.jpg /g;s/^/mv /" | sh

0
import os,re,sys

mapping = <Insert your mapping here> #Dictionary Key value entries (Lookup)

for k,v in mapping:
    for f in os.listdir("."):
        if re.match('1500',f): #Executes code on specific files
            os.rename(f,f.replace(k,v))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接