在Bash中,这个用连字符风格命名的函数如何实现无限调用?

编程语言 2026-07-08

我可以在 bash 中像下面这样运行这段代码

#: can works well:
#: (start a new bash)
foo_a () { echo a ; }
foo-a () { foo_a ; }
echo will out a:
foo_a

运行起来效果很好。

这些也同样有效:

#: can works well:
#: (start a new bash)
foo_a () { echo a ; }
foo-a () { foo_a ; }
alias foo-a=foo_a
echo will out a:
foo_a

#: can works well:
#: (start a new bash)
foo_a () { echo a ; }
alias foo-a=foo_a
function foo-a () { foo_a ; }
echo will out a:
foo_a

因此我们可以知道,foo-a () { foo_a ; }function foo-a () { foo_a ; } 都可以在Bash中定义一个名为 foo-a 的函数。

但这些并不能正确工作:

#: will broke the terminal window or always in blocking:
#: (start a new bash)
foo_a () { echo a ; }
alias foo-a=foo_a
foo-a () { foo_a ; }
echo boom here:
foo_a

#: will broke the terminal window or always in blocking:
#: (start a new bash)
foo_a () { echo a ; }
alias foo-a=foo_a
foo-a () { foo_a ; }
function foo-a () { foo_a ; }
echo boom here:
foo_a

#: will broke the terminal window or always in blocking:
#: (start a new bash)
foo_a () ( echo a )
alias foo-a=foo_a
foo-a () { foo_a ; }
echo boom here:
foo_a

#: will spawn too much bash sub-process to fill your memory:
#: (start a new bash)
foo_a () { echo a ; }
alias foo-a=foo_a
foo-a () ( foo_a )
echo spawn here:
foo_a

只需在 foo-a 之前添加 function

#: can works well:
#: (start a new bash)
foo_a () { echo a ; }
alias foo-a=foo_a
function foo-a () ( foo_a )
echo will out a:
foo_a

或者让 alias foo-a=foo_a 出现在 foo-a 的定义之后,也可以再次正常工作。

只有在一个 alias foo-a=foo_a 出现在 foo-a 的定义之前,且没有 function 提示时,问题才会发生:出现堆栈溢出或创建大量Bash子进程;这是否会创建子进程,取决于 foo-a 的定义。

所以,我的问题是:

  • 这种现象(也就是生成大量子进程之类的情况)是一个 bug 还是一个 feature
  • 它是如何发生的?

我猜这问题/现象的关键在于 foo-a 的定义,问题在于它会在普通函数调用时陷入无限执行(原因我也不知道为什么),并且这种关系看起来像是由一个预定义的别名构成(我也不知道为什么别名会在这里产生影响)。


我用来测试的Bash版本:

  • GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)
  • GNU bash, version 5.2.37(1)-release (x86_64-pc-msys)

并且,我也在 brush-shell-v0.4.0 上测试过,它won't 会带来任何问题——上述提到的所有情况在 brush 中都能正常工作。

解决方案

命令中的第一个单词总是会被展开为别名。即使它处在函数定义的开头,这一点也成立。因此,当你这样做时

alias foo-a=foo_a
foo-a () { foo_a ; }

就等价于

alias foo-a=foo_a
foo_a () { foo_a ; }

因为别名会替换函数名。这显然会造成无限递归,函数无条件地调用自身。

如果把别名定义在函数之后,就不会出现同样的问题,因为函数名不会被改变。并且如果你使用 function 命令来定义函数,别名就不是第一个单词,因此不会被展开;不过,这种语法并非可移植的,这是一个 bash 的扩展。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章