使用sed批量重命名文件


87

目的

更改这些文件名:

  • F00001-0708-RG-biasliuyda
  • F00001-0708-CS-akgdlaul
  • F00001-0708-VF-hioulgigl

这些文件名:

  • F0001-0708-RG-biasliuyda
  • F0001-0708-CS-akgdlaul
  • F0001-0708-VF-hioulgigl

壳牌代码

去测试:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

去表演:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

我的问题

我不了解sed代码。我了解替代命令

$ sed 's/something/mv'

手段。而且我对正则表达式有所了解。但是我不明白这里发生了什么:

\(.\).\(.*\)

或在这里:

& \1\2/

在我看来,前者的意思是:“一个字符,然后是一个字符,然后是一个字符的任何长度序列”,但是肯定有更多的东西。至于后面的部分:

& \1\2/

我不知道。


Answers:


149

首先,我应该说,最简单的方法是使用prename或重命名命令。

在Ubuntu,OSX(Homebrew软件包rename,MacPorts软件包p5-file-rename)或其他使用perl重命名(prename)的系统上:

rename s/0000/000/ F0000*

或在从util-linux-ng重命名的系统上,例如RHEL:

rename 0000 000 F0000*

这比等效的sed命令更容易理解。

但是对于理解sed命令,sed联机帮助页很有帮助。如果运行man sed并搜索&(使用/命令进行搜索),则会发现它是s / foo / bar /替换中的特殊字符。

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

因此,\(.\)与第一个字符匹配,可以由引用\1。然后.匹配下一个始终为0的字符。然后\(.*\)匹配文件名的其余部分(可以由引用)\2

替换字符串使用&(原始文件名)将所有内容组合在一起,\1\2这是文件名除第二个字符(为0)之外的每个部分。

恕我直言,这是一种非常神秘的方法。如果由于某种原因重命名命令不可用,而您想使用sed进行重命名(或者您可能对重命名所做的事情太复杂了吗?),则在正则表达式中使用更明确的名称将使其更具可读性。也许像这样:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

能够看到s / search / replacement /中实际更改的内容,使其更具可读性。另外,如果您不小心运行了两次或类似的文件,它也不会继续从文件名中吸收字符。


1
在我的RHEL服务器上,重命名语法为“重命名0000 000 F0000 *”
David LeBauer 2010年

1
很有可能rename本身就是一个“重命名”链接。即rename已经“更名为”prename..例如,在Ubuntu:readlink -f $(which rename)输出/usr/bin/prename......在rename由提到的大卫是完全不同的程序。
Peter.O 2012年

1
很好,彼得。我已经更新了答案,以解决两个重命名实用程序。
爱德华·安德森

3
要对此进行调试,请最后将管道移至sh中。这些命令将回显到屏幕上。
本·马修斯

1
您确定通过管道传递随机数据是一个好建议sh吗?这是潜在危险,因为可以执行任意代码(您将数据视为代码)。
gniourf_gniourf

44

您已经有了sed的说明,现在您可以只使用shell,而无需外部命令

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

1
很好,但是您不能使用括号进行引用。
Leonidas Tsampros

26

sed几年前,我写了一篇小文章,其中包含有关批量重命名的示例:

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

例如:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

如果正则表达式包含组(例如\(subregex\),则可以在替换文本中将它们用作\1\\2等等。


请注意,不鼓励仅链接的答案(链接随着时间的流逝会变得陈旧)。请考虑在此处编辑答案并添加简介。
kleopatra 2013年

效率不高,但是可以完成数百个文件的工作。已投票。
Varun Chandak

21

最简单的方法是:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

或者,

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

这会将F00001文件名中的前缀替换为F0001。此处归功于mahesh:http : //www.debian-administration.org/articles/150


3
您应该正确引用变量插值;mv "$i" "${i/F00001/F0001}"。但是+1
三胞胎

7

sed命令

s/\(.\).\(.*\)/mv & \1\2/

替换方法:

\(.\).\(.*\)

与:

mv & \1\2

就像常规sed命令一样。但是,括号&\n标记会稍微改变它。

搜索字符串在开头匹配(并记住作为模式1)单个字符,然后是单个字符,随后是字符串的其余部分(记住是模式2)。

在替换字符串中,您可以引用这些匹配的模式以将其用作替换的一部分。您还可以将整个匹配部分称为&

因此,该sed命令正在执行的操作是mv基于原始文件(用于源)以及从字符1和3开始的命令,从而有效地删除字符2(用于目标)。它将按照以下格式为您提供一系列代码:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

等等。


1
这是一个很好的解释,但指出如何将sed命令与其他命令一起实际重命名文件可能很有用。例如:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
jcarballo 2014年

@jcarballo:它是危险的解析ls通过,管道sed通过一个shell然后通过管道!它可以使用伪造的文件名任意执行代码。问题在于数据应被视为数据,并且这里通常无需任何预防措施就可以将其序列化为代码。我希望paxdiablo可以删除此答案,因为它实际上并没有显示出良好的做法。(我偶然发现了这个问题,因为初学者| sh在出现问题的命令后随机传送了一条消息,并且看到了这个问题,并且答案认为它会更好,我感到恐惧!):)
gniourf_gniourf

3

反斜杠括号内的内容表示,“在匹配模式时,请保留此处匹配的内容”。稍后,在替换文本一侧,您可以使用“ \ 1”(第一个带括号的块),“ \ 2”(第二个块)等将这些记忆片段重新找回。


1

如果您真正要做的只是删除第二个字符,无论它是什么,都可以执行以下操作:

s/.//2

但是你的命令正在建立一个 mv命令,并将其通过管道传递到外壳上以执行。

这比您的版本更具可读性:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

删除第四个字符,因为find在每个文件名前都添加了“ ./”。


希望您可以删除此答案。虽然在OP的特定情况下可能很好,但仍有很多人看到这样的答案并且不理解它,并| sh在无效的命令后随机进行管道传输,以期希望它能正常工作更好。太恐怖了!(此外,这也不是好习惯)。希望您能理解!
gniourf_gniourf


0
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash

可怕!受到任意代码执行的限制(可能不在问题的特定上下文中,但是很多人看到这样的答案,然后尝试随机键入看起来像它的东西,这很危险!)。我希望您可以删除此答案(此外,我在这里提出了另一个好的建议)。
gniourf_gniourf

0

这是我会做的:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

然后,如果一切正常,请添加| sh到末尾。所以:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh

0

使用perl重命名(工具箱中必须具有):

rename -n 's/0000/000/' F0000*

-n当输出看起来不错时,请移除开关,以便将其重命名为真实。

警告 还有其他相同名称的工具可能无法执行此操作,因此请小心。

util-linux软件包中的重命名命令不会。

如果运行以下命令(GNU

$ rename

您会看到perlexpr,那么这似乎是正确的工具。

如果不是,请将其设置为默认值(通常已经是这种情况),Debian并派生如下Ubuntu

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

对于archlinux:

pacman -S perl-rename

对于RedHat系列发行版:

yum install prename

“ prename”包位于EPEL存储库中。


对于Gentoo:

emerge dev-perl/rename

对于* BSD:

pkg install gprename

要么 p5-File-Rename


对于Mac用户:

brew install rename

如果您的另一个发行版没有此命令,请搜索软件包管理器以进行安装或手动进行操作

cpan -i File::Rename

旧的独立版本可以在这里找到


男子改名


该工具最初由Perl的父亲Larry Wall编写。


-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done

4
欢迎来到SO。请考虑添加代码说明。它将帮助其他用户理解它。
Digvijay S

这个答案很好,但是与上面的高度支持的答案几乎是重复的答案。
埃里克·莱斯钦斯基
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.