在目录中递归查找和替换文件名

Posted

技术标签:

【中文标题】在目录中递归查找和替换文件名【英文标题】:Find and replace filename recursively in a directory 【发布时间】:2012-03-12 17:03:24 【问题描述】:

我想把以123_xxx.txt开头的文件夹中的所有文件重命名为xxx.txt

例如,我的目录有:

123_xxx.txt
123_yyy.txt
123_zzz.txt

我想将所有文件重命名为:

xxx.txt
yyy.txt
zzz.txt

我在这个论坛上看到了一些有用的 bash 脚本,但我仍然对如何使用它来满足我的要求感到困惑。

假设我使用:

for file in `find -name '123_*.txt'` ; do mv $file ?.txt ; done

这是正确的做法吗?

【问题讨论】:

【参考方案1】:

你可以这样做:

find . -name '123_*.txt' -type f -exec sh -c '
for f; do
    mv "$f" "$f%/*/$f##*/123_"
done' sh  +

没有管道,没有读取,没有机会破坏格式错误的文件名,没有非标准的工具或功能。

【讨论】:

这是利用parameter expansion 从路径末尾剥离基本名称,然后附加一个新名称,该名称的前缀最多为 123_。要阅读的两个运算符是%##,其余的是简单的通配符。【参考方案2】:
find . -name "123*.txt" -exec rename 's/^123_//'  ";" 

会做的。不需要 AWK,不需要 for,不需要 xargs,但需要 rename,这是 Perl 库中一个非常有用的命令。它并不总是包含在 Linux 中,但很容易从 repos 中安装。

【讨论】:

对于那些拥有 perl rename 的人来说,这优于过度杀伤 awk 和循环上面的答案。 你在 -name 前少了一个点 (.),在 mac 上无法运行 @ChanLe Gnu 在 Linux 上的 find 不需要目录名来操作,但需要 '.'自动,如果缺少名称。 补充一点,如果您只讨论单个目录中的文件,则不需要使用 find 。最好让它尽可能简单,所以你可以做rename 's/^123_//' *,它会做同样的事情。干杯。 @HodorTheCoder 如果你在 bash (shopt -s globstar) 中设置了 globstar,你可以递归地进行:rename 's/^123_//' **/*【参考方案3】:

如果您想将名为 foo 的文件名中的字符串替换为 bar,您可以在 linux ubuntu 中使用它,根据需要更改文件类型

find -name "*foo*.filetype" -exec rename 's/foo/bar/'  ";"

【讨论】:

这是最通用的通用答案 这真的很有效而且很简单。我怎样才能使它递归地替换目录中的文件名。【参考方案4】:

您可以检查“重命名”工具

例如

rename 's/^123_//' *.txt

或(需要gawk)

find . -name '123_*.txt'|awk 'print "mv "$0" "gensub(/\/123_(.*\.txt)$/,"/\\1","g");'|sh

测试

kent$  tree
.
|-- 123_a.txt
|-- 123_b.txt
|-- 123_c.txt
|-- 123_d.txt
|-- 123_e.txt
`-- u
    |-- 123_a.txt
    |-- 123_b.txt
    |-- 123_c.txt
    |-- 123_d.txt
    `-- 123_e.txt

1 directory, 10 files

kent$  find . -name '123_*.txt'|awk 'print "mv "$0" "gensub(/\/123_(.*\.txt)$/,"/\\1","g");'|sh

kent$  tree
.
|-- a.txt
|-- b.txt
|-- c.txt
|-- d.txt
|-- e.txt
`-- u
    |-- a.txt
    |-- b.txt
    |-- c.txt
    |-- d.txt
    `-- e.txt

1 directory, 10 files

【讨论】:

你应该提到谐音陷阱:你谈到了特殊的重命名命令,它从 util-linux 中篡夺了 rename 的名称(它不处理正则表达式)。 rename 示例不是递归的。 find 示例是,但它不必要地不使用 rename 进行重命名。并且它将生成的脚本通过管道传送到sh,这很恶心。 谢谢,我可以在重命名后将文件名修改为全部小写吗?我的意思是.. 任何 xxx.txt 最终都应该是 xxx.txt?【参考方案5】:

对 Kent 的轻微改动,不需要 gawk 并且更具可读性,(尽管这值得商榷..)

find . -name "123*" | awk 'a=$1; gsub(/123_/,""); printf "mv \"%s\" \"%s\"\n", a, $1' | sh

【讨论】:

这是唯一一个在 FreeNAS 上真正为我工作的。 在 Ubuntu 16.04 上的工作就像一个魅力。谢谢。【参考方案6】:

要扩展Sorpigal's answer,如果你想用hello_替换123_,你可以使用

    find . -name '123*.txt' -type f -exec bash -c 'mv "$1" "$1/\/123_/\/hello_"' --  \;

【讨论】:

【参考方案7】:

如果您的文件名中没有换行符:

find -name '123_*.txt' | while IFS= read -r file; do mv "$file" "$file#123_"; done

如果您的find 支持-print0 标志(GNU find 支持),则以真正安全的方式:

find -name '123_*.txt' -print0 | while IFS= read -r -d '' file; do mv "$file" "$file#123_"; done

【讨论】:

【参考方案8】:

您可以为此编写一个小 bash 脚本。 使用以下内容创建一个名为 recursive_replace_filename 的文件:

#!/bin/bash

if test $# -lt 2; then
  echo "usage: `basename $0` <to_replace> <replace_value>"
fi

for file in `find . -name "*$1*" -type f`; do
  mv "'$file'" "$file/'$1'/'$2'"
done

使可执行文件运行:

$ chmod +x recursive_replace_filename
$ ./recursive_replace_filename 123_ ""

请注意,此脚本可能很危险,请确保您知道它在做什么,在哪个文件夹中执行它,以及使用哪些参数。在这种情况下,当前文件夹中所有包含123_的文件都将被重命名。

【讨论】:

按预期工作。不错!【参考方案9】:

尝试了上面的答案,但它对我不起作用,因为我同时在文件夹和文件名中有字符串,所以这就是我执行以下 bash 脚本:

  for fileType in d f
  do
    find  -type $fileType -iname "stringToSearch*" |while read file
    do
      mv $file $( sed -r "s/stringToSearch/stringToReplaceWith/" <<< $file )
    done
  done

首先我先替换内部文件夹名称,然后替换内部文件名。

【讨论】:

在文件名中使用空格对我来说效果很好。只需确保在 mv 行中引用两个 var 以使用空格【参考方案10】:

这是迄今为止唯一可行的解​​决方案:

find-rename-recursive --pattern '123_' --string '' -- . -type f -name "123_*"

所有其他解决方案都不适合我——有些甚至删除了文件!

详情见https://github.com/l3x/helpers#find-rename-recursive

【讨论】:

【参考方案11】:

使用 util-linux 2.28.2 中的重命名,我不得不使用不同的语法:

find -name "*.txt" -exec rename -v "123_" ""   ";" 

【讨论】:

【参考方案12】:

如果名称是固定的,您可以访问每个目录并在子外壳中执行重命名(以避免更改当前目录)相当简单。这就是我如何将一堆new_paths.json 文件重命名为paths.json

for file in $(find root_directory -name new_paths.json)
  do
     (cd $(dirname $file) ; mv new_paths.json paths.json)
  done

【讨论】:

【参考方案13】:

使用上面的例子,我用它来替换部分文件名...这是用来重命名目录中的各种数据文件,由于供应商更改名称。

find . -name 'o3_cloudmed_*.*' -type f -exec bash -c 'mv "$1" "$1/cloudmed/revint"' -- \;

【讨论】:

【参考方案14】:

下面,find 命令在树中递归搜索作为第一个参数传递的目录(这里是当前目录“.”)。以下参数是过滤我们要查找的文件的谓词。 “-name”谓词是当前文件名必须匹配的模式(这里我们只想管理以“123_”开头并以“.txt”结尾的文件名。“-type”过滤文件的类型(' f' 表示常规文件,'d' 表示目录...)。“-exec”谓词运行命令行,其中 '' 是当前文件名,';' 终止命令。

find . -name '123_*.txt' -type f -exec bash -c 'name=;mv $name $name//123_//' \;

【讨论】:

感谢您的回答,您也可以解释一下您的解决方案吗?【参考方案15】:
prename s/^123_// 123_*

见prename in the official Perl5 wiki。为了您的方便,请复制:

prename - 根据正则表达式重命名文件的脚本。 (原版在哪里发布的?)最初命名为 rename,它主要被发现为 prename,因为原版与 util-linux 包中的 rename 命令冲突。许多分叉和重新实现:

http://search.cpan.org/dist/rename/ http://search.cpan.org/dist/pbr/ http://search.cpan.org/dist/File-Rename/ http://search.cpan.org/dist/File-PerlMove/ http://search.cpan.org/dist/App-FileTools-BulkRename/ http://search.cpan.org/dist/App-perlmv/ http://training.perl.com/scripts/rename https://github.com/msabramo/ren-regexp

【讨论】:

premane 在我的 bash shell 和命令提示符中无法识别。仅供参考,我已经安装了 perl... 有助于正确拼写命令。阅读我链接到的文档。 我有prename,但它不是递归的。

以上是关于在目录中递归查找和替换文件名的主要内容,如果未能解决你的问题,请参考以下文章

递归查找和替换文件中的字符串,为受影响的文件创建备份

使用grep和sed递归查找和替换所有文件中的字符串[重复]

批量替换多个文件里边的字符串

用Python在给定目录及其子目录中递归替换文件中的字符串?

在所有子目录中的文件中查找和替换文本[重复]

使用 .bat 文件在目录中包含的多个文件中查找和替换字符串