87

我有像系统a_dbg.txt, b_dbg.txt ...中的文件Suse 10。我想编写一个 bash shell 脚本,它应该通过从中删除“_dbg”来重命名这些文件。

谷歌建议我使用rename命令。所以我rename _dbg.txt .txt *dbg*CURRENT_FOLDER

我的实际CURRENT_FOLDER包含以下文件。

CURRENT_FOLDER/a_dbg.txt
CURRENT_FOLDER/b_dbg.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt

执行rename命令后,

CURRENT_FOLDER/a.txt
CURRENT_FOLDER/b.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt

它不是递归的,如何使这个命令重命名所有子目录中的文件。就像我将拥有如此多的子目录,这些子目录的名称是不可预测的XXYY而且我CURRENT_FOLDER也会有一些其他的文件。

4

10 回答 10

141

您可以使用find递归查找所有匹配的文件:

$ find . -iname "*dbg*" -exec rename _dbg.txt .txt '{}' \;

编辑:'{}'和是什么\;

-exec参数使 find为找到rename的每个匹配文件执行。'{}'将替换为文件的路径名。最后一个标记,\;仅用于标记 exec 表达式的结束。

所有这些都在 find 的手册页中有很好的描述:

 -exec utility [argument ...] ;
         True if the program named utility returns a zero value as its
         exit status.  Optional arguments may be passed to the utility.
         The expression must be terminated by a semicolon (``;'').  If you
         invoke find from a shell you may need to quote the semicolon if
         the shell would otherwise treat it as a control operator.  If the
         string ``{}'' appears anywhere in the utility name or the argu-
         ments it is replaced by the pathname of the current file.
         Utility will be executed from the directory from which find was
         executed.  Utility and arguments are not subject to the further
         expansion of shell patterns and constructs.
于 2013-05-14T11:11:16.717 回答
23

对于递归重命名,我使用以下命令:

find -iname \*.* | rename -v "s/ /-/g"
于 2016-03-28T05:15:33.873 回答
19

我编写的小脚本以递归方式将 /tmp 和子目录下的所有带有 .txt 扩展名的文件替换为 .cpp 扩展名

#!/bin/bash

for file in $(find /tmp -name '*.txt')
do
  mv $file $(echo "$file" | sed -r 's|.txt|.cpp|g')
done
于 2014-12-02T05:20:17.017 回答
14

用 bash:

shopt -s globstar nullglob
rename _dbg.txt .txt **/*dbg*
于 2013-05-14T22:24:21.213 回答
9

find -execdir rename也适用于基本名称的非后缀替换

https://stackoverflow.com/a/16541670/895245仅适用于后缀,但这将适用于基名上的任意正则表达式替换:

PATH=/usr/bin find . -depth -execdir rename 's/_dbg.txt$/_.txt' '{}' \;

或仅影响文件:

PATH=/usr/bin find . -type f -execdir rename 's/_dbg.txt$/_.txt' '{}' \;

-execdir首先cd进入目录,然后仅在基本名称上执行。

在 Ubuntu 20.04 上测试,找到 4.7.0,重命名 1.10。

方便又安全的帮手

find-rename-regex() (
  set -eu
  find_and_replace="$1"
  PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
    find . -depth -execdir rename "${2:--n}" "s/${find_and_replace}" '{}' \;
)

GitHub 上游.

用连字符“-”替换空格“”的示例用法。

试运行,显示什么将被重命名为什么而不实际执行它:

find-rename-regex ' /-/g'

进行替换:

find-rename-regex ' /-/g' -v

命令说明

awesome-execdir选项cd在执行命令之前进入目录rename,不像-exec.

-depth确保重命名首先发生在孩子身上,然后是父母身上,以防止缺少父目录的潜在问题。

-execdir是必需的,因为 rename 不能很好地与非基本名称输入路径一起使用,例如以下失败:

rename 's/findme/replaceme/g' acc/acc

之所以PATH需要进行黑客攻击,是因为-execdir有一个非常烦人的缺点:如果您的环境变量中有任何相对路径,find则非常固执己见并且拒绝做任何事情,例如,失败:-execdirPATH./node_modules/.bin

find:相对路径 './node_modules/.bin' 包含在 PATH 环境变量中,与 find 的 -execdir 操作结合使用是不安全的。请从 $PATH 中删除该条目

另请参阅:https ://askubuntu.com/questions/621132/why-using-the-execdir-action-is-insecure-for-directory-which-is-in-the-path/1109378#1109378

-execdir是 POSIX 的 GNU find扩展rename是基于 Perl 的,来自rename包。

重命名前瞻解决方法

如果您的输入路径不是来自find,或者如果您已经受够了相对路径的烦恼,我们可以使用一些 Perl 前瞻来安全地重命名目录,如下所示:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

我还没有找到一个方便的类比-execdirxargshttps: //superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686

sort -r需要确保文件位于其各自目录之后,因为较长的路径位于具有相同前缀的较短路径之后。

在 Ubuntu 18.10 中测试。

于 2019-01-12T21:21:19.120 回答
6

上面的脚本可以写成一行:

find /tmp -name "*.txt" -exec bash -c 'mv $0 $(echo "$0" | sed -r \"s|.txt|.cpp|g\")' '{}' \;
于 2015-07-19T10:04:15.970 回答
4

如果您只想重命名并且不介意使用外部工具,那么您可以使用rnm。命令将是:

#on current folder
rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' *

-dp -1将使其递归到所有子目录。

-fo意味着仅文件模式。

-ssf '_dbg'搜索文件名中带有 _dbg 的文件。

-rs '/_dbg//'用空字符串替换 _dbg。

您也可以使用 CURRENT_FOLDER 的路径运行上述命令:

rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' /path/to/the/directory
于 2016-01-29T13:31:05.280 回答
0

您可以在下面使用它。

rename --no-act 's/\.html$/\.php/' *.html */*.html
于 2017-10-27T06:53:19.977 回答
0

这个命令对我有用,记住首先安装重命名包:

find -iname \*.* | grep old-name | rename -v "s/oldname/newname/g
于 2022-02-05T10:15:44.360 回答
-3

经典解决方案:

for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done
于 2015-09-17T18:02:13.853 回答