Então, eu fiz essa coisa nos testes e, sim, isso consome muita memória. Eu intencionalmente usei um número menor também. Eu posso imaginar que bash
monopolizar esses recursos por dias a fio poderia ser um pouco irritante.
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
Como você pode ver, há um impacto significativo nos recursos consumidos do processo. Bem, vou tentar esclarecer isso, mas - o mais próximo que eu possa dizer - será necessário substituir o processo do shell por outro.
Primeiro, vou definir uma variável de marcador apenas para mostrar que vem comigo. Nota: isso não é export
ed.
var='just
testing
'\''
this stuff
'\'''
Em seguida, vou executar $0
. Esse é o mesmo tipo de coisa que os daemons de longa duração devem fazer ocasionalmente para atualizar seu estado. Faz sentido aqui.
PRIMEIRO MÉTODO: AQUI DOCUMENTO
Usarei o shell atual para criar um descritor de arquivo de entrada heredoc para o novo processo de shell exec
ed que conterá todas as variáveis declaradas do shell atual. Provavelmente isso pode ser feito de forma diferente, mas eu não conheço todas as opções de linha de comando adequadas para bash
.
O novo shell é invocado com a opção -l
ogin - que fará com que seus arquivos profile/rc
sejam originados por padrão - e quaisquer outras opções de shell atualmente configuradas e armazenadas no parâmetro especial shell $-
. Se você acha que -l
ogin não é uma maneira correta de usar, em vez disso, usar a opção -i
deve pelo menos obter a execução do arquivo rc
.
exec "${0#-}" "-l$-" 3<<ENV
$(set)
ENV
Ok. Isso levou apenas um segundo. Como isso funcionou?
. /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
Muito bem, como parece. <&3
trava na entrada do novo processo shell até que seja lido - então eu faço isso com .
e a fonte. Ele provavelmente conterá algumas variáveis somente de leitura padrão que já foram definidas no novo shell por seus arquivos rc
e etc e, portanto, haverá alguns erros - mas eu despejo isso para 2>/dev/null
. Depois que eu fizer isso, como você pode ver, eu tenho todas as variáveis do processo do shell antigo aqui comigo - para incluir meu marcador $var
.
SEGUNDO MÉTODO: VARIÁVEIS DE MEIO AMBIENTE
Depois de fazer um google ou dois sobre o assunto, acho que isso pode ser outra maneira que vale a pena considerar. Inicialmente, considerei isso, mas ( aparentemente erroneamente ) desconsiderou essa opção com base na crença de que havia um limite de tamanho arbitrário imposto pelo kernel para o valor de uma única variável de ambiente - algo como ARGLEN ou LINEMAX (o que provavelmente afetará isso) , mas menor para um valor único. O que eu estava certo, no entanto, é que uma chamada execve
não funcionará quando o ambiente total é muito grande. E assim, acredito que isso deve ser preferido apenas no caso de você poder garantir que seu ambiente atual seja pequeno o suficiente para permitir uma chamada exec
.
Na verdade, isso é diferente o suficiente para fazer tudo de novo de uma só vez.
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
Uma coisa que não consegui fazer no primeiro turno é migrar as funções do shell. Sem contar que você está acompanhando (o que é provavelmente o melhor caminho) , pelo que eu sei, não há nenhuma maneira de fazer isso. O bash
permite isso, já que declare -f
funciona da mesma maneira para funções que set
faz para as variáveis do shell de forma portável. Para fazer isso também, com o primeiro método, basta adicionar ; declare -f
a set
no documento here.
Minha variável de marcador permanecerá a mesma, mas aqui está minha função de marcador:
chk () {
printf '###%s:###\n%s\n' \
\$VAR "${var-NOT SET}" \
PSINFO "$(ps -Fp $$)" \
ENV\ LEN "$(env | wc -c)"
}
E assim, ao invés de alimentar o novo shell com um descritor de arquivo, eu o entregarei em duas variáveis de ambiente:
varstate=$(set) fnstate=$(declare -f) exec "${0#-}" "-l$-"
Ok. Então eu acabei de substituir o shell em execução, e agora o que?
chk
bash: chk: command not found
Claro. Mas ...
{ 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