在目录中递归查找和替换文件名
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 中安装。
【讨论】:
对于那些拥有 perlrename
的人来说,这优于过度杀伤 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 命令冲突。许多分叉和重新实现:
【讨论】:
premane 在我的 bash shell 和命令提示符中无法识别。仅供参考,我已经安装了 perl... 有助于正确拼写命令。阅读我链接到的文档。 我有prename
,但它不是递归的。以上是关于在目录中递归查找和替换文件名的主要内容,如果未能解决你的问题,请参考以下文章
使用grep和sed递归查找和替换所有文件中的字符串[重复]