the*_*man 39 linux bash file-extension
我试图小写所有扩展程序而不管它是什么.到目前为止,根据我所看到的,您必须指定要转换为小写的文件扩展名.但是,我只想在名称中的第一个最后一个点之后小写所有内容.
.
我怎么能这样做bash
?
Igo*_*bin 66
解
您可以在一行中解决任务:
find . -name '*.*' -exec sh -c '
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
Run Code Online (Sandbox Code Playgroud)
注意:这将破坏包含换行符的文件名.但是现在忍受我.
用法示例
$ mkdir C; touch 1.TXT a.TXT B.TXT C/D.TXT
$ find .
.
./C
./C/D.TXT
./1.TXT
./a.TXT
./B.TXT
$ find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
$ find .
.
./C
./C/D.txt
./a.txt
./B.txt
./1.txt
Run Code Online (Sandbox Code Playgroud)
说明
您可以在当前目录(.
)中找到.
名称(-name '*.*'
)中包含句点的所有文件,并为每个文件运行命令:
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"
Run Code Online (Sandbox Code Playgroud)
该命令意味着:尝试将文件扩展名转换为小写(这使得sed
):
$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt
Run Code Online (Sandbox Code Playgroud)
并将结果保存到a
变量中.
如果更改了某些内容[ "$a" != "$0" ]
,请重命名该文件mv "$0" "$a"
.
正在处理的文件的名称({}
)sh -c
作为其附加参数传递给它,它在命令行中被视为$0
.它使脚本安全,因为在这种情况下,shell将{}作为数据而不是代码部分,就像在命令行中直接指定它一样.(我感谢 @gniourf_gniourf 指出了这个非常重要的问题).
如您所见,如果您{}
直接在脚本中使用,则可以在文件名中进行一些shell注入,例如:
; rm -rf * ;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,shell将把注入视为代码的一部分,并且它们将被执行.
虽然版本
脚本版本更清晰但更长一点:
find . -name '*.*' | while IFS= read -r f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Run Code Online (Sandbox Code Playgroud)
对于包含换行符的文件名,这仍然会中断.要解决此问题,您需要find
支持-print0
(如GNU find
)和Bash(以便read
支持-d
分隔符切换):
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Run Code Online (Sandbox Code Playgroud)
这仍然会打破包含尾随换行符的文件(因为它们将a=$(...)
被子shell 吸收.如果你真的想要一个万无一失的方法(你应该!),最近版本的Bash(Bash≥4.0)支持,,
这里的参数扩展最终解决方案:
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
base=${f%.*}
ext=${f##*.}
a=$base.${ext,,}
[ "$a" != "$f" ] && mv -- "$f" "$a"
done
Run Code Online (Sandbox Code Playgroud)
回到原始解决方案
或者一次性find
(回到最初的解决方案,其中包含一些非常简单的修复程序):
find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
Run Code Online (Sandbox Code Playgroud)
我添加了-type f
以便只重命名常规文件.如果没有这个,如果在文件名之前重命名目录名,您仍然可能会遇到问题.如果您还想重命名目录(以及链接,管道等),您应该使用-depth
:
find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
Run Code Online (Sandbox Code Playgroud)
以便find
执行深度优先搜索.
您可能会争辩说,bash
为每个找到的文件生成进程效率不高.这是正确的,之前的循环版本会更好.
小智 49
我用这个命令取得了成功.
rename JPG jpg *.JPG
Run Code Online (Sandbox Code Playgroud)
哪个rename
命令告诉shell重命名当前文件夹中的每个匹配项JPG
,jpg
并且所有文件名都具有扩展名JPG
.
如果您看到Bareword"JPG"不允许使用"eval 1"第1行使用"strict subs"时尝试:
rename 's/\.JPG$/.jpg/' *.JPG
Run Code Online (Sandbox Code Playgroud)
fik*_*r4n 12
这个更短但更通用,结合其他人的答案:
rename 's/\.([^.]+)$/.\L$1/' *
Run Code Online (Sandbox Code Playgroud)
模拟
对于模拟,使用-n
,即rename -n 's/\.([^.]+)$/.\L$1/' *
.通过这种方式,您可以看到在执行实际更改之前将要更改的内容.示例输出:
Happy.Family.GATHERING.JPG renamed as Happy.Family.GATHERING.jpg
Hero_from_The_Land_Across_the_River.JPG renamed as Hero_from_The_Land_Across_the_River.jpg
rAnD0m.jPg1 renamed as rAnD0m.jpg1
Run Code Online (Sandbox Code Playgroud)
关于语法的简短说明
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
\.([^.]+)$
装置的任何东西,但点(序列[^.]
在字符串(的端部)$
),点后(\.
).\L$1
表示dot(\.
)后跟第\L
1 组的小写()()$1
[^.]+
)'
而不是双引号"
来包装正则表达式以避免shell扩展thk*_*ala 10
好吧,您可以使用此代码段作为您需要的任何替代方案的核心:
#!/bin/bash
# lowerext.sh
while read f; do
if [[ "$f" = *.* ]]; then
# Extract the basename
b="${f%.*}"
# Extract the extension
x="${f##*.}"
# Convert the extension to lower case
# Note: this only works in recent versions of Bash
l="${x,,}"
if [[ "$x" != "$l" ]]; then
mv "$f" "$b.$l"
fi
else
continue
fi
done
Run Code Online (Sandbox Code Playgroud)
然后,您需要做的就是将需要重命名的文件列表提供给其标准输入.例如,对于当前目录下的所有文件和任何子目录:
find -type f | lowerext.sh
Run Code Online (Sandbox Code Playgroud)
一个小优化:
find -type f -name '*.*' | lowerext.sh
Run Code Online (Sandbox Code Playgroud)
如果你需要比这个更具体的答案,你必须更加具体......
如果您使用的是 ZSH:
zmv '(*).(*)' '$1.$2:l'
Run Code Online (Sandbox Code Playgroud)
如果你得到zsh: command not found: zmv
那么只需运行:
autoload -U zmv
Run Code Online (Sandbox Code Playgroud)
然后再试一次。
感谢这篇关于 zmv 的提示的原始文章和小写/大写替换语法的ZSH 文档。
这将为您的 '.mp3 完成工作 - 但仅在工作目录中 - 但是能够使用带有空格的文件名:
for f in *.[mM][pP]3; do mv "$f" "${f%.*}.mp3"; done
Run Code Online (Sandbox Code Playgroud)
更正:
for f in *.[mM][pP]3; do [[ "$f" =~ \.mp3$ ]] || mv "$f" "${f%.*}.mp3"; done
Run Code Online (Sandbox Code Playgroud)