找到多个文件并在Linux中重命名它们

问题描述 投票:56回答:9

我在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也会有一些其他文件。

linux bash shell scripting suse
9个回答
111
投票

您可以使用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.

12
投票

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

#!/bin/bash

for file in $(find /tmp -name '*.txt')
do
  mv $file $(echo "$file" | sed -r 's|.txt|.cpp|g')
done

12
投票

为了递归重命名,我使用以下命令:

find -iname \*.* | rename -v "s/ /-/g"

11
投票

用bash:

shopt -s globstar nullglob
rename _dbg.txt .txt **/*dbg*

2
投票

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

find /tmp -name "*.txt" -exec bash -c 'mv $0 $(echo "$0" | sed -r \"s|.txt|.cpp|g\")' '{}' \;

2
投票

find -execdir rename用正则表达式重命名文件和目录

如果要使用正则表达式重命名文件和目录,而不是简单地使用后缀,那么这是一个很好的模式:

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 upstream

用连字符''替换空格''的示例用法。干运行:

find-rename-regex ' /-/g'

做替换:

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

-execdir不同,令人敬畏的cd选项在执行rename命令之前将-exec放入目录。

-depth确保重命名首先发生在子项上,然后发生在父项上,以防止丢失父目录的潜在问题。

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

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

PATH黑客攻击是必需的,因为-execdir有一个非常恼人的缺点:如果你的find环境变量中有任何相对路径,-execdir非常自以为是并拒绝对PATH做任何事情,例如: ./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是一个GNU找到extension to POSIXrename是基于Perl的,来自rename包。

重命名预测变通方法

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

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

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

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

在Ubuntu 18.10中测试过。


0
投票

如果您只想重命名并且不介意使用外部工具,那么您可以使用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

0
投票

你可以在下面使用它。

rename --no-act 's/\.html$/\.php/' *.html */*.html

-3
投票

经典方案:

for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done
© www.soinside.com 2019 - 2024. All rights reserved.