使用UNIX批量重命名文件 - 我应该使用awk吗?

3

我有大量需要从命令行进行重命名的图像文件。我认为最好的方法是使用awk(如果不正确,请纠正)。

文件名如下:

1038282829128738912-1.jpg

每个图像文件都以“103”开头,我想用“201003”替换文件名的这一部分,保留其余部分不变...例如:

2010038282829128738912-1.jpg

这些图片存放在一个主文件夹(images)下的多个子文件夹中,但将所有图片复制到一个文件夹(images_renamed)中会更方便。
我不知道该如何开始操作,我已经搜索了awk用法,但只找到了在文件内部重命名文本的示例。
感谢您的帮助。

Perl的rename脚本可以轻松完成此任务。实际上,编写Perl的动机之一是awk对许多文件的有限支持。 rename应该已经是基于debian的发行版或http://search.cpan.org/~rmbarker/File-Rename-0.05/的一部分。 - msw
感谢您的所有答案 - 我将阅读一些并尝试其中一些。我稍后会回来标记问题已解决 - 我相信这里一定有一些正确的答案 :-) 再次感谢。 - calumbrodie
请点击此处查看:http://theunixshell.blogspot.com/2013/01/bulk-renaming-of-files-in-unix.html - Vijay
6个回答

7
如果您在UNIX上有rename命令,您应该能够使用类似以下的命令:
mkdir images_renamed
cd images_renamed
cp ../103*.jpg .
rename 103 201003 *.jpg
rename 's/FROM/TO/' FILE命令可以将FILE中所有指定的文件重命名,将第一次出现的FROM替换为TO
如果没有这个命令,你可以使用类似以下的命令:
mkdir images_renamed
for fspec in 103*.jpg ; do
    cp ${fspec} images_renamed/201003${fspec:3}
done

为了递归地完成这个任务,我会将其放入一个带有 find 命令的脚本中:
#!/usr/bin/bash

rm -rf images_renamed
ls -lR images
echo

cd images
find . -name '*.jpg' | while read -r; do
    mkdir -p "../images_renamed/$(dirname "$REPLY")"
    echo 'Copying from' [$REPLY]
    echo '          to' [../images_renamed/$REPLY] and renaming.
    echo
    cp "$REPLY" "../images_renamed/$REPLY"
    cd "$(dirname "../images_renamed/$REPLY")"
    rename 103 201003 "$(basename "$REPLY")"
    cd - >/dev/null
done

cd ..
ls -lR images_renamed

只有中间部分是必需的,其余部分是用于测试的。下面的输出显示了它的工作原理,将每个文件复制到新的目录结构并重命名相关文件。

images:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:55 dir1
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:55 dir2
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:56 dir3

images/dir1:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 102xxx.jpg
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 103xxx.jpg

images/dir2:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 103yyy.jpg

images/dir3:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:55 dir 4

images/dir3/dir 4:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 103zzz.jpg

Copying from [./dir1/102xxx.jpg]
          to [../images_renamed/./dir1/102xxx.jpg] and renaming.

Copying from [./dir1/103xxx.jpg]
          to [../images_renamed/./dir1/103xxx.jpg] and renaming.

Copying from [./dir2/103yyy.jpg]
          to [../images_renamed/./dir2/103yyy.jpg] and renaming.

Copying from [./dir3/dir 4/103zzz.jpg]
          to [../images_renamed/./dir3/dir 4/103zzz.jpg] and renaming.

images_renamed:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir1
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir2
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir3

images_renamed/dir1:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 102xxx.jpg
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 201003xxx.jpg

images_renamed/dir2:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 201003yyy.jpg

images_renamed/dir3:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir 4

images_renamed/dir3/dir 4:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 201003zzz.jpg

为了扁平化文件层次结构,您可以使用类似以下的内容:
#!/usr/bin/bash

rm -rf images_renamed
ls -lR images
echo

cd images
mkdir -p ../images_renamed
find . -name '*.jpg' | while read -r; do
    newfile="$(basename "$REPLY")"
    echo 'Copying from' [$REPLY]
    echo '          to' [../images_renamed/$newfile] and renaming.
    echo
    cp "$REPLY" "../images_renamed/$newfile"
    cd ../images_renamed
    rename 103 201003 "$newfile"
    cd - >/dev/null
done

这将输出:

cd ..
ls -lR images_renamed
images:
total 0
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:55 dir1
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:55 dir2
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:56 dir3

images/dir1:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 102xxx.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 103xxx.jpg

images/dir2:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 103yyy.jpg

images/dir3:
total 0
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:55 dir 4

images/dir3/dir 4:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 103zzz.jpg

Copying from [./dir1/102xxx.jpg]
          to [../images_renamed/102xxx.jpg] and renaming.

Copying from [./dir1/103xxx.jpg]
          to [../images_renamed/103xxx.jpg] and renaming.

Copying from [./dir2/103yyy.jpg]
          to [../images_renamed/103yyy.jpg] and renaming.

Copying from [./dir3/dir 4/103zzz.jpg]
          to [../images_renamed/103zzz.jpg] and renaming.

images_renamed:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 102xxx.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 201003xxx.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 201003yyy.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 201003zzz.jpg

但是需要记住的是,文件名冲突(不同目录下相同的文件名)会互相覆盖。


不,你需要在它周围加上一个查找。更新以展示一种方法。 - paxdiablo
谢谢您的回答。我唯一想要改变的是目标位置。不是保留从中复制文件的目录结构 - 我想将所有图像复制到“images_renamed”文件夹中。我尝试了更改您提供的脚本,但没有成功。我无法解决$REPLY和dirname、basename等之间的关系。我可以看出$REPLY是find命令匹配的完整路径和文件名,但我能否仅从此信息中提取文件名以在重命名步骤中使用(即仅使用文件名,而不是目录)? - calumbrodie
你真棒 - 谢谢。我发完帖子后就学会了这个 '$(basename $REPLY)' 小技巧。 - calumbrodie

0
在ZSH中,您可以这样做(可能在Bash中也适用,但我不确定):
for f in **/103*.jpg; mv $f some_other_directory/201003${${f:t}#103}

也许我还可以向您介绍一款我编写的工具:http://github.com/jkramer/virn。它可以让您使用Vim批量重命名文件,包括所有Vim的功能,如正则表达式等(实际上,您也可以使用任何其他$EDITOR)。

0

这样的东西可能会有所帮助:

cd images
find . -name '*.jpg' | while read -r img; do
    mkdir -p "../images_renamed/$(dirname "$img")"
    cp -v "$img" "../images_renamed/$(echo "$img" | sed -e 's/^103/201003/')"
done

注意:未经测试。不应该删除任何东西。

你真的想为每个文件创建一个目录吗? - Dennis Williamson
这不是我的工作。我会获取原始文件名(包括目录),并创建该目录及其上层可能存在的目录。你可能需要再读一遍这行。 - scy

0

以下代码应该可以正常工作:

ls -d images*
images/          images_renamed/


for i in `find images -type f`
do
  cp $i images_renamed/$(basename $i | sed s/^103/201003/)
done

如果文件名包含空格,则无法正常工作。请使用find | while read并在包含文件名的变量周围加上引号。 - Dennis Williamson
同意,但基于原帖文件名的约定,我觉得没有必要这样做。 - dogbane

0
find /path -type f -name "10*jpg" | sed 's/.*/mv &/' | sed 's/mv \(.*\/\)\(.[^/]*\)/& \120\2/' | sh

-1

虽然没有测试过,但类似这样的代码可能在bash中起作用:

for i in `ls 103*.jpg`; do cp $i newdir/201003${i:3}; done

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