bashに名前なしで中括弧拡張変数を強制的に割り当て解除する方法は?

bashに名前なしで中括弧拡張変数を強制的に割り当て解除する方法は?

~のためベンチマーク、次のコマンドを実行します。

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

吹く中かっこ拡張そしてリストを1 2 3 4 5 6 ... 100000000メモリに保存します。

私の考えでは、これはいつか何とか公開されると思います。最終的には一時変数です。数日が経過しましたが、bashプロセスはまだ17.9GBのメモリを占めていました。

bashでこれらの一時変数を強制的に消去できますか?unset変数名がわからないため使用できません。 (明らかにunset i役に立たない)

もちろん、1つの解決策は、シェルを閉じて新しいシェルを開くことです。

私はbashメーリングリストにこの質問を投稿するのに役立つ答えを受けました。チェットラムジー:

これはメモリリークではありません。 Mallocの実装では、メモリをカーネルに再リリースする必要はありません。 bash malloc(およびその他)は限られた状況でのみそうします。 mmap または sbrk を使用してカーネルから取得したメモリと malloc を介してキャッシュに保存されたメモリは、リークを構成しません。リークは、アプリケーションまたは malloc 自体がこれ以上処理できないメモリです。

malloc() はデフォルトでアプリケーションとカーネル間のキャッシュです。カーネルにメモリを返すタイミングと方法を決定します。

答え1

だから私はテストでこれを行いましたが、メモリを大量に消費しました。また、意図的に小さい数字を使用しました。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

ご覧のとおり、これはプロセスが消費するリソースに大きな影響を与えます。まあ、私はこれを明確にしようとします。しかし、私が知っている限り、シェルプロセスを別のシェルプロセスに置き換える必要があります。

まず、表示するためにマーカー変数を設定します。注:これはexport編集内容ではありません。

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

これは、$0長期実行デーモンが状態を更新するために時々実行する必要がある作業と同じです。これは意味があります。

最初の方法:ここに文書化

現在、シェルを使用して、新しく編集されたシェルプロセスのHeredoc入力ファイル記述子を作成します。execこれには、現在シェルで宣言されているすべての変数が含まれます。多分違うかもしれませんがわかりませんbash

新しいシェルは、-loginスイッチ(ファイルが通常どおりインポートされることを保証profile/rc)と、現在の特別なシェルパラメータに設定および保存されている他のシェルオプションを使用して呼び出されます$--loginが正しいアプローチではないと思われる場合は、-iスイッチを使用して少なくともrcファイルを実行する必要があります。

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

いいです、たった1秒しかかかりませんでした。効果はどうですか?

. /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読み込まれるまで、新しいシェルプロセスの入力を待ちます。だからそうして.インポートします。おそらく、ファイルなどを介して新しいシェルに設定されたいくつかのデフォルトの読み取り専用変数が含まれているため、rcいくつかのエラーがあります2>/dev/null。ただし、これを行った後は、ご覧のとおり、すべての変数の変数があります。古いシェルプロセス - 私のフラグが含まれています$var

2番目の方法:環境変数

この問題についてGoogleを1〜2回検索した後、これが考慮する価値のある別のアプローチになる可能性があると思いました。最初はこう思ったけど明らかに間違っていた)単一の環境変数の値にカーネル強制ランダムな長さ制限があるという信念に基づいて、このオプションは考慮されません。アグレンまたはライアンマックス (これが影響を与える可能性があります)ただし、単一値の場合は小さいです。しかし、私の正しい声明は次のとおりです。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

最初のラウンドで私が失敗したことの1つは、シェル機能を移行することでした。直接追跡することは含まれません。(おそらくこれが最善の方法でしょう)、私が知っている限り、これを行うことができるシェル移植可能な方法はありません。bashただし、この機能は移植可能なシェル変数とほぼ同じ方法で機能するため、実際には許可されますdeclare -f。最初の方法を使用してこれを行うには、setここに; declare -f追加します。set

私のマーカー変数は同じままですが、私のマーカー機能は次のとおりです。

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

したがって、新しいシェルにファイル記述子を提供するのではなく、2つの環境変数に渡します。

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

わかりました、実行中のシェルを変更しました。今どうなりますか?

chk
bash: chk: command not found

確かに。しかし...

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

出力

###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

答え2

習慣。 Bashは、何らかの目的で割り当てられたメモリをオペレーティングシステムに返しません。 (しかし、私が間違っている場合は訂正してください。)

ただし、bashは必要に応じて他の目的でメモリを再利用し、そうでない場合はカーネルはメモリを交換して実際にRAMに入らないようにします。

関連情報