bash - Bash - 递归限制子shell
问题描述
我决定在我的生产服务器上测试 bash 中的递归。
不要在生产机器上运行此代码!
#!/usr/bin/bash
function fibo {
if [ $1 -le 1 ]; then
echo $1
else
echo $((
$( fibo $(( $1 - 1 )) ) +
$( fibo $(( $1 - 2 )) )
))
fi
}
fibo 100
我怀疑它会很慢,但是,没想到它会运行数十个并行作业。我在想,因为服务器是多核的,所以我可以终止该进程。
不幸的是,服务器冻结并从托管公司重新启动它。
有没有办法限制 bash 中的并行作业?
是否可以在计时器中设置主进程的 PID 并停止所有子作业?
当然,我只要求学术目的。
编辑
Tail 递归版本就像一个魅力:
#!/usr/bin/bash
fibo() {
local n=$1 a=$2 b=$3
case $n in
0) echo "$a" ;;
1) echo "$b" ;;
*) fibo $((n - 1)) "$b" $((a + b)) ;;
esac
}
fibo "${1:-10}" 0 1
基准:
time ./fibotr.sh 100
3736710778780434371
real 0m0.078s
user 0m0.000s
sys 0m0.015s
解决方案
至于预防方法,请参阅我们的姊妹网站Unix & Linux Stack Exchange。他们提出的热门问题之一是安全执行叉形炸弹。
作为您的原始算法的实现,它根本不分叉任何子shell,因此即使传递任意大的数字也不可能变成分叉炸弹:
#!/usr/bin/env bash
declare -g _fibo_resA _fibo_resB # be explicit that it's intentional that these
# variables aren't locals, for future readers
fibo() {
local _fibo_outvar _fibo_inVal _fibo_locResA _fibo_locResB
_fibo_outvar=$1; _fibo_inVal=$2
if [ "$_fibo_inVal" -le 1 ]; then
printf -v "$_fibo_outvar" 1
else
fibo _fibo_resA "$(( _fibo_inVal - 1 ))"; _fibo_locResA=$_fibo_resA
fibo _fibo_resB "$(( _fibo_inVal - 2 ))"; _fibo_locResB=$_fibo_resB
printf -v "$_fibo_outvar" "$(( _fibo_locResA + _fibo_locResB ))"
fi
}
# note that 100 is way too big; performance breaks down between 20 and 30
# but at least it won't act like a fork bomb!
fibo result "${1:-100}" # let user pass in a value they choose
echo "$result"
为什么要这样写?
- 最重要的变化是消除
$( )
,但这不再需要在标准输出上将输出传递给父级。因此,为此目的使用间接分配。 - 使用命名空间变量名(即使是本地人!)可以防止在返回结果时按名称发生冲突:如果用户选择将结果写入与我们的任何本地人匹配的变量名,他们的值将被写入本地,而不是他们希望将结果放入的全局变量。
locResA
并且locResB
被使用是因为我们没有分叉,也不能制作resA
和resB
本地化;fibo
在我们能够返回它们之前,它们会阻止其他副本覆盖结果。- 使用
funcname() {
语法声明形式是因为function funcname {
它是 ksh-ism。然而,在 ksh 中,这种格式会改变块内代码的行为;bash 不尊重这种意图。funcname() {
因此,对于具有 ksh 背景的人来说,使用符合 POSIX 标准的惊喜更少,并提高了您的代码与其他 POSIX shell 的兼容性;请参阅https://wiki.bash-hackers.org/scripting/obsolete(请注意,不止一处提到了函数声明语法)。
推荐阅读
- php - 插入标签没有得到现有标签的ID
- sql - 如何在sql server中以'hhmmss'或'hh:mm:ss'格式获取最后一小时
- angularjs - AngularJS - 删除复选框无法正常工作,一次只能删除一个
- android - 如何在广播接收器中将布尔状态成功或不提供给pendingIntent
- angularjs - 在 docker 容器中执行 ng
- elasticsearch - 如何聚合多个替代术语?
- php - phpinfo() 显示无效值
- react-native - react-native 代码推送:允许的代码更改?
- cassandra - cassandra 支持物化视图中非主键列上的 where 子句
- c# - Azure 输出 VM 列表,其中包含每个 VM 的总存储空间