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
我想找到所有匹配某个模式的文件并在前缀后附加一个字符串,如果扩展名存在则保留它。有些文件有扩展名,有些则没有。
所需转换:
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