将所有文件扩展名转换为小写

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为每个找到的文件生成进程效率不高.这是正确的,之前的循环版本会更好.

  • Macs [随附](http://stackoverflow.com/questions/18298356/sed-and-mac-os-x-differences-with-to-upper-to-lower-and-whole-capture-control-s)不幸的是BSD`sed`,而不是GNU`sed`,只有后者支持[lowercasing](http://www.gnu.org/software/sed/manual/html_node/The-_0022s_0022-Command.html#The -_0022s_0022-Command)`\ L`序列.使用[Homebrew](http://brew.sh)的Mac用户可以`brew install gnu-sed`并使用`gsed`替换`sed`运行你的shell命令. (8认同)

小智 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)

  • 在mac上,`brew install rename`可以工作,但由于它是一个不区分大小写的文件系统,你必须重命名为`jpg_`,然后从`jpg_`重新命名为`jpg`. (9认同)
  • 我喜欢这个答案.只需一个简单的正则表达式匹配和替换,这对于使用rename命令的其他情况是通用的.如果您犯了错误,请先使用-n开关进行"干运行". (3认同)

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扩展

  • 我想说这是最好的答案,因为它简短而优雅,并且实际上回答了这个问题(与其他投票较高的答案相比)!可能来晚了... (2认同)

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)

如果你需要比这个更具体的答案,你必须更加具体......

  • @Adam:`tr'[:ucase:]''[:lcase:]'`也可以,但是使用任何类型的命令替换每个案例转换至少启动一个新进程... (2认同)

Mic*_*ard 6

如果您使用的是 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 文档


The*_*eke 5

这将为您的 '.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)