【问题标题】:How to Make a "for loop" Recursive如何使“for循环”递归
【发布时间】:2020-01-23 17:55:46
【问题描述】:

我有一个命令似乎成功地将双引号 (") 替换为当前目录中的文件名的单引号 (')。但是,我需要递归地执行此操作(即,所有子目录中的所有文件)。这里是我正在使用的代码:

for f in *; do
    if [[ -f "$f" ]]; then
        new_name=$(echo "$f" | sed "s/\"/'/g")
        mv "$f" "$new_name"
    fi
done

任何建议将不胜感激。

【问题讨论】:

  • 更安全地使用find -execdir 来执行此操作,这样您的目录就不会在操作期间被符号链接换出。添加答案,稍等...
  • (顺便说一句,如果可以避免echo "...anything..." | sed,则最好避免它;与仅在bash内部进行字符串操作相比,启动外部程序或shell管道的成本很高-如果您的文件系统设置允许刷新-to-disk 被推迟,这可能比运行 mv 花费更多的时间;请参阅 BashFAQ #100 和/或 parameter expansion 上的 bash-hackers 页面了解替代方案。

标签: bash loops terminal


【解决方案1】:

最佳实践(以及在处理大型且深度嵌套的目录树时更有效的解决方案)是为此使用find,而不是bash 中的for 循环。 Using Find 在 -print0、-exec 和 -execdir 之间进行权衡;在这里,我使用的是最后一个:

#!/usr/bin/env bash

# define the function we're going to export for access from a subprocess
do_replace() {
  local name new_name old_quotes='"' new_quotes="'"
  for name do  # loops by default over "$@", the argument list given to the function
    new_name=${name//$old_quotes/$new_quotes}
    mv -- "$name" "$new_name"
  done
}
export -f do_replace # actually export that function

# tell find to start a new copy of bash that can run the function we exported
# in each directory that contains one or more file or directory names with quotes.
# Using ``-execdir ... {} ';'`` to work around a MacOS bug
# ...use ``-execdir ... {} +`` instead with GNU find for better performance.
find . -depth -name '*"*' -execdir bash -c 'do_replace "$@"' _ {} ';'

这样,每个目录都有一个新的 bash 副本,因此您不会对其中包含 /s 的名称进行操作;这样可以避免在您重命名其他用户可以写入的目录中的文件时可能发生的一些安全漏洞。


也就是说,简单的事情(在 bash 4.0 或更高版本中)是启用globstar,之后** 将递归:

#!/usr/bin/env bash
# WARNING: This calculates the whole glob before it runs any renames; this can be very
# inefficient in a large directory tree.

case $BASH_VERSION in ''|[123].*) echo "ERROR: Bash 4.0+ required" >&2; exit 1;; esac
shopt -s globstar
for f in **; do
  : "put your loop's content here as usual"
done

【讨论】:

  • 谢谢查尔斯!我在 Bash 5.0.11(1)-release 上。但是,我不确定如何运行这些命令。它们是否打算通过几个单独的命令运行?使用 globstar 方法,我的循环内容在其中看起来如何?例如,我是否将冒号保留在“将循环的内容放在这里...”前面?到目前为止,我似乎得到了一些结果,但没有递归。
  • 冒号类似于true——它是一个传统的占位符。我使用它是因为空循环体是无效的语法。替换整个循环体。
  • 我不太清楚问题是什么(关于“究竟如何运行这些命令”)。第一个示例应该是您可以在脚本中放入可运行的可执行文件,或一次手动运行一个命令。
  • @LivingMiracles, ...添加的 cmets 有帮助吗?我还修复了由 old_quotes 和 new_quotes 变量未导出(将它们移到函数中意味着它们不需要)引起的错误。
  • 好的,知道了; MacOS 附带的find 版本的错误。如果你不能升级到现代的 GNU findutils 版本(4.7.0 肯定没有这个 bug),那么我们会想要从 -execdir ... {} + 切换到(更慢的)-execdir ... {} ';'。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多