递归 bash 函数在下降后会失去返回的能力吗?

Posted

技术标签:

【中文标题】递归 bash 函数在下降后会失去返回的能力吗?【英文标题】:Does a recursive bash function lose its ability to return after descending? 【发布时间】:2016-02-27 08:27:55 【问题描述】:

参考this post on exiting a function in bash

我尝试制作简单的示例函数来解释。该函数被赋予一个目录并查找具有目录基名的 .foo 文件。如果它不存在,它可能只需要重命名,但需要先找到它。我当然可以重写这个。查找循环是需要返回的原因。在这种情况下,我只是对 bash 函数的递归性质感到好奇。

given_directory=$"/home/bob"
some_function "$given_directory"

some_function () 
    specific_foo_file=$"$1"/"$(basename "$1")".foo
    if [ -f "$specific_foo_file" ] ; then
        echo "do file stuff"
    else
        find . -name "*.foo"|while file ; do
            if mv "$file" "$specific_foo_file" ; then
                echo "renamed $file to $specific_foo_file"
                some_function $given_directory
                return 0
            else
                echo "could not rename $file"
                return 1
            fi
        done
        echo "can't find any .foo files, oh well"
    fi

到目前为止,如果重命名成功,则始终调用末尾的 echo。

编辑:对于我正在使用的环境:

-bash-3.2$ uname -a
SunOS XXXXXXXXXX 5.10 Generic_150400-29 sun4v sparc SUNW,SPARC-Enterprise-T5120

【问题讨论】:

如果您不只是说return 0 而只是说return,那么您将获得您想要的效果(因为默认情况下,函数以其最新命令的退出状态退出)。 另外,通过shellcheck.net 运行它并修复它发现的问题,这样我们就不必在细节上进行kibitz。 :) find | while 返回的返回码与echo "can't find ..." 代码将始终 运行,这并不重要。没有什么能阻止它。 修改了我的答案。 @EtanReisner,很好的收获,非常感谢。 顺便说一句,您可能只想要specific_foo_file="$1/$(basename "$1").foo"$"" 是 i18n 语法,用于查找翻译表中给出的字符串以查看您是否有本地化版本;在这里不太可能相关。 【参考方案1】:

如果要将错误的退出状态传递到多个级别,则需要显式地将其传递到中间层。那就是:

some_function "$given_directory" || return
如果some_function 成功,函数的执行将继续到下一行。 如果some_function 失败,函数会立即返回失败时使用的相同退出状态。

也就是说,由于您在函数调用之后有一个 return 0 作为紧随其后的行,因此您可以简单地将其更改为裸 return,并具有相同的效果。


另外,您还有一个严重的问题,即您的while 循环发生在子shell 中,因此当它returns 时,它只会影响该子shell,而不影响调用它的外壳。相反,重构你的代码,把逻辑放在主 shell 中:

while IFS= read -r -d '' file ; do
    if mv -- "$file" "$specific_foo_file" ; then
        echo "renamed $file to $specific_foo_file"
        some_function "$given_directory" || return
    else
        echo "could not rename $file"
        return 1
    fi
done < <(find . -name '*.foo' -print0)

请参阅BashFAQ #24 以了解此问题。

【讨论】:

@EtanReisner,这就是我讨论这两个选项的原因(将return 0 更改为return,或添加|| return)。当然,如果从return 0 中删除0,则|| return 不会增加任何值。 我假设 OP 在 done 之后仍然有他们的回声,所以如果我们根本不返回,我们会得到一个 0 的状态。此外,这意味着我们将在失败后继续处理其他文件(并最终达到该回显)而不是短路。 @CharlesDuffy 对于我的 find 版本,-print0 给了我一个find: bad option -print0 任何建议? 除了不使用 find 依赖于 shell globbing 之外,并不是很多都不会影响安全性。这是哪个版本的 bash?如果是 4.x,那么你可以 shopt -s globstar nullglob,并做一个递归 glob:for file in *.foo **/*.foo; do (要清楚我所说的“妥协安全”是什么意思——想想如果你有一个名为 ./tmp/i-am-evil/$'\n'/etc/passwd$'\n'.foo 的文件,名称中有一个文字换行符会发生什么;攻击者可以欺骗你的脚本处理任意文件——如果你的find 直接发出这样的名称——或者创建无法重命名的文件——如果它将不可打印的字符更改为可打印的内容)。

以上是关于递归 bash 函数在下降后会失去返回的能力吗?的主要内容,如果未能解决你的问题,请参考以下文章

函数递归

☀️~爆肝万字总结递归~❤️玩转算法系列之我如何才能掌握递归解题的能力❤️~十大经典问题助你突破极限~☀️

Pascal算法之回溯及递推详细介绍、

递归下降分析程序

python递归函数

递归本身是一个特征吗?