【问题标题】:Using sed to mass rename files使用 sed 批量重命名文件
【发布时间】:2011-01-23 06:47:55
【问题描述】:

目标

更改这些文件名:

  • 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/

我不知道。

【问题讨论】:

标签: bash shell sed file-rename


【解决方案1】:

反斜杠括号的意思是,“在匹配模式时,保留这里匹配的东西。”稍后,在替换文本方面,您可以使用“\1”(第一个带括号的块)、“\2”(第二个块)等来取回那些记住的片段。

【讨论】:

    【解决方案2】:

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

    在 Ubuntu、OSX(Homebrew 包 rename、MacPorts 包 p5-file-rename)或其他带有 perl 重命名(前名)的系统上:

    rename s/0000/000/ F0000*
    

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

    rename 0000 000 F0000*
    

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

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

      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/ 使其更具可读性。也不会保留 如果您不小心运行它,会从文件名中吸取字符 两次或什么的。

    【讨论】:

    • 在我的 RHEL 服务器上,重命名语法为“rename 0000 000 F0000*”
    • 很可能rename 本身就是一个“重命名” 链接。即rename 已从prename“重命名”。例如,在Ubuntu 中:readlink -f $(which rename) 输出/usr/bin/prename ... David提到的rename > 完全是一个不同的程序。
    • 好点,彼得。我已经更新了解决这两个重命名实用程序的答案。
    • 要调试它,请在最后将管道删除到 sh 中。命令将在屏幕上回显。
    • 您确定通过sh 传递随机数据是一个好建议吗?这是潜在的危险,因为可以执行任意代码(您将数据视为代码)。
    【解决方案3】:

    括号捕获特定字符串以供反斜杠数字使用。

    【讨论】:

      【解决方案4】:

      你已经有了你的 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
      

      【讨论】:

      • 很好,但你不能用括号做引用。
      【解决方案5】:

      sed 命令

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

      表示替换:

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

      与:

      mv & \1\2
      

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

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

      在替换字符串中,您可以参考这些匹配的模式以将它们用作替换的一部分。您也可以将整个匹配部分称为&

      所以sed 命令所做的是基于原始文件(用于源文件)和字符 1 和 3 之后创建一个mv 命令,有效地删除字符 2(用于目标文件)。它会按照以下格式为您提供一系列行:

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

      等等。

      【讨论】:

      • 这是一个很好的解释,但指出如何将 sed 命令与其他命令一起使用来实际重命名文件可能会很有用。例如:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
      • @jcarballo:解析ls、通过sed然后通过shell 进行管道是危险的! 它可以使用伪造的文件名执行任意代码。问题是数据应该被视为数据,在这里它通常被序列化为代码而没有任何预防措施。我希望 paxdiablo 可以删除此答案,因为它确实没有显示出良好的做法。 (我偶然发现了这个问题,因为一个初学者在一个不起作用的命令之后随机传送| sh,在看到这个问题之后,答案认为它会更好——我很害怕!):)
      【解决方案6】:

      如果您真正要做的只是删除第二个字符,无论它是什么,您都可以这样做:

      s/.//2
      

      但是您的命令正在构建一个 mv 命令并将其通过管道传送到 shell 以供执行。

      这并不比你的版本更具可读性:

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

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

      【讨论】:

      • 我希望你能删除这个答案。虽然在 OP 的特定情况下它可能很好,但有很多人看到这样的答案并且不理解它,并在一个不起作用的命令之后随机管道 | sh,希望它会工作得更好。太可怕了! (此外,这不是一个好习惯)。希望你能理解!
      【解决方案7】:
       ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash
      

      【讨论】:

      • 太糟糕了!受到任意代码执行的影响(可能不是在问题的特定上下文中,但有很多人看到这样的答案并尝试随机输入看起来像它的东西,这很危险!)。我希望您可以删除此答案(此外,您在这里还有另一个好答案,我赞成)。
      【解决方案8】:

      几年前,我写了一篇小帖子,其中包含有关使用 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 等。

      【讨论】:

      • 请注意,不鼓励仅链接的答案(链接会随着时间的推移变得陈旧)。请考虑在此处编辑您的答案并添加概要。
      • 效率不高,但可以完成几百个文件的工作。赞成。
      【解决方案9】:

      我会这样做:

      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
      

      【讨论】:

        【解决方案10】:

        最简单的方法是:

        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

        【讨论】:

        • 你应该正确引用变量插值; mv "$i" "${i/F00001/F0001}"。但是+1
        【解决方案11】:
        for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done
        

        【讨论】:

        • 欢迎来到 SO。请考虑添加您的代码的解释。它将帮助其他用户理解它。
        • 这个答案很好,但它与上面高度赞成的答案几乎重复。
        【解决方案12】:

        使用 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
        

        如果您在其他发行版中没有此命令,请搜索您的包管理器进行安装或do it manually

        cpan -i File::Rename
        

        可以找到旧的独立版本here


        man rename


        这个工具最初是由 Perl 的父亲 Larry Wall 编写的。

        【讨论】:

          【解决方案13】:

          一些对我有用的例子:

          $ tree -L 1 -F .
          .
          ├── A.Show.2020.1400MB.txt
          └── Some Show S01E01 the Loreming.txt
          
          0 directories, 2 files
          
          ## remove "1400MB" (I: ignore case) ...
          
          $ for f in *; do mv 2>/dev/null -v "$f" "`echo $f | sed -r 's/.[0-9]{1,}mb//I'`"; done;
          renamed 'A.Show.2020.1400MB.txt' -> 'A.Show.2020.txt'
          
          ## change "S01E01 the" to "S01E01 The"
          ## \U& : change (here: regex-selected) text to uppercase;
          ##       note also: no need here for `\1` in that regex expression
          
          $ for f in *; do mv 2>/dev/null "$f" "`echo $f | sed -r "s/([0-9] [a-z])/\U&/"`"; done
          
          $ tree -L 1 -F .
          .
          ├── A.Show.2020.txt
          └── Some Show S01E01 The Loreming.txt
          
          0 directories, 2 files
          $ 
          

          【讨论】:

            猜你喜欢
            • 2010-10-10
            • 1970-01-01
            • 2013-12-19
            • 1970-01-01
            • 2011-03-05
            • 2020-10-08
            • 2015-07-12
            • 2017-04-20
            相关资源
            最近更新 更多