bash 查找 exec:重命名文件并保留可选扩展名?

bash find exec: rename files and preserve optional extensions?

我想找到所有匹配某个模式的文件并在前缀后附加一个字符串,如果扩展名存在则保留它。有些文件有扩展名,有些则没有。

所需转换:

tools-win/foo.exe => tools-win/foo_bar.exe
tools-osx/foo     => tools-osx/foo_bar

有没有办法用 bash 参数表达式来做到这一点?我目前的尝试是:

find . -path 'tools-*/*' \
    -execdir sh -c 'mv "" "${1%.*}_.${1##.}"' _ {} bar \;`

这适用于具有扩展名的文件,但捕获没有扩展名的文件的整个前缀:

tools-win/foo.exe => tools-win/foo_bar.exe
tools-osx/foo     => tools-osx/foo_bar.foo

我可以在这里使用一个参数表达式来处理这两种情况吗?

第一次简单化:

格式化:

find | 
  while read -r f
  do case $f in
     *.???) echo mv "$f" "${f%.???}_bar${f##${f%.???}}" ;;
         *) echo mv "$f" "${f}_bar"                     ;;
     esac
  done

魔法混乱是"${f%.???}_bar${f##${f%.???}}"-
${f%.???} 是没有简单定义的扩展名的文件名。 ${f##${f%.???}} 是用于从文件前面删除所有 else 的简单定义的扩展名,因此它 (复杂派生的) ) 前述 "simplistically-defined extension"。 :)

这应该不会创建子 shell(除了 find),所以它应该非常有效。


注意脚本

重大缺陷在 .??? 中,当它命中文件名 x.v1.2.3 并将其重命名为 x.v1_bar.2.3 时,它可能不会执行您想要的操作。这可能是固定的,但在没有看到您的数据的情况下,我不想错误地详细说明它。


不过,它可以压缩成一行 -

  find | while read -r f; do case $f in *.???) echo mv "$f" "${f%.???}_bar${f##${f%.???}}" ;; *) echo mv "$f" "${f}_bar";; esac; done

我积极避免尝试将所有 工作到 中。我建议将这整个事情通过管道传输到一个文件中,并在执行之前扫描是否有问题。如果一切正常,或者异常很少,也许您可​​以手动编辑这些异常,那么您可以直接执行文件。

内部 shell 脚本可以通过剥离包括最后的 . 在内的所有内容来解析前缀,然后通过从原始字符串中剥离前缀来解析后缀。最终的字符串可以用标准的连接来组装:

PREFIX=${1%.*}
SUFFIX=${1##"${PREFIX}"}
FINAL=${PREFIX}_bar${SUFFIX}

请注意,如果 ${PREFIX} 与输入字符串相同,则 ${SUFFIX} 将为空。

因此,虽然它不是 sh 中的单行代码,但只调用 find -exec 一次而没有管道的调用如下所示:

find "${PATH_TO_SEARCH}" -path "${PATH_TO_SEARCH}/tools-*/*" \
    -execdir sh -c 'p=${1%.*};s=${1##"${p}"};mv "" "${p}_${s}"' sh {} bar \;`

您可以使用 rename、a.k.a 非常简单地做到这一点。 Perl rename。它在不同的包管理器/操作系统中伪装成不同的名称。

它有一个 -X 开关,可以删除扩展名,稍后再将其添加回来,还有一个 -a 开关,可以让您将字符串作为后缀添加到当前名称。因此,要将 _bar 添加到扩展名之前的文件名的基本部分,在所有 PNG 文件上,使用:

rename -X -a "_bar" *.png

它还能做一百万件事:

  • 添加 --dry-run 看看它在不实际做任何事情的情况下会做什么,这很漂亮
  • 使用$N将顺序计数器引入文件名
  • 当两次更改会导致文件被覆盖时,它会自动检测冲突,并在造成任何伤害之前警告您 - 这是无价的
  • 您可以将整个 Perl 脚本传递给它,以您可以想象的最复杂的方式处理文件名

您可以测试您的 rename 是否是我所指的 Perl:

file $(which rename)

如果正确,您将看到它是一个 Perl 可执行文件。如果您为您的 OS 找到了正确的一个,也许您会在这个答案中添加一条评论,说明您安装了哪个软件包来获得它。


在 macOS 上,您可以使用 homebrew 安装我指的 rename 工具,如下所示:

brew install rename