Linux 将所有文件扩展名转换为小写
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11818408/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert all file extensions to lower-case
提问by thevoipman
I'm trying to lower-case all my extensions regardless of what it is. So far, from what I've seen, you have to specify what file extensions you want to convert to lower-case. However, I just want to lower-case everything after the firstlast dot .
in the name.
我试图小写我所有的扩展,不管它是什么。到目前为止,根据我所见,您必须指定要转换为小写的文件扩展名。但是,我只想小写名称中第一个最后一个点之后的所有内容.
。
How can I do that in bash
?
我怎样才能做到这一点bash
?
采纳答案by Igor Chubin
Solution
解决方案
You can solve the task in one line:
您可以在一行中解决任务:
find . -name '*.*' -exec sh -c '
a=$(echo "$ 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 "a=$(echo "$ echo 1.txt | sed -r "s/([^.]*)$/\L/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)$/\L/"
2.txt
" | sed -r "s/([^.]*)$/\L/");
[ "$a" != "; rm -rf * ;
" ] && mv "{}" "$a"
" | sed -r "s/([^.]*)$/\L/"); [ "$a" != "find . -name '*.*' | while IFS= read -r f
do
a=$(echo "$f" | sed -r "s/([^.]*)$/\L/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
" ] && mv "find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
a=$(echo "$f" | sed -r "s/([^.]*)$/\L/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
" "$a" ' {} \;
$ find .
.
./C
./C/D.txt
./a.txt
./B.txt
./1.txt
" | sed -r "s/([^.]*)$/\L/");
[ "$a" != "find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
base=${f%.*}
ext=${f##*.}
a=$base.${ext,,}
[ "$a" != "$f" ] && mv -- "$f" "$a"
done
" ] && mv "find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "#!/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
" ] && mv -- "find -type f | lowerext.sh
" "$a"' {} \;
" ] && mv -- "find -type f -name '*.*' | lowerext.sh
" "$a"' {} \;
" "$a" ' {} \;
Note: this will break for filenames that contain newlines. But bear with me for now.
注意:对于包含换行符的文件名,这将中断。但暂时先忍受我吧。
Example of usage
使用示例
for f in *.[mM][pP]3; do mv "$f" "${f%.*}.mp3"; done
Explanation
解释
You find all files in current directory (.
) that have period .
in its name (-name '*.*'
) and run the command for each file:
您在当前目录 ( .
) 中查找.
名称中包含句点( ) 的所有文件,-name '*.*'
并为每个文件运行以下命令:
for f in *.[mM][pP]3; do [[ "$f" =~ \.mp3$ ]] || mv "$f" "${f%.*}.mp3"; done
That command means: try to convert file extension to lowercase (that makes sed
):
该命令的意思是:尝试将文件扩展名转换为小写(即sed
):
mmv -v "*.*" "#1.#l2"
and save the result to the a
variable.
并将结果保存到a
变量中。
If something was changed [ "$a" != "$0" ]
, rename the file mv "$0" "$a"
.
如果更改了某些内容[ "$a" != "$0" ]
,请重命名该文件mv "$0" "$a"
。
The name of the file being processed ({}
) passed to sh -c
as its additional argument and it is seen inside the command line as $0
.
It makes the script safe, because in this case the shell take {} as a data, not as a code-part, as when it is specified directly in the command line.
(I thank@gniourf_gniourf for pointing me at this really important issue).
正在处理的文件的名称 ( {}
)sh -c
作为其附加参数传递给它,它在命令行中显示为$0
. 它使脚本安全,因为在这种情况下,shell 将 {} 作为数据,而不是作为代码部分,因为它是在命令行中直接指定的。(我感谢@gniourf_gniourf指出这个非常重要的问题)。
As you can see, if you use {}
directly in the script,
it's possible to have
some shell-injections in the filenames, something like:
如您所见,如果您{}
直接在脚本中使用,则文件名中可能会有一些 shell 注入,例如:
$ mmv -v "*.*" "#1.#l2"
FOO.BAR.MP3 -> FOO.bar.mp3 : done
foo bar 'baz' (CD 1).MP3 -> foo bar 'baz' (CD 1).mp3 : done
In this case the injection will be considered by the shell as a part of the code and they will be executed.
在这种情况下,shell 会将注入视为代码的一部分,并且它们将被执行。
While-version
版本
Clearer, but a little bit longer, version of the script:
更清晰但更长一点的脚本版本:
import os
files = os.listdir('.')
for f in files:
path, ext = os.path.splitext(f)
if ext.isupper():
os.rename(f, path + ext.lower())
This still breaks for filenames containing newlines. To fix this issue, you need to have a find
that supports -print0
(like GNU find
) and Bash (so that read
supports the -d
delimiter switch):
对于包含换行符的文件名,这仍然会中断。要解决此问题,您需要有一个find
支持-print0
(如 GNU find
)和 Bash(以便read
支持-d
分隔符开关):
rename JPG jpg *.JPG
This still breaks for files that contain trailing newlines (as they will be absorbed by the a=$(...)
subshell. If you reallywant a foolproof method (and you should!), with a recent version of Bash (Bash≥4.0) that supports the ,,
parameter expansion here's the ultimate solution:
对于包含尾随换行符的文件,这仍然会中断(因为它们会a=$(...)
被子shell吸收。如果你真的想要一个万无一失的方法(你应该!),使用支持,,
参数扩展的最新版本的 Bash(Bash≥4.0)这里是最终解决方案:
rename 's/\.JPG$/.jpg/' *.JPG
Back to the original solution
回到原来的解决方案
Or in one find
go (back to the original solution with some fixes that makes it really foolproof):
或者一次性find
(回到原始解决方案,并进行一些修复,使其真正万无一失):
find -name '*.JPG' | sed 's/\(.*\)\.JPG/mv ".JPG" ".jpg"/' |sh
I added -type f
so that only regular files are renamed. Without this, you could still have problems if directory names are renamed before file names. If you also want to rename directories (and links, pipes, etc.) you should use -depth
:
我添加-type f
以便只重命名常规文件。如果没有这个,如果在文件名之前重命名目录名,您仍然可能会遇到问题。如果您还想重命名目录(和链接、管道等),您应该使用-depth
:
rename -n 's/\.JPG$/\.jpg/' *
so that find
performs a depth-first search.
从而find
执行深度优先搜索。
You may argue that it's not efficient to spawn a bash
process for each file found. That's correct, and the previous loop version would then be better.
您可能会争辩说,bash
为找到的每个文件生成一个进程效率不高。没错,以前的循环版本会更好。
回答by thkala
Well, you could use this snippet as the core of whatever alternative you need:
好吧,您可以使用此代码段作为您需要的任何替代方案的核心:
rename 's/\.JPG$/\.jpg/' *
Afterwards, all you need to do is feed a list of the files you need to rename to its standard input. E.g. for all files under the current directory and any subdirectory:
之后,您需要做的就是将需要重命名的文件列表提供给其标准输入。例如对于当前目录和任何子目录下的所有文件:
rename 's/\.([^.]+)$/.\L/' *
A small optimization:
一个小优化:
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
You will have to be more specific if you need a more concrete answer than this...
如果您需要比这更具体的答案,则必须更具体...
回答by Theodros Zelleke
This will do the job for your '.mp3's - but only in the working directory - however is able to consume filenames with whitespace:
这将为您的“.mp3”完成这项工作 - 但仅限于工作目录 - 但是能够使用带有空格的文件名:
zmv '(*).(*)' '.:l'
Correction:
更正:
autoload -U zmv
回答by Elmar Zander
If you have mmv
(=move multiple files) installed and your filenames contain at most one dot, you can use
如果您安装了mmv
(=移动多个文件)并且您的文件名最多包含一个点,您可以使用
It does not get more than one dot right (since the matching algo for *
is not greedy in mmv
), however, it handles ()
and '
correctly. Example:
它不会超过一个点的权利(因为算法中的匹配*
不贪心mmv
),然而,它处理()
和'
正确。例子:
Not perfect, but much easier to use and remember than all the find/exec/sed
stuff.
不完美,但比所有find/exec/sed
东西更容易使用和记住。
回答by Jason Sundram
So, these solutions that look like line noise are nice and all, but this is easy to do from the python REPL (I know the OP asked for bash, but python is installed on a lot of systems that have bash these days...):
所以,这些看起来像线路噪音的解决方案很好,但是这很容易从 python REPL 中实现(我知道 OP 要求使用 bash,但是现在很多系统上都安装了 python... ):
##代码##回答by P Sreedhar
I got success with this command.
我用这个命令成功了。
##代码##Where rename
is a command that tells the shell to rename every occurrence of JPG
to jpg
in the current folder with all filenames having extension JPG
.
whererename
是一个命令,它告诉 shell将当前文件夹中所有出现的JPG
to重命名jpg
为所有文件名都具有JPG
.
If you see Bareword "JPG" not allowed while "strict subs" in use at (eval 1) line 1 with this approach try:
如果您在 (eval 1) 第 1 行使用此方法看到不允许使用裸字“JPG”,而“严格子文件”正在使用,请尝试:
##代码##回答by Govind Totla
recursively for all one fine solution:
递归所有一个很好的解决方案:
##代码##The above recursively renames files with the extension "JPG" to files with the extension "jpg"
以上递归地将扩展名为“JPG”的文件重命名为扩展名为“jpg”的文件
回答by balcoder
If your only interested in certain file extensions like converting all higher case "JPG" extensions to lower case "jpg" You could use the command line utility renamelike so. CD into directory you want to change. Then
如果您只对某些文件扩展名感兴趣,例如将所有大写的“JPG”扩展名转换为小写的“jpg”,您可以像这样使用命令行实用程序重命名。CD 进入您要更改的目录。然后
##代码##Use -n option to test what will be changed, then when you happy with results use without like so
使用 -n 选项来测试将要更改的内容,然后当您对结果感到满意时使用而不是这样
##代码##回答by fikr4n
This is shorter but more general, combined from other's answer:
这是更短但更一般的,结合其他人的回答:
##代码##Simulation
模拟
For simulation, use -n
, i.e. rename -n 's/\.([^.]+)$/.\L$1/' *
. This way you can see what will be changed before the real changes being performed. Example output:
对于模拟,使用-n
,即rename -n 's/\.([^.]+)$/.\L$1/' *
。通过这种方式,您可以在执行真正的更改之前看到将要更改的内容。示例输出:
Short explanation about the syntax
关于语法的简短说明
- The syntax is
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
\.([^.]+)$
means sequence of anything but dot ([^.]
) at the end of the string ($
), after dot (\.
).\L$1
means dot (\.
) followed by lowercase (\L
) of 1stgroup ($1
)- First group in this case is the extension (
[^.]+
) - You better use single quote
'
instead of double quote"
to wrap the regex to avoid shell expansion
- 语法是
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
\.([^.]+)$
表示[^.]
字符串 ($
)末尾的点 ( ) 以外的任何序列,在点 (\.
) 之后.\L$1
点的装置(\.
),随后小写(\L
1个)第一组($1
)- 在这种情况下,第一组是扩展名 (
[^.]+
) - 您最好使用单引号
'
而不是双引号"
来包装正则表达式以避免外壳扩展
回答by Michael Leonard
If you are using ZSH:
如果您使用的是 ZSH:
##代码##If you get zsh: command not found: zmv
then simply run:
如果你得到zsh: command not found: zmv
那么只需运行:
And then try again.
然后再试一次。
Thanks to this original articlefor the tip about zmv and the ZSH documentationfor the lowercase/uppercase substitution syntax.