100 轮循环 bash 脚本中的分段错误(核心已转储)

Segmentation fault (core dumped) over a 100 round loop bash script

我知道关于那个著名的 "Segmentation fault core dump" 有很多问题,但就我所见, C/C++ 涉及其中的大部分。但我的问题是特定于 bash 脚本的,所以我在这里试试运气。

我正在 运行 编写一个模拟器脚本(400 多行所以我不能给你看代码,可以吗?)它使用骰子和配置文件设置和计算两个角色之间的战斗(就像你会的那样在龙与地下城桌面角色扮演游戏中做)。我有一个重新初始化功能,可以从头开始重新设置战斗,以便在有赢家时可以开始新的战斗。在 X 轮结束时,会显示其中的一些值(命中百分比、每轮 %block %hp 等)。

如果我设置 1、50 或 100 回合(100 场战斗),没关系,它 运行 很完美。但是超过 130~ 次战斗,突然间,它显示 "Segmentation fault (core dumped)" 消息而没有任何延迟或其他并发症。

我对它有一个大致的了解,但我无法解释它为什么会发生,我无法解决它,我不知道该怎么做,或者要寻找什么。

在浏览了很多主题之后我可以说的一些笔记:

没有导入,没有基于命令系统,没有sed,awk,没有数组,没有"complicated"命令。我只是在玩变量(整数)。没有字符串。最"complexe"的命令(获取随机数)大概是

(echo $((1 + RANDOM % 20))) 

我的条件都是这样

if [[ "$Skill_Block2" == "Yes" ]];
then

在双引号之间使用双括号和变量(我听说在条件中忘记双引号变量可能会导致问题)。

没有&&或||或 -a 或 -o (我还读到使用直接 "if" 语句会更好)

整个脚本都是围绕函数构建的(更容易修改/实现)。计算伤害的函数由一个函数调用,该函数检查一个角色是否可以躲避成功的攻击,该攻击已被另一个允许该攻击成功或失败的函数降落。等等,我不知道这是不是一个好的开发方式,但到目前为止"worked"。

我有口音和法语字符,但我的 OS 版本(Ubuntu)似乎很好地管理了它们。

我几乎重复了每一个决议,所以我可以跟踪错误。也许显示这么多文本正在占用我的虚拟内存?但老实说,我绝不会期望在 Linux 上出现这种情况。

我不认为我有无限循环,因为我可以 运行 它 50++ 次而没有任何问题,完全相同的顺序。

为了显示统计数据,我使用了一种肮脏的方式(我认为):

touch statistique.txt
    echo "#|Player 1|Player 2" > statistique.txt
    echo "ATT OK|$Number_Touch_OK1|$Number_Touch_OK1" >> statistique.txt
    echo "ATT Failed|$Number_Hit_Failed1|$Number_Hit_Failed2" >> statistique.txt
    echo "DEF Tried |$Number_Dodge_Tried1|$Number_Dodge_Tried2" >> statistique.txt
    [...]
    echo "Victory Number|$Victory1|$Victory2" >> statistique.txt
    echo " "
    column statistique.txt -t -s "|"

我想知道 EOF,但我不确定变量是否会被解释。但至少我有一个格式很好的文本。

我的 Ubuntu 在我的 Windows 上是 运行。可能是问题所在?


所以我来了。我感到很困惑,我不太热衷于将此消息作为没有任何代码的文字墙发布,因为它太长了(但如果有人足够勇敢,我可以分享代码,没问题)。 我在 bash 上看到的关于内存泄漏的消息太少了,所以...我无法想象 Linux OS 运行 内存不足 如果您有任何想法,建议, 软件(我试过 Valgrind,但同样,我不确定它是否适用于 bash 脚本),请告诉我。

编辑:这是文件 (solveur.sh): https://github.com/IlliciteS/script

正如评论中所指出的,您的程序无休止地递归。随着递归的每一级消耗更多的内存,bash 遇到分段错误。这是重现问题的最小脚本(包含在 bash -c 内,这样您的交互式 shell 就不会崩溃):

$ bash -c 'f() { f; }; f'
Segmentation fault (core dumped)

您可以通过使用 bash 的 FUNCNEST 变量限制最大递归级别来防止段错误。然而,这只会更优雅地中止一点。要解决实际问题,您必须重写脚本。

要确定问题,您可以查看脚本的调用图:

此图中的每个循环都可能使调用堆栈达到最大值。一点递归就可以了。但是对于您的任务,循环似乎更自然。我将从使用循环开始新的战斗开始。删除 Relance_Match() 并将您的主要 "method" 更改为

for ((i=0; i<Nombre_Match_Total; i++)); do
  Who_start_fight
  echo "..."
  Reinitialisation_Carac_New_Match
done
echo "..."
Statistique

与递归无关的进一步改进

您可以使用数组极大地简化脚本。现在,每个玩家都有自己的一组变量和代码片段,例如

# for player one
Victoire1=0
Touch1() {
  # lots of code using <insertThingHere>1, e.g.
  ((Victoire1++))
}

# for player two
Victoire2=0
Touch2() {
  # the same code as in Touch1, but with <insertThingHere>2 instead, e.g.
  ((Victoire2++))
}

使用数组可以简化为

Victoire=(0 0)
Touch() {
  player=
  # lots of code using <insertThingHere>[player], e.g.
  ((Victoire[player]++));
}