Contents

Linux中将所有文件夹和文件重命名为小写

1. 概述

通常,Linux 中文件和目录名称的转换可以有效地使用各种文本控制命令,如trsedrename 。对于文件和目录,可以先使用*find *命令扫描感兴趣的目标位置,然后在结果树中出现大写字母时立即开始重命名过程,从而将名称转换为小写。

但是,当目录树中的多个位置存在大写元素时,重命名过程可能会失败。在本教程中,我们介绍了一种尝试解决此问题的替代方法,以及符号链接内容的情况。

2. 示例目录结构

我们将展示重命名脚本如何作用于文件名和目录名。让我们考虑一个包含四个目录的目录结构的示例。其中每一个都包含不同数量的不同深度级别的子目录和文件。

例如,除了 level-3 之外的所有子元素都包含带有一些大写字符的子元素。让我们看看这个案例的目录结构:

$ tree
.
├── Level-0
│   └── File_L0.txt
├── Level-1
│   └── Sub_Level-1
│       └── file_L1-S1.txt
├── Level-2
│   └── Sub_Level-1
│       ├── File_L2-S1.txt
│       └── Sub_Level-2
│           └── File_L2-S2.txt
└── level-3
    ├── file_l3.txt
    └── sub_level-1
        ├── file_l3-s1.txt
        └── sub_level-2
            └── file_l3-s1-s2.txt

3. 构建文件和目录重命名脚本

在本节中,我们将分析脚本代码reverseLowerCase.sh的具体细节。为了在任何文件名更改之前提前评估脚本在屏幕上的效果,用户可以声明他的意图。

该过程通过调用以下命令启动,提供感兴趣的目标目录(完整路径)作为参数:

$ ./reverseLowerCase.sh /home/dimtyp/Base

为了执行任何有效的重命名,我们使用前面的命令在末尾添加关键字do_rename

$./reverseLowerCase.sh /home/dimtyp/Base do_rename

3.1. 重命名动作

最初,我们为保存输出的文件定义了两个变量。此外,我们提供了应用脚本的目录和一个标志来解析我们的意图,以便有效地应用或仅在屏幕上显示所需的更改:

#!/bin/bash
LEVELSFILE=$PWD/fileDirs 
OUTPUT=$PWD/myOutput
myPath="$1"
myAction="$2"

接下来,定义了两个函数:

  • 根据第二个参数的正确选择,通过以下函数在屏幕上显示友好的标题消息
  • 当第二个参数是do_rename时,正确的将操作重命名为小写
[ "$myAction" = "do_rename" ] && MSG=="   RENAMING   " || MSG="  PRINT-ONLY " 
message() {
    echo
    echo "====================="
    echo "$MSG"
    echo "====================="
    echo
}
	
renaming_action() {
    lowerCaseFileExistFlag=$([ -e "${myDirName}/${myLowercaseBasename}" ] && echo $?) 
    dontOverwriteFlag=${lowerCaseFileExistFlag:+n}
    case "$myAction" in
        do_rename) mv -v$dontOverwriteFlag "${myDirName}/${myBaseName}" "${myDirName}/${myLowercaseBasename}" >> $OUTPUT  ;;
                *) echo "${myDirName}/${myBaseName} --> ${myDirName}/${myLowercaseBasename}" >> $OUTPUT ;;
    esac
}

renaming_action函数最终涵盖了一个额外的特殊情况。当两个或多个文件存在于同一子目录中且名称仅在类型大小写上有所不同时(例如Readme.txtREADME.txt),所有文件名将共享相同的小写名称。因此,来自mv命令的每次重命名尝试都将覆盖已经存在的相同名称。

对于命令mv-n选项可以防止这种情况发生,并且在目标文件名存在时使用变量dontOverwriteFlag设置,忽略覆盖操作。

3.2. 最深目录级别的确定

一个while 循环遵循确定所检查目录的最大深度级别。我们打印每个级别的元素数量,不包括隐藏文件(-name ‘[!.]*’):

cd "$myPath"
echo "myPath: $myPath"
echo "myAction: ${myAction:-NONE}"
message
level=0
elementsNumber=100
while [ $f -ne 0 ]; do 
    elementsNumber=`find "$myPath" -mindepth $level -name '[!.]*' | wc -l | awk '{print $1}'`
    ((level++ ))
    echo "files-dirs: $elementsNumber -- depth: $level" >> $LEVELSFILE
done
cat $LEVELSFILE | column -t
echo
echo "Max-Depth: $((level - 1))"
echo

3.3. 有效的重命名机制

从先前评估的最深级别开始,第二个while循环使用大写字符重命名文件名。basenamedirname 命令拆分完整的文件名路径以允许此更新:

while [ $level -ge 0 ]; do 
    for aFile in `find . -mindepth "$level" -maxdepth "$level" -name '[!.]*' -print | tr '\n' ' '`; do 
        myDirname=$(dirname $aFile)
        myBasename=$(basename $aFile)
        myLowercaseBasename=${myBasename,,} 
        symlinkFlag=0
        [ ${myBasename} != ${myLowercaseBasename} ] && renaming_action && symlinkFlag=1

3.4. 处理符号链接

在结束之前,我们检查一个文件是否是符号链接,因为目标文件重命名会破坏链接关系。在这种情况下,我们更新符号链接指向的目标文件。我们需要知道符号链接指向的目标文件是否已经被重命名。symlinkFlag变量通知我们值为 1 :

        if [ -L "$aFile" ]; then
            case $symlinkFlag in
                0) aSymlinkFile="${myDirname}/${myBasename}";;
                1) aSymlinkFile="${myDirname}/${myLowercaseBasename}";;
            esac
            targetFile=$(readlink -f "$aSymlinkFile")
            if [[ "$targetFile" =~ ^"$myPath".* ]]; then
                ln -sf "${targetFile,,}" "${aSymlinkFile}"
            fi
        fi
    done 
    (( level-- ))
done

最后,我们在屏幕上读取有效或假设的重命名操作,然后删除保存结果的文件:

cat -n $OUTPUT | column -t
rm $LEVELSFILE
rm $OUTPUT

3.5. 执行重命名脚本

当我们执行脚本时,屏幕会显示执行的名称修改:

$ ./reverseLowerCase.sh /home/dimtyp/Base
myPath: /home/dimtyp/Base
myAction: NONE
=====================
  PRINT-ONLY 
=====================
files-dirs:  17  --  depth:  1
files-dirs:  16  --  depth:  2
files-dirs:  12  --  depth:  3
files-dirs:  7   --  depth:  4
files-dirs:  2   --  depth:  5
files-dirs:  0   --  depth:  6
Max-Depth: 5
1   ./Level-2/Sub_Level-1/Sub_Level-2/File_L2-S2.txt  -->  ./Level-2/Sub_Level-1/Sub_Level-2/file_l2-s2.txt
2   ./Level-2/Sub_Level-1/Sub_Level-2                 -->  ./Level-2/Sub_Level-1/sub_level-2
3   ./Level-2/Sub_Level-1/File_L2-S1.txt              -->  ./Level-2/Sub_Level-1/file_l2-s1.txt
4   ./Level-1/Sub_Level-1/file_L1-S1.txt              -->  ./Level-1/Sub_Level-1/file_l1-s1.txt
5   ./Level-0/File_L0.txt                             -->  ./Level-0/file_l0.txt
6   ./Level-2/Sub_Level-1                             -->  ./Level-2/sub_level-1
7   ./Level-1/Sub_Level-1                             -->  ./Level-1/sub_level-1
8   ./Level-0                                         -->  ./level-0
9   ./Level-2                                         -->  ./level-2
10  ./Level-1                                         -->  ./level-1

通过tree 命令,我们可以验证有效的名称更改(使用do_rename时):

/home/dimtyp/Base
├── level-0
│   └── file_l0.txt
├── level-1
│   └── sub_level-1
│       └── file_l1-s1.txt
├── level-2
│   └── sub_level-1
│       ├── file_l2-s1.txt
│       └── sub_level-2
│           └── file_l2-s2.txt
└── level-3
    ├── file_l3.txt
    └── sub_level-1
        ├── file_l3-s1.txt
        └── sub_level-2
            └── file_l3-s1-s2.txt