使用正则表达式重命名 linux 中的文件

Rename files in linux with regex

这其实不是问题,是我自己解决的。但我想post在这里提出我的解决方案,以节省处于相同情况下的其他人的时间和精力。

所以我遇到了必须重命名很多 (+3000) 匹配特定模式的情况。在我的例子中,文件是 syncthing 的自动备份,因此文件将重命名为:

foo.bar -> foo~20150221-1330.bar

在通过论坛和手册页进行大量搜索后,我创建了以下单行代码,它使用 find、sedxargsmv 命令恢复了原始文件名在 linux:

find . -type f  | sed -e 'p;s/\(.*\)~20[0-9]\{6\}-[0-9]\{6\}\(.*\)//' | xargs -n2 -d'\n' mv

如果您愿意,可以将 sed 部分替换为您自己的模式。该命令可以顺便处理空格(感谢 xargs 中的 -d'\n' 标志),但不能处理换行符。 我希望你们中的一些人发现这个命令有用。

好的,我将提供有关每个命令的作用的更多信息:

  • find:给出当前目录下的所有常规文件(不是目录)
  • sedp 将打印来自标准输入的每一行,s/regex/regex/ 将打印相同的行,但被替换。所以你得到每个文件后跟固定文件名:

    ./foo/bar~20150221-172703.txt
    ./foo/bar.txt`
    
  • xargs: -n2 将采用两行并将它们作为参数发送到 mv,-d'\n' 将修复文件夹名称中的空格问题(分隔符是设置为换行符而不是空格)

你的答案很好,但是如果你还想解决也包含换行符的文件名,如果你不想使用 sed 并且你想确保只有带有 [=36 的文件=]basename 匹配这个模式被考虑在内(你的方法失败了,例如,dir~20150221-172703/file,你的方法让 mv 抱怨很多关于 filefile 当文件名与你的模式不匹配时是同一个文件)你需要稍微不同地进行。


一种可能性,如果您的 find 支持 -print0 选项(GNU find 支持):让 find 吐出所有文件名,并使用 while 循环,其中 Bash(不是 sed)将执行替换。像这样:

find . -type f -print0 | while IFS= read -r -d '' file; do
    dirname=${file%/*}
    basename=${file##*/}
    # Perform the substitution only on basename
    # Since you like regex, you can use them
    if [[ $basename =~ ^(.*)~20[0-9]{6}-[0-9]{6}(.*)$ ]]; then
        new_basename=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
        echo mv "$dirname/$basename" "$dirname/$new_basename"
    fi
done

您还可以通过使用 -regex 过滤器(不是 POSIX,但 GNU find 支持它)来确保 find 仅吐出匹配的文件:

find . -regextype posix-basic -regex '.*~20[0-9]\{6\}-[0-9]\{6\}[^/]*' -type f

不确定这是否完全回答了您的问题,但至少它修复了您的方法存在的缺陷。

没有 GNU 的好东西,就很难有一些健壮和干净的东西...

您可以使用 rnm:

rnm -rs '/(.*)~20[0-9]{6}-[0-9]{6}(.*)//' -fo -dp -1 *
  1. -fo : 仅文件模式
  2. -dp:目录深度(-1 表示无限深度,即转到所有子目录)。

不过您可以完全使用自己的正则表达式模式,在这种情况下,您必须将正则表达式模式更改为基本 (BRE):

rnm --regex basic -rs '/\(.*\)~20[0-9]\{6\}-[0-9]\{6\}\(.*\)//' -fo -dp -1 *

注:

  1. 唯一无效字符是空字符和路径定界符 (/)。
  2. 默认正则表达式模式为 javascript。