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

110

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

Google建议我使用rename命令。 因此,我在CURRENT_FOLDER上执行了命令rename _dbg.txt .txt *dbg*

我的实际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中还有其他文件。


8
我收到了 Bareword "..." not allowed while "strict subs" in use at (eval 1) line 1. 的错误信息。 - Albert Hendriks
6
为什么没有一个叫做“JUST rename -r”的工具,以简化我们的工作,而不是采用笨重的解决方案? - neverMind9
13个回答

0

对于 @CiroSantilliПутлерКапут六四事 的出色回答进行补充:不要匹配我们不必重命名的查找中的文件。

我发现这在 Cygwin 上显著提高了性能。

请随意纠正我的无效 bash 代码。

FIND_STRING="ZZZZ"
REPLACE_STRING="YYYY"

FIND_PARAMS="-type d"

find-rename-regex() (
  set -eu
  find_and_replace="${1}/${2}/g"
  echo "${find_and_replace}"
  find_params="${3}"
  mode="${4}"
  if [ "${mode}" = 'real' ]; then
    PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
      find . -depth -name "*${1}*" ${find_params} -execdir rename -v "s/${find_and_replace}" '{}' \;
  elif [ "${mode}" = 'dryrun' ]; then
    echo "${mode}"
    PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
      find . -depth -name "*${1}*" ${find_params} -execdir rename -n "s/${find_and_replace}" '{}' \;
  fi
)

find-rename-regex "${FIND_STRING}" "${REPLACE_STRING}" "${FIND_PARAMS}" "dryrun"
# find-rename-regex "${FIND_STRING}" "${REPLACE_STRING}" "${FIND_PARAMS}" "real"

0

在Ubuntu上(安装rename后),这个更简单的解决方案对我来说效果最好。它将空格替换为下划线,但可以根据需要进行修改。

find . -depth | rename -d -v -n "s/ /_/g"

-depth标志告诉find首先遍历目录的深度,这很好,因为我想先重命名叶节点。

rename上的-d标志告诉它仅重命名路径的文件名组件。我不知道行为有多普遍,但在我的安装(Ubuntu 20.04)上,它可以是文件或目录,只要它是路径的叶节点。

我建议首先使用-n(无操作)标志以及-v,这样您就可以看到将被重命名的内容以及如何重命名。

使用两个标志一起,它首先重命名目录中的所有文件,然后再重命名目录本身。倒序工作。这正是我所需要的。


说句题外话,有多个名为“rename”的实用工具,功能和语法各不相同。Ubuntu的rename软件包是“Perl”版本。(因此,这基本上是早期带有相同建议的答案的重复)。 - tripleee

-4

经典解决方案:

for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done

4
这完全破碎了。请不要这样做。 - gniourf_gniourf
1
也许它看起来能够工作,但是当文件名包含空格或通配符时,它就会出错。你说这是一个经典的解决方案,但更可能是你自己想出来的解决方案,然而它显示出了可怕的错误(抱歉说话有些严厉,但是你的代码行可以被列为教科书中不应该做的例子;练习一下:数一下错误的数量)。 - gniourf_gniourf
1
不要使用for f in $(find ....)这种方式!因为它会在文件名中包含空格或通配符时出错;此外,find命令有-exec选项,很可能可以代替该方式;或者使用for f in *_dbg*; do,如果你想要递归匹配,则可以使用for f in **/*_dbg; do并开启shopt -s globstar选项。 - gniourf_gniourf
echo $f | sed 's/_dbg//':这通常显示出对shell参数扩展功能的缺乏了解(除了echo $f语句中缺少引号)。请考虑使用echo "${f/_dbg}" - gniourf_gniourf
2
最终,一个更好的解决方案是:shopt -s globstar nullglob; for f in **/*_dbg*; do mv -- "$f" "${f/_dbg}"; done - gniourf_gniourf
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接