在Bash中使用正则表达式和x=${...}重命名文件?

4

我有一堆文件,其中包含用括号括起来的前缀代码。我想把这些前缀变成后缀,就像这样:

Finance-(4BF)-001.doc   --> Finance-001-4BF.doc
Corporate-(K04)-001.doc --> Corporate-001-K04.doc

我之前在Windows机器上编写了一个非常简单的VBScript来完成这个任务,但现在我需要在Linux上完成。经过一番繁琐的搜索,我没有找到一个简单而优雅的方式来应用正则表达式到文件名并使用匹配的正则表达式重命名文件。

目前我有以下代码:

#!/bin/bash
for i in *.doc
do
    x=${i//[\(\)]/}
    echo "$i renames to: $x"
done

这将输出以下内容:
Corporate-(K04)-001.doc renames to: Corporate-K04-001.doc
Finance-(4BF)-001.doc renames to: Finance-4BF-001.doc

我知道上面的正则表达式只是去掉文件名中的括号...但一定有一种方法可以匹配它们的内容(例如\((\w)\)),然后在重命名命令中使用该匹配项(例如$1)吧?
另外,作为一个 Linux 初学者,我不理解 x=${...} 在做什么,因为我不知道它被称作什么,所以无法通过 Google 来搜索。我猜想它是将正则表达式应用于字符串 i 但是如果这样的话,为什么不能像 $1$2 那样提取匹配项呢?
谢谢!
4个回答

3
根据您所运行的系统,应该有一个重命名工具,它实际上是一个perl脚本,可以允许您使用正则表达式。我知道在较新版本的Ubuntu中有这样一个命令,而在我的Slackware发行版中没有。

不过,您可以尝试以下方法:

rename 's/-\((.{3})\)-(.{3})/-$2-$1/' *.doc

否则,您需要依靠sed或awk。


1
实际上有两个常见的rename程序:Perl发行版中的rename(可能已预装在Ubuntu中),以及util-linux(-ng)中的rename(可能已预装在Fedora中)。它们是不同的。还有mmv和许多其他程序... - ephemient

3
构造函数${...}被称为"参数扩展",可以在Bash手册中找到。
替换功能非常基本,不支持反向引用($1)。您可以使用sed代替:
x=$(sed -E 's/\(([[:alnum:]]+)\)-([[:alnum:]]+)/\2-\1/' <<< "$i")

请注意,[[:alnum:]]在其他语言中与\w相同,但POSIX正则表达式中没有后者。有关详细信息,请参见man re_format

请参阅《高级Bash脚本指南》中的参数替换部分,了解更多有关扩展/替换语法的信息。另外,操作字符串也是值得一读的章节。 - Jonah Bishop
那个完美地运作了 - 谢谢。我唯一的进一步问题是:有没有一种方法可以使用“普通”的正则表达式(即非POSIX的)来完成这个任务,这样我就不必重新格式化我的正则表达式了? - WackGet
@WackGet 使用除 sed 之外的工具,例如 Perl 或 Bash 本身。 - ephemient
很抱歉,Bash也不理解\w,因为它使用正则表达式库,这是一个符合POSIX标准的实现。Perl可以。 - user123444555621
虽然这是一个非常老的话题,但我对一件事情感到好奇/困惑。组定位器/标识符(即\1\2等)不是从左到右编号的吗?在这种情况下,对于bash,也就是按照组括号打开的顺序来编号?如果是这样,那么为什么替换字符串中读取的是\2-\1而不是\1-\2?o_0? - Cbhihe

3
在Bash中,${parameter/pattern/string}语法不是正则表达式,而是一种glob(特殊处理#%)。请参考参数扩展
Bash有[[ =~ ]]条件表达式可以使用正则表达式,并将捕获的组放入${BASH_REMATCH[@]}数组中。
#!/bin/bash
for i in *.doc; do
    if [[ $i =~ ^(.*)-\((.*)\)-([^.]*)(..*)?$ ]]; then
        x="${BASH_REMATCH[1]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]}${BASH_REMATCH[4]}"
        echo "$i renames to: $x"
    fi
done

0

试试这个:

#!/bin/bash
for i in *.doc; do
  x=${i#*(}
  x=${x%)*}
  echo "$i renames to: ${i%%-*}-$x-${i##*-}"
done

是的。您可以将括号的左右两侧(包括括号本身)剥离。然后,如果需要连接,您可以回显您剥离的内容,但要减去一开始在括号内部的括号...虽然这种方法在bash中完全正确,但它很复杂,难以阅读,并且不能为OP提供完整的答案。是的,我知道这已经是“古老的”问题了。 - Cbhihe

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