我正试图降低所有扩展程序的大小写,无论它是什么。到目前为止,根据我所看到的,您必须指定要转换为小写的文件扩展名。但是,我只想在名称中第一个最后一个点.
之后小写一切。
我如何在bash
中执行此操作?
答案 0 :(得分:62)
<强>解决方案强>
您可以在一行中解决任务:
find . -name '*.*' -exec sh -c '
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
注意:对于包含换行符的文件名,这会中断。但是现在忍受我。
使用示例
$ 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
<强>解释强>
您可以在当前目录(.
)中找到名称中包含句点.
的所有文件(-name '*.*'
)并为每个文件运行命令:
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"
该命令意味着:尝试将文件扩展名转换为小写(使sed
):
$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt
并将结果保存到a
变量。
如果更改了某些内容[ "$a" != "$0" ]
,请重命名文件mv "$0" "$a"
。
正在处理的文件的名称({}
)作为其附加参数传递给sh -c
,并在命令行中显示为$0
。
它使脚本安全,因为在这种情况下,shell将{}作为数据而不是代码部分,就像在命令行中直接指定它一样。
(我感谢 @gniourf_gniourf 指出我这个非常重要的问题)。
如您所见,如果您在脚本中直接使用{}
,
它有可能
文件名中有一些shell注入,如:
; rm -rf * ;
在这种情况下,注入将被shell视为一部分 代码,它们将被执行。
<强>虽然版本强>
更清晰,但更长一点的脚本版本:
find . -name '*.*' | while IFS= read -r f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
对于包含换行符的文件名,这仍然会中断。要解决此问题,您需要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
对于包含尾随换行符的文件,这仍然会中断(因为它们会被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
返回原始解决方案
或者在一个find
go(回到最初的解决方案,其中包含一些非常简单的修复程序):
find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
我添加了-type f
,以便只重命名常规文件。如果没有这个,如果在文件名之前重命名目录名,您仍然可能会遇到问题。如果您还想重命名目录(以及链接,管道等),则应使用-depth
:
find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
以便find
执行深度优先搜索。
您可能会争辩说,为找到的每个文件生成bash
进程效率不高。这是正确的,之前的循环版本会更好。
答案 1 :(得分:47)
我用这个命令取得了成功。
rename JPG jpg *.JPG
其中rename
是一个命令,告诉shell将当前文件夹中JPG
的每次出现重命名为jpg
,所有文件名的扩展名为JPG
。
如果您看到Bareword“JPG”不允许使用“eval 1”第1行使用“strict subs”时尝试:
rename 's/\.JPG$/.jpg/' *.JPG
答案 2 :(得分: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
之后,您需要做的就是将需要重命名的文件列表提供给其标准输入。例如。对于当前目录下的所有文件和任何子目录:
find -type f | lowerext.sh
小优化:
find -type f -name '*.*' | lowerext.sh
如果你需要比这更具体的答案,你必须更加具体......
答案 3 :(得分:9)
这个更短但更通用,与其他答案相结合:
rename 's/\.([^.]+)$/.\L$1/' *
<强>模拟强>
对于模拟,请使用-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
关于语法的简短说明
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
\.([^.]+)$
表示在点([^.]
)$
)处除了点(\.
)之外的任何序列
.\L$1
表示点(\.
)后跟1 st 组的小写(\L
)($1
)[^.]+
)'
而不是双引号"
来包装正则表达式以避免shell扩展答案 4 :(得分:3)
这将为你的'.mp3做 - 但只在工作目录中 - 但是能够使用空格的文件名:
for f in *.[mM][pP]3; do mv "$f" "${f%.*}.mp3"; done
更正:
for f in *.[mM][pP]3; do [[ "$f" =~ \.mp3$ ]] || mv "$f" "${f%.*}.mp3"; done
答案 5 :(得分:1)
如果您安装了mmv
(=移动多个文件)且文件名最多包含一个点,则可以使用
mmv -v "*.*" "#1.#l2"
它没有多于一个点(因为*
中mmv
的匹配算法并不贪婪),但它正确处理()
和'
。例如:
$ mmv -v "*.*" "#1.#l2"
FOO.BAR.MP3 -> FOO.bar.mp3 : done
foo bar 'baz' (CD 1).MP3 -> foo bar 'baz' (CD 1).mp3 : done
不完美,但比所有find/exec/sed
内容更容易使用和记忆。
答案 6 :(得分:1)
递归地获取所有一个精细解决方案:
find -name '*.JPG' | sed 's/\(.*\)\.JPG/mv "\1.JPG" "\1.jpg"/' |sh
以上递归地将扩展名为“JPG”的文件重命名为扩展名为“jpg”的文件
答案 7 :(得分:1)
如果您使用的是ZSH:
zmv '(*).(*)' '$1.$2:l'
如果你得到zsh: command not found: zmv
,那么只需运行:
autoload -U zmv
然后再试一次。
感谢这个original article关于zmv的提示和ZSH documentation的小写/大写替换语法。
答案 8 :(得分:0)
如果您对某些文件扩展名感兴趣,例如将所有较高级别的“JPG”扩展名转换为小写“jpg”,您可以使用命令行实用程序重命名。 CD进入您想要更改的目录。然后
rename -n 's/\.JPG$/\.jpg/' *
使用-n选项来测试将要更改的内容,然后当您对结果使用时感到满意
rename 's/\.JPG$/\.jpg/' *
答案 9 :(得分:0)
如果已安装JDK,则可以简单地编译为以下内容:
import java.io.File;
public class FileRename {
public static void main(String[] args) {
long numFilesChanged = 0;
long numFilesNotChanged = 0;
try {
final File directory = new File(".");
final File[] listOfFiles = directory.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (!listOfFiles[i].isFile()) {
continue;
}
final File fileToRename = listOfFiles[i];
final String filename = fileToRename.getName();
final String woExtension = filename.substring(0, filename.lastIndexOf('.'));
final String extension = filename.substring(filename.lastIndexOf('.'));
if (extension.toLowerCase().equals(extension)) {
numFilesNotChanged++;
} else {
numFilesChanged++;
fileToRename.renameTo(new File(woExtension + extension.toLowerCase()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Changed: " + numFilesChanged);
System.out.println("Not changed:" + numFilesNotChanged);
}
}
然后运行。
如果您没有安装JDK但安装了Java,请使用在线Java编译器(例如:https://www.compilejava.net/)对其进行编译,然后将生成的.class文件放入要重命名文件并执行的文件夹中: java FileRename
。
如果您不熟悉Java或需要帮助进行编译和/或运行,请随时与我联系。
答案 10 :(得分:0)
我一直在寻找一种简单的方法(无需考虑),但最终我终于想出了办法,并提出了这个方法(当然,是在原始帖子之后)
find . -name \\*.JPG -print -exec rename s/.JPG/.jpg/ {} \\;
我在大约6万个文件上运行了该文件,并且运行良好,但是,如果要先对其进行测试,则可以使用-n
选项“重命名”。
答案 11 :(得分:-1)
所以,这些看起来像线路噪音的解决方案都很好,但是这很容易从python REPL做起(我知道OP要求bash,但python安装在很多现在都有bash的系统上...):
import os
files = os.listdir('.')
for f in files:
path, ext = os.path.splitext(f)
if ext.isupper():
os.rename(f, path + ext.lower())