我有一个 shell 脚本,它使用单个变量作为关联数组(KEY=VALUE
每行一个)。
在脚本的整个执行过程中,变量被操纵以添加、删除或修改条目:
附加:
VARIABLE="$(printf "%s\n%s" "$VARIABLE" "KEY=VALUE")"
调整:
VARIABLE="$(printf "%s\n" "$VARIABLE" | sed -E "s,^(KEY=).*$,\1VALUE,")"
消除:
VARIABLE="$(printf "%s\n" "$VARIABLE" | grep -E -v "^KEY=.*$")"
当在终端中执行时(或作为旧机器上的服务)系统通过初始化脚本),该脚本运行良好,但是当作为服务运行时系统过了一会儿,脚本开始在日志中吐出错误消息:
sh: printf: I/O error
经过大量的试验和错误,我无法确定脚本中的哪些命令产生了这些错误,但我注意到当变量的长度达到 8000 字节(我猜是 8192,但我由于附加了整行,因此无法准确指出它)。
我很确定变量长度是问题所在,因为我实现了一个例程,只要变量长度接近 8192 字节,就会修剪数组中最旧的条目,现在脚本确实可以运行systemd
很长时间而不会出现错误;但这当然不理想,因为丢失了一些信息。
我在网上搜索了有关 shell 脚本中最大变量长度的信息,但没有找到任何有用的信息:
dash
手册页没有提及最大变量长度。
对于那些想要编写便携式的人sed脚本,请注意,已知某些实现会将行长度(模式和保留空间)限制为不超过 4000 字节。这POSIX标准规定符合sed实现应支持至少 8192 字节的行长度。GNU sed对线路长度没有内置限制;只要可以malloc()更多(虚拟)内存,您可以根据需要提供或构建线路。
...但这适用于线长度,而不是整个文本长度(单行不超过 80 个字符)
无论如何,由于错误仅在脚本运行时出现systemd
,我尝试在单元文件中增加LimitMSGQUEUE
和/或LimitSTACK
,但无济于事(这是一个盲目的猜测,因为我不完全理解消息队列或进程的概念堆栈,但显示的数字systemctl show
看起来像 8 KB 左右)。关于内存的所有其他限制(LimitRSS
、LimitAS
、LimitMEMLOCK
)似乎都足够高(远超过 8192 字节),所以我不知道下一步该怎么做。
systemd
当变量长度超过 8 KB 时,我该怎么做才能让这个脚本正常运行?
答案1
与其说是答案,不如说是诊断……
在我的系统上,运行dash
v0.5.8-2.10,可变长度可以相当大,至少2^30字符。演示,将变量的长度加倍,${x}
直到其字符长度${#x}
超过约25%可用内存(由特别指定 furp
功能):
首次启动dash
:
dash
然后,(在dash
)中运行以下代码:
furp() { free | { read z; read a b c d; echo $((100*$c/$b)) ; } }
x=1
while [ `furp` -lt 25 ] ; do
x="${x}${x}"; echo ${#x}
done | tail -1
输出(在我的系统上,可能会根据可用内存而有所不同):
1073741824
尝试将类似上面的代码放入脚本中,然后在相同的systemd
环境下运行它并检查输出。
答案2
(由于缺乏代表)
“sh: printf: I/O error” 表示脚本是使用sh
而不是dash
由 systemd 运行的,也许这就是 diff 发挥作用的地方?