bash 搜索+替换文件名中的字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7171659/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:57:42  来源:igfitidea点击:

Search+replace strings in filenames

bash

提问by Cam

Using bash, how can I search for all occurrences of the substring 'foo' in all filenames (including folders) contained recursively in a directory and replace them them with 'bar'?

使用 bash,如何在目录中递归包含的所有文件名(包括文件夹)中搜索所有出现的子字符串 'foo' 并将它们替换为 'bar'?

For example, if the current structure looks like:

例如,如果当前结构如下所示:

-foo_test
    - fooo.txt
    - xfoo
        - yfoo.h
- 1foo.c

It should look like this after running the bash script:

运行 bash 脚本后,它应该如下所示:

-bar_test
    - baro.txt
    - xbar
        - ybar.h
- 1bar.c

回答by Adrian Pronk

Both variations shown here using work correctly on OPs test structure:

此处显示的两种变体都在 OP 测试结构上正常工作:

find . -depth -name '*foo*' -execdir bash -c 'mv -i "" "${1//foo/bar}"' bash {} \;

or, if you have a very large number of files and want it to run faster:

或者,如果您有大量文件并希望它运行得更快:

find . -depth -name '*foo*' -execdir bash -c 'for f; do mv -i "$f" "${f//foo/bar}"; done' bash {} +

EDIT: As noted in the comments, my earlier answer using a findcommand that did not use the execdiroption and using renamehas problems renaming files in directories that contain foo in their name. As suggested, I have changed the find commands to use -execdir, and I have deleted the variation using the renamecommand since it is a non-standard command.

编辑:如评论中所述,我之前的回答使用find未使用该execdir选项的命令并使用在rename重命名名称中包含 foo 的目录中的文件时出现问题。按照建议,我已将 find 命令更改为 use -execdir,并且我已使用该rename命令删除了变体,因为它是一个非标准命令。

回答by SSteve

This was tricky because of directorynames with multiple instances of "foo". When you change ./foo_test/xfooto ./bar_test/xbareverything that was in ./foo_testbecomes inaccessible. So I changed the file names first then changed the last occurrence of "foo" in the directory names. I added echo statements to track what's going on during development. You can, of course, expunge them.

这很棘手,因为目录名称包含多个“foo”实例。当您更改./foo_test/xfoo为无法访问的./bar_test/xbar所有内容./foo_test时。所以我首先更改了文件名,然后更改了目录名称中最后一次出现的“foo”。我添加了 echo 语句来跟踪开发过程中发生的事情。当然,您可以删除它们。

#!/bin/sh
#first change the file names
#append '.' to process files in current directory
for D in $(find -d . -name "*foo*" -type d ) '.' 
do 
    pushd $D >> /dev/null
    echo 'directory: ' "$D"
    for file in $(find . -name "*foo*" -type f -maxdepth 1)
    do
        echo '    change' "$file" 'to' `echo "$file" | sed s/foo/bar/g`
        mv "$file" `echo "$file" | sed s/foo/bar/g`
    done
    popd >> /dev/null
done

echo ''

#Now change the directory names
for D in $(find -d . -name "*foo*" -type d )
do 
    echo 'change' "$D" 'to' `echo "$D" | sed 's/\(.*\)foo/bar/'`
    #change only the last occurance of foo
    mv "$D" `echo "$D" | sed 's/\(.*\)foo/bar/'`
done

I have no doubt there are shorter, more elegant ways to do this (probably just by removing half of the lines in this script), but I'm pretty sure this works.

我毫不怀疑有更短、更优雅的方法来做到这一点(可能只是通过删除这个脚本中的一半行),但我很确定这是有效的。



EDITThe identical loops were a red flag. This version only loops once. You get a message attempting mv '.' '.', but it's safely ignored.

编辑相同的循环是一个危险信号。这个版本只循环一次。您收到一条消息尝试mv '.' '.',但它被安全地忽略了。

#!/bin/sh
#first change the file names
#append '.' to change file in current directory
for D in $(find -d . -name "*foo*" -type d ) '.' 
do 
    pushd $D >> /dev/null
    echo 'directory: ' "$D"
    for file in $(find . -name "*foo*" -type f -maxdepth 1)
    do
        echo '    change' "$file" 'to' `echo "$file" | sed s/foo/bar/g`
        mv "$file" `echo "$file" | sed s/foo/bar/g`
    done
    popd >> /dev/null

    echo 'change' "$D" 'to' `echo "$D" | sed 's/\(.*\)foo/bar/'`
    #change only the last occurence of foo
    mv "$D" `echo "$D" | sed 's/\(.*\)foo/bar/'`
done