如何强制bash释放没有名称的大括号扩展变量?

对于benchmarking,我运行了命令:

for i in {1..100000000}; do 
  echo "$i" line >> file
done

Bash expanded the braces并将列表1 2 3 4 5 6 … 100000000存储在内存中.

我以为这会以某种方式解除分配.毕竟,这是一个临时变量.从那以后几天过去了,bash进程仍然占用了17.9GB的内存.

我可以强制bash清除这些临时变量吗?我不能使用unset,因为我不知道变量名. (显然我没有帮助)

当然,解决方案是关闭shell并打开一个新的.

我还在bash邮件列表上询问了这个问题,得到了Chet Ramsey的有用回复:

That’s not a memory leak. Malloc implementations need not release
memory back to the kernel; the bash malloc (and others) do so only
under limited circumstances. Memory obtained from the kernel using
mmap or sbrk and kept in a cache by a malloc implementation doesn’t
constitute a leak. A leak is memory for which there is no longer a
handle, by the application or by malloc itself.

malloc() is basically a cache between the application and the kernel.
It gets to decide when and how to give memory back to the kernel.

最佳答案
所以,我在测试中做了这件事,是的,它耗费了大量的内存.我也指出了一个较小的数字.我可以想象,持续数日囤积这些资源的bash可能会有点刺激.

ps -Fp "$$"; : {1..10000000}; ps -Fp "$$"

UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 32601  4241  0  3957  3756   4 08:28 pts/1    00:00:00 bash -l
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 32601  4241 59 472722 1878712 4 08:28 pts/1   00:00:28 bash -l

如您所见,对流程消耗的资源有重大影响.好吧,我会尝试清除它,但是 – 尽可能接近 – 它需要用另一个替换shell进程.

首先,我将设置一个标记变量,以显示它随我而来.注意:这不会导出.

var='just
testing
'\''
this stuff
'\'''

接下来我将执行0美​​元.这是长期运行的守护进程必须偶尔刷新状态的事情.这里有道理.

第一种方法:HERE-DOCUMENT

我将使用当前的shell为新执行的shell进程构建一个heredoc输入文件描述符,该进程将包含所有当前shell的声明变量.可能它可以以不同的方式完成,但我不知道bash的所有正确的命令行开关.

使用-login开关调用新shell – 它将确保您的profile / rc文件是按常规来源的 – 以及当前设置并存储在特殊shell参数$ – 中的任何其他shell选项.如果您认为-login不是正确的方法,那么使用-i开关应该至少应该运行rc文件.

exec "${0#-}" "-l$-" 3<<ENV
$(set)
ENV

好.那只花了一秒钟.它是如何工作的?

. /dev/fd/3 2>/dev/null
echo "$var"; ps -Fp "$$"

just
testing
'
this stuff
'
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 32601  4241 12  4054  3800   5 08:28 pts/1    00:00:29 bash -lhimBH

很好,就像看起来一样. <& 3挂起新的shell进程的输入,直到它被读取 - 所以我这样做.并采购它.它可能包含一些默认的只读变量,这些变量已经通过其rc文件等在新shell中设置,因此会有一些错误 - 但我将其转储到2> / dev / null.在我这样做之后,正如你所看到的,我在这里有所有旧shell进程的变量 – 包括我的标记$var.

第二种方法:环境变量

在做了一两个关于此事的谷歌之后,我认为这可能是另一种值得考虑的方式.我最初考虑过这一点,但是(apparently erroneously)基于这样的信念,即对单个环境变量的值有一个内核强制的任意长度限制 – 比如ARGLEN或LINEMAX(可能会影响这个),但对于单一价值.但是,我的正确理由是,当总环境太大时,execve呼叫将无效.因此,我认为只有在您可以保证当前环境足够小以允许执行exec调用的情况下,这应该是首选.

事实上,这是不同的,我将一次性完成所有这一切.

ps -pF "$$"; : {1..10000000}; ps -pF "$$"

UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 26296  4241  0  3957  3788   3 14:28 pts/1    00:00:00 bash -l
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 26296  4241 38 472722 1878740 3 14:28 pts/1   00:00:11 bash -l

我在第一轮中没有做的一件事就是迁移shell函数.不计算自己跟踪它们(这可能是最好的方法),据我所知,没有贝壳可移植的方法来做到这一点.但是bash确实允许它,因为declare -f对于为shell变量设置的函数的工作方式大致相同.要使用第一种方法执行此操作,您只需添加;声明-f在here-document中设置.

我的标记变量将保持不变,但这是我的标记函数:

chk () {
    printf '###%s:###\n%s\n' \
        \$VAR "${var-NOT SET}" \
        PSINFO "$(ps -Fp $$)" \
        ENV\ LEN "$(env | wc -c)"
}

因此,不是为新shell提供文件描述符,而是将其交给两个环境变量:

varstate=$(set) fnstate=$(declare -f) exec "${0#-}" "-l$-"

好.所以我刚刚更换了运行的shell,那么现在呢?

chk
bash: chk: command not found

当然.但…

{   echo '###EVAL/UNSET $FNSTATE###'
    eval "$fnstate"; unset fnstate
    chk
    echo '###EVAL/UNSET $VARSTATE###'
    eval "$varstate"; unset varstate
    chk
}

OUTPUT

###EVAL/UNSET $FNSTATE###
###$VAR:###
NOT SET
###PSINFO:###
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 26296  4241 10  3991  3736   1 14:28 pts/1    00:00:12 bash -lhimBH
###ENV LEN:###
6813
###EVAL/UNSET $VARSTATE###
bash: BASHOPTS: readonly variable
bash: BASH_VERSINFO: readonly variable
bash: EUID: readonly variable
bash: PPID: readonly variable
bash: SHELLOPTS: readonly variable
bash: UID: readonly variable
###$VAR:###
just
testing
'
this stuff
'
###PSINFO:###
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
mikeserv 26296  4241 10  4056  3772   1 14:28 pts/1    00:00:12 bash -lhimBH
###ENV LEN:###
2839

转载注明原文:如何强制bash释放没有名称的大括号扩展变量? - 代码日志