如何重命名目录及其子目录中的所有文件?

3
我正在尝试访问目录及其子目录中的所有文件,并尝试使用以下代码来实现它:
#!/bin/bash
NEWNAME="newFile"
echo "- - - - - - - - - - - "
for f in *\ *; do mv "$f" "${f// /_}"; done
for f in *.* *; do mv "$f" "${f// /_}"; done
FILES=$(find ./ -type f)

for f in $FILES; do
    
    path=$(dirname "${f}")
    extension="${f##*.}"
    echo $extension;
    mv "$f" "${f/$f/${$path/$NEWNAME.$extension}}"
done

但是由于我对bash的知识缺乏,导致出现了以下错误:
mv: rename * * to *_*: No such file or directory
mv: rename very_important_folder to very_important_folder/very_important_folder: Invalid argument
//very_important_folder/bob
./app.sh: line 13: ${$path/$NEWNAME.$extension}: bad substitution

这是我的文件夹设置:
-very important folder:
  -|filewithoutspaces.py
  -|anothersubfolder:
    --|file with spaces.txt
-app.sh

因此,我正在尝试更改目录中所有文件的名称为newname,但保留原有扩展名。我遇到了更改带有空格名称的文件的问题,并且对bash并不熟悉。 它确实可以更改非文件夹中的文件,但无法更改子目录中的文件。 我使用的是MacBook Air (M1, 2020) macOS 12.3 Monterey。


代码存在许多问题。如果您能够确切地解释您想要做什么,那么有人可能会帮助您。 - pjh
我正在尝试将特定文件夹中所有文件的名称更改为相同的名称。 但由于我对bash缺乏知识,我遇到了无数问题。 - Itamar Cohen
1
@ItamarCohen,如果您这样做,那么您最终将只剩下一个文件夹,并且除了一个原始文件之外的所有文件都将被删除。这真的是您想要的吗?如果不是,请提供一个具体的例子。例如,如果您从包含文件a.txtb.txtc.txt的文件夹开始,您希望最终得到什么?另外,您代码的第一部分似乎在尝试重命名文件以用下划线替换空格。这是一个单独的活动还是与代码的其余部分有关? - pjh
1
@ItamarCohen,抱歉,我不明白你想做什么。如果你能提供一个具体的例子:在运行程序之前和之后的目录和文件,那会很有帮助。 - pjh
2
代码的目的是什么?“访问”所有文件的方式是什么?您可以考虑编写应用程序以正确处理路径中的空格,而不是重命名所有文件。 - KamilCuk
显示剩余4条评论
1个回答

3

我不确定你真正想做什么,但是以下结构肯定可以帮助你:

#!/bin/bash
shopt -s globstar

for path in ./**
do
    [[ -d "$path" ]] && continue

    [[ $path =~ ^(.*/)([^/]+)(\.[^/]*)$|^(.*/)(.+)$ ]]

    dirpath="${BASH_REMATCH[1]}${BASH_REMATCH[4]}"
    filename="${BASH_REMATCH[2]}${BASH_REMATCH[5]}"
    extension="${BASH_REMATCH[3]}"

    echo mv "$path" "$dirpath${filename// /_}$extension"
done
注:
  • shopt -s globstar 允许匹配任意路径长度的通配符**
  • ./** 用于确保生成的路径中至少有一个/。它极大地简化了将路径拆分为其不同组件的操作。
  • [[ -d "$path" ]] && continue 表示跳过目录路径。
  • [[ $path =~ ... ]] 是使用Bash正则表达式将路径拆分为dirnamefilenameextension的方法。
  • echo ... 现在,您已经可以随心所欲地使用文件路径的不同组件。

更新

对于旧版本的bash,你可以定义一个函数,在find中调用它以解决问题:

#!/bin/bash

doit() {
    local path dirpath filename extension
    for path
    do
        [[ $path =~ ^(.*/)([^/]+)(\.[^/]*)$|^(.*/)(.+)$ ]]

        dirpath="${BASH_REMATCH[1]}${BASH_REMATCH[4]}"
        filename="${BASH_REMATCH[2]}${BASH_REMATCH[5]}"
        extension="${BASH_REMATCH[3]}"

        echo mv "$path" "$dirpath${filename// /_}$extension"
    done
}
export -f doit

find . -type f -exec bash -c 'doit "$0" "$@"' {} +

请注意,如果您在函数内部使用外部变量(例如代码中的NEWNAME),您还必须将其导出:export NEWNAME="newFile"
顺便说一句,将Shell变量大写不安全。


这会导致 ./newcode.sh: line 2: shopt: globstar: invalid shell option name - Itamar Cohen
哦,所以你正在运行bash-3。globstar不容易替换,你将不得不使用find -execfind | xargs的变通方法。 - Fravadona
我该如何安装Bash 4? - Itamar Cohen
似乎我除了3之外还有bash 5,但它仍然显示错误...我该如何使用它? - Itamar Cohen
一种方法是修改你的 shebang。例如,如果你使用 Homebrew 安装了 bash-5,那么 shebang 需要像 #!/opt/homebrew/bin/bash 这样。 - Fravadona

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接