我想查找所有与模式匹配的文件,并将字符串附加到前缀,并保留扩展名(如果存在)。有些文件带有扩展名,而其他文件则没有。
所需的转换:
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%.*}_$2.${1##.}"' _ {} bar \;`
这适用于具有扩展名的文件,但会捕获不具有扩展名的文件的整个前缀:
tools-win/foo.exe => tools-win/foo_bar.exe
tools-osx/foo => tools-osx/foo_bar.foo
在这里可以使用单个参数表达式来处理这两种情况吗?
答案 0 :(得分:1)
第一次简化:
格式化:
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 ,因此它 是上述(复杂派生的)前面提到的“简单定义”延期”。 :)
除了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
我积极避免尝试将所有应用于查找。我建议将整个内容通过管道传输到文件中,并在执行前扫描疯狂性。如果一切正常,或者只有很少的例外,您可以手动编辑它们,然后就可以执行文件。
答案 1 :(得分:1)
您可以使用rename
(也称为Perl rename
)非常简单地进行操作。它会在不同的程序包管理器/操作系统中以不同的名称伪装。
它具有一个-X
开关,该开关删除扩展名,然后再将其添加回去;还有一个-a
开关,允许您将字符串作为后缀添加到当前名称。因此,要在扩展名之前的文件名的基础部分中添加_bar
,请在所有PNG文件上使用:
rename -X -a "_bar" *.png
它可以做一百万件事:
--dry-run
来查看在不实际执行任何操作的情况下,这很漂亮 $N
将顺序计数器引入文件名您可以测试您的rename
是否是我所指的Perl:
file $(which rename)
,如果正确,您将看到它是Perl可执行文件。如果您找到适合您的操作系统的软件包,也许您会在此答案中添加注释,说明您安装了哪个软件包来获取它。
在macOS上,您可以安装自制软件所指的rename
工具,如下所示:
brew install rename
答案 2 :(得分:0)
内部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 "$1" "${p}_$2${s}"' sh {} bar \;`