重命名其下的子目录和文件

时间:2013-09-12 07:58:22

标签: bash shell

我有一个目录结构,其中目录和文件命名错误,我需要修改名称。目录有子目录,这些子目录只有文件。

在这个树中,我需要按照下表重命名文件和子目录。例如,名为hdm的目录应重命名为hdu。根据下面的转换表,名为hdm_e_01.png的文件应重命名为hdu_e_01.png

但是,请注意,这需要分两个阶段进行,因为已经有一个名为hdu的目录,需要根据表重命名为hds,再加上新重命名的目录{ {1}}需要保持为hdu而不是更改为hdu。因此,第一阶段需要将它们全部重命名为非冲突的临时和第二阶段的最终名称。

hds

2 个答案:

答案 0 :(得分:1)

首先,重命名目录:

cd /to/your/root/dir
cp -R hda /some/tmp/dir/hdx
cp -R hde /some/tmp/dir/hdw
cp -R hdi /some/tmp/dir/hdv
...

现在重命名您的文件:

cd /some/tmp/dir/ 
for d in "$(find . -type d -name 'hd*')" ; do
    cd "${d}"
    for f in *; do
        mv "${f}" "${d}${f:3}"
    done
    cd ..
done

基本上它遍历你的目录,并根据目录名称,按照你的规范重命名那里的文件,除了它不检查哪些是从文件名的开头删除的3个字符。

所以我建议在echo行前添加mv,例如:

        echo mv "${f}" "${d}${f:3}"

然后首先检查输出。

如果您对结果满意,请执行重命名,然后删除原件并将修改后的目录移动到原位。

然后有像mmv这样的解决方案,但不是基础bash标准。

答案 1 :(得分:0)

我感谢你提出的有趣问题。这是我的解决方案。

#!/bin/bash

TABLE_FILE='file.table'
DIRECTORY='.'
PRETEND=true  ## Set to false to change files.

declare -A RMAP=() FLAGS=() RENAMED=() NEWPATHS=()
declare -a ORDER=()

function check_directory {
    if [[ ! -d $DIRECTORY ]]; then
        echo "Directory does not exist: $DIRECTORY"
        return 1
    fi
    return 0
}

function parse_table_file {
    local -A F=() T=()
    local -i I=0

    if [[ ! -f $TABLE_FILE || ! -r $TABLE_FILE ]]; then
        echo "Table file does not exist or is not readable: $TABLE_FILE"
        return 1
    fi

    while read FROM TO; do
        if [[ -n ${F[FROM]} ]]; then
            echo "We can't have two the same sources: $FROM"
            return 1
        fi

        if [[ -n ${T[TO]} ]]; then
            echo "We can't have two them same replacements: $TO"
            return 1
        fi

        RMAP[$FROM]=$TO ORDER[I++]=$FROM F[$FROM]=. T[$TO]=.
    done < "$TABLE_FILE"
    return 0
}

function rename {
    local FROM=$1 TO=$2

    if [[ ${FLAGS[$FROM]} != R ]]; then
        if [[ ${FLAGS[$TO]} == H ]]; then
            echo "Circular rename point detected: $FROM - $TO"
            return 1
        elif [[ -n ${RMAP[$TO]} ]]; then
            FLAGS[$FROM]=H
            rename "$TO" "${RMAP[$TO]}" || return 1
        fi

        local -a TARGETS=("$DIRECTORY/$FROM"*)
        local NEWNAME NEWPATH

        for TARGET in "${TARGETS[@]}"; do
            NEWNAME=${TO}${TARGET##**"/${FROM}"}
            NEWPATH=${DIRECTORY}/${NEWNAME}

            echo "Renaming $TARGET to $NEWNAME ($NEWPATH)."

            if [[ -n ${NEWPATHS[$NEWPATH]} ]]; then
                echo "$TO has had same file matches as the previous ones: $NEWPATH through pattern ${NEWPATHS[$NEWPATH]}."
                return 1
            fi

            if [[ -e $NEWPATH ]]; then
                if [[ -n ${RENAMED[$NEWPATH]} ]]; then
                    if [[ $PRETEND == false ]]; then
                        echo "Can't rename $TARGET to $NEWNAME ($NEWPATH): file still exists and should have been renamed already with previous set ${RENAMED[$NEWPATH]}."
                        return 1
                    fi
                else
                    echo "Can't rename $TARGET to $NEWNAME ($NEWPATH): file already exists and is not part of the renaming sequence."
                    return 1
                fi
            fi

            if [[ $PRETEND == false ]]; then
                mv "$TARGET" "$NEWPATH" || {
                    echo "Failed to rename $NEWNAME to $NEWPATH."
                    return 1
                }
            fi

            NEWPATHS[$NEWPATH]=$TO
            RENAMED[$TARGET]="$FROM - $TO"
        done

        FLAGS[$FROM]=R
    fi

    return 0
}

function process {
    for FROM in "${ORDER[@]}"; do
        rename "$FROM" "${RMAP[$FROM]}" || return 1
    done
    return 0
}

check_directory && parse_table_file && process

该脚本使用递归来解决和检测冲突,而无需临时重命名。它还可以假装重命名以查看如果已经进行了更改将会发生什么,并防止出错。

示例我有这些文件:

aaa (D)
aab (D)
aaa.txt (F)

然后我在桌子上有这些线:

aaa aab
aab aac

这是我的输出:

Renaming ./aab to aac (./aac).
Renaming ./aaa to aab (./aab).
Renaming ./aaa.txt to aab.txt (./aab.txt).