Como eu gero as variáveis de ambiente de outro processo?

24

Se eu examinar /proc/1/environ , posso ver uma cadeia de caracteres delimitada por nulos de% de variáveis de ambiente do processo 1 . Eu gostaria de trazer essas variáveis para o meu ambiente atual. Existe uma maneira fácil de fazer isso?

A página proc man me fornece um snippet que ajuda a imprimir cada variável de ambiente linha por linha (cat /proc/1/environ; echo) | tr '%code%0' '\n' . Isso me ajuda a verificar se o conteúdo está correto, mas o que eu realmente preciso fazer é fornecer essas variáveis para a minha sessão bash atual.

Como faço isso?

    
por Dane O'Connor 16.04.2014 / 21:54

8 respostas

22

O seguinte converterá cada variável de ambiente em uma instrução export , devidamente citada para leitura em um shell (porque LS_COLORS , por exemplo, provavelmente terá ponto-e-vírgula nela), então a origina.

[O printf em /usr/bin , infelizmente, geralmente não suporta %q , por isso precisamos chamar o criado em bash .]

. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
    
por 16.04.2014 / 23:58
10

Em bash , você pode fazer o seguinte. Isso funcionará para todos os possíveis conteúdos das variáveis e evita eval :

while IFS= read -rd '' var; do declare +x "$var"; done </proc/$PID/environ

Isso irá declarar as variáveis de leitura como variáveis do shell no shell em execução. Para exportar as variáveis para o ambiente do shell em execução:

while IFS= read -rd '' var; do export "$var"; done </proc/$PID/environ
    
por 17.04.2014 / 01:21
10

Nesta resposta, assumo um sistema em que /proc/$pid/environ retorna o ambiente do processo com o PID especificado, com bytes nulos entre as definições das variáveis. ( Então Linux, Cygwin ou Solaris (?) ).

Zsh

export "${(@ps:
while IFS= read -r -d "" PWD; do export "$PWD"; done </proc/$pid/environ
PWD=$(pwd)
0:)$(</proc/$pid/environ)}"

(Muito simples como o zsh diz: um redirecionamento de entrada sem comando <FILE é equivalente a cat FILE . A saída da substituição do comando sofre expansão de parâmetro com a flags ps:@0: " dividir em bytes nulos "e "$@" " se a coisa toda estiver entre aspas duplas, então trate cada elemento da matriz como um campo separado "(generalizando read ). )

Bash, mksh

eval $(</proc/$pid/environ awk -v RS='
export "${(@ps:
while IFS= read -r -d "" PWD; do export "$PWD"; done </proc/$pid/environ
PWD=$(pwd)
0:)$(</proc/$pid/environ)}"
' '{gsub("7", "7\77"); print "export 7" $0 "7"}')

(Nestas shells, um delimitador vazio passado para PWD resulta em bytes nulos sendo separadores. Eu uso PWD como um nome de variável temporária para evitar a invasão de outra variável que pode acabar sendo importada. Embora você possa tecnicamente importar cd também, ele só ficaria parado até o próximo /proc/PID/environ .)

POSIX

A portabilidade POSIX não é interessante para essa questão, porque se aplica apenas a sistemas que possuem /proc/PID/environ . Portanto, a questão é o que o Solaris sed suporta - ou se o Solaris tem RS , ele não usava, mas estou muito atrás da curva dos recursos do Solaris para que isso aconteça hoje em dia. No Linux, os utilitários GNU e BusyBox são nulos, mas com restrições.

Se insistirmos na portabilidade POSIX, nenhum dos utilitários de texto POSIX é necessário para lidar com bytes nulos, então isso é difícil. Aqui está uma solução que assume que o awk suporta um byte nulo como o delimitador de registro (nawk e gawk, assim como o BusyBox awk, mas o mawk não).

eval $(</proc/$pid/environ awk -v RS='%pre%' '{gsub("7", "7\77"); print "export 7" $0 "7"}')

O BusyBox awk (que é a versão comumente encontrada em sistemas Linux embarcados) suporta bytes nulos, mas não define "BEGIN" to -v 'RS=""7""' em um bloco %code% e não a sintaxe da linha de comando acima; no entanto, ele suporta %code% . Eu não investiguei por que, isso parece um bug na minha versão (Debian wheezy).

(Embrulhe todas as linhas registros separados por nulo em aspas simples %code% , depois de escapar as aspas simples dentro dos valores.)

Advertências

Tenha em atenção que qualquer um destes pode tentar definir variáveis só de leitura (se a sua shell tiver variáveis só de leitura).

    
por 17.04.2014 / 02:27
6

Eu dei voltas e voltas com isso. Eu estava frustrado com a portabilidade de bytes nulos. Não me agradou que não houvesse uma maneira confiável de lidar com eles em uma concha. Então continuei procurando. A verdade é que eu encontrei várias maneiras de fazer isso, apenas alguns dos quais são anotados na minha outra resposta. Mas os resultados foram pelo menos duas funções de shell que funcionam assim:

_pidenv ${psrc=$$} ; _zedlmt <$near_any_type_of_file

Primeiro, falarei sobre a delimitação de od . Na verdade, é bem fácil de fazer. Aqui está a função:

_zedlmt() { od -t x1 -w1 -v  | sed -n '
    /.* \(..\)$/s///
    /00/!{H;b};s///
    x;s/\n/\x/gp;x;h'
}

Basicamente stdin recebe stdout e grava em seu sednull cada byte que recebe em hexadecimal um por linha.

printf 'This
printf %b\n $(printf 'Fewer
printf %b\n $(printf \
        "Fe\n\"w\"er
_pidenv() { ps -p $1 >/dev/null 2>&1 &&
        [ -z "${1#"$psrc"}" ] && . /dev/fd/3 ||
        cat <&3 ; unset psrc pcat
} 3<<STATE
        $( [ -z "${1#${pcat=$psrc}}" ] &&
        pcat='$(printf %%b "%s")' || pcat="%b"
        xeq="$(printf '\x%x' "'=")"
        for x in $( _zedlmt </proc/$1/environ ) ; do
        printf "%b=$pcat\n" "${x%%"$xeq"*}" "${x#*"$xeq"}"
        done)
#END
STATE
'nu\t'll\'s
#output like printenv for any running process
_pidenv $pid 

#save human friendly env file
_pidenv $pid >/preparsed/env/file 

#save unparsed file for sourcing at any time
_pidenv ${pcat=$pid} >/sourcable/env.save 

#.dot source any pid's $env from any file stream    
_pidenv ${pcat=$pid} | sh -c '. /dev/stdin'

#feed any pid's env in on a heredoc filedescriptor
su -c '. /dev/fd/4' 4<<ENV
    $( _pidenv ${pcat=$pid} )
ENV

#.dot sources any $pid's $env in the current shell
_pidenv ${psrc=$pid} 
h ere
_pidenv ${pcat=$$}
    #output
LC_COLLATE=$(printf %b "\x43")
GREP_COLOR=$(printf %b "\x33\x37\x3b\x34\x35")
GREP_OPTIONS=$(printf %b "\x2d\x2d\x63\x6f\x6c\x6f\x72\x3d\x61\x75\x74\x6f")
LESS_TERMCAP_mb=$(printf %b "\x1b\x5b\x30\x31\x3b\x33\x31\x6d")
LESS_TERMCAP_md=$(printf %b "\x1b\x5b\x30\x31\x3b\x33\x31\x6d")
LESS_TERMCAP_me=$(printf %b "\x1b\x5b\x30\x6d")
LESS_TERMCAP_se=$(printf %b "\x1b\x5b\x30\x6d")
LESS_TERMCAP_so=$(printf %b "\x1b\x5b\x30\x30\x3b\x34\x37\x3b\x33\x30\x6d")
LESS_TERMCAP_ue=$(printf %b "\x1b\x5b\x30\x6d")
." | _zedlmt | tee /dev/stderr) #output \x46\x65\x0a\x22\x77\x22\x65\x72 \x27\x6e\x75\x09\x27\x6c\x6c\x27\x73 \x68\x20\x20\x20\x20\x65\x72\x65 \x2e Fe "w"er 'nu 'll's h ere .
nulls
_pidenv ${psrc=$$} ; _zedlmt <$near_any_type_of_file
here
_zedlmt() { od -t x1 -w1 -v  | sed -n '
    /.* \(..\)$/s///
    /00/!{H;b};s///
    x;s/\n/\x/gp;x;h'
}
.' | _zedlmt | tee /dev/stderr) #output \x46\x65\x77\x65\x72 \x6e\x75\x6c\x6c\x73 \x68\x65\x72\x65 \x2e Fewer nulls here .
is
printf 'This
printf %b\n $(printf 'Fewer
printf %b\n $(printf \
        "Fe\n\"w\"er
_pidenv() { ps -p $1 >/dev/null 2>&1 &&
        [ -z "${1#"$psrc"}" ] && . /dev/fd/3 ||
        cat <&3 ; unset psrc pcat
} 3<<STATE
        $( [ -z "${1#${pcat=$psrc}}" ] &&
        pcat='$(printf %%b "%s")' || pcat="%b"
        xeq="$(printf '\x%x' "'=")"
        for x in $( _zedlmt </proc/$1/environ ) ; do
        printf "%b=$pcat\n" "${x%%"$xeq"*}" "${x#*"$xeq"}"
        done)
#END
STATE
'nu\t'll\'s
#output like printenv for any running process
_pidenv $pid 

#save human friendly env file
_pidenv $pid >/preparsed/env/file 

#save unparsed file for sourcing at any time
_pidenv ${pcat=$pid} >/sourcable/env.save 

#.dot source any pid's $env from any file stream    
_pidenv ${pcat=$pid} | sh -c '. /dev/stdin'

#feed any pid's env in on a heredoc filedescriptor
su -c '. /dev/fd/4' 4<<ENV
    $( _pidenv ${pcat=$pid} )
ENV

#.dot sources any $pid's $env in the current shell
_pidenv ${psrc=$pid} 
h ere
_pidenv ${pcat=$$}
    #output
LC_COLLATE=$(printf %b "\x43")
GREP_COLOR=$(printf %b "\x33\x37\x3b\x34\x35")
GREP_OPTIONS=$(printf %b "\x2d\x2d\x63\x6f\x6c\x6f\x72\x3d\x61\x75\x74\x6f")
LESS_TERMCAP_mb=$(printf %b "\x1b\x5b\x30\x31\x3b\x33\x31\x6d")
LESS_TERMCAP_md=$(printf %b "\x1b\x5b\x30\x31\x3b\x33\x31\x6d")
LESS_TERMCAP_me=$(printf %b "\x1b\x5b\x30\x6d")
LESS_TERMCAP_se=$(printf %b "\x1b\x5b\x30\x6d")
LESS_TERMCAP_so=$(printf %b "\x1b\x5b\x30\x30\x3b\x34\x37\x3b\x33\x30\x6d")
LESS_TERMCAP_ue=$(printf %b "\x1b\x5b\x30\x6d")
." | _zedlmt | tee /dev/stderr) #output \x46\x65\x0a\x22\x77\x22\x65\x72 \x27\x6e\x75\x09\x27\x6c\x6c\x27\x73 \x68\x20\x20\x20\x20\x65\x72\x65 \x2e Fe "w"er 'nu 'll's h ere .
nulls%pre%here%pre%.' | _zedlmt | tee /dev/stderr) #output \x46\x65\x77\x65\x72 \x6e\x75\x6c\x6c\x73 \x68\x65\x72\x65 \x2e Fewer nulls here .
is%pre%a%pre%lot%pre%\of%pre%\nulls.' | od -t x1 -w1 -v #output 0000000 54 0000001 68 0000002 69 0000003 73 0000004 00 0000005 69 0000006 73 #and so on
a%pre%lot%pre%\of%pre%\nulls.' | od -t x1 -w1 -v #output 0000000 54 0000001 68 0000002 69 0000003 73 0000004 00 0000005 69 0000006 73 #and so on

Aposto que você pode adivinhar qual é o sed , certo? Escrito assim, é fácil lidar com qualquer printf . teenull apenas salva os dois últimos caracteres em cada linha até encontrar um nulo, substituindo as novas linhas intermediárias por printf e formatando a string. O resultado é uma matriz delimitada por printf de cadeias de bytes hexadecimais. Olhe:

%pre%

Eu cansei o acima para \nnull para que você possa ver a saída do comando susbstitution e o resultado do processamento de sed . Espero que você perceba que o subshell realmente não é cotado, mas _zedlmt ainda é dividido apenas no delimitador ${pcat} . Olhe:

%pre%

Não há citações nessa expansão - não importa se você cita ou não. Isso ocorre porque os valores de mordida são obtidos separadamente, com exceção do /proc ewline gerado para cada vez que .dot imprime uma sequência. A divisão de palavras não se aplica. E é isso que torna isso possível:

%pre%

A função acima usa ${psrc} para set um fluxo preparado de código de bytes para fornecimento de ambiente de qualquer processo que pode ser encontrado em printenv ou diretamente para $pid /proc/$pid/environ o mesmo no atual shell, ou sem um parâmetro, para exibir uma saída processada do mesmo para o terminal, como $var ou .dot . Tudo o que você precisa é de um %code% - qualquer arquivo readable %code% .

Você usa assim:

%pre%

Mas qual é a diferença entre human friendly e sourcable ? Bem, a diferença é o que torna esta resposta diferente de todas as outras aqui - incluindo a minha outra. Todas as outras respostas dependem do shell de uma forma ou de outra para lidar com todos os casos de borda. Simplesmente não funciona tão bem assim. Por favor, acredite em mim - eu tentei. Look:

%pre%

NÃO quantidade de caracteres ou citações contidas pode quebrar isso porque os bytes para cada valor não são avaliados até o instante em que o conteúdo é originado. E nós já sabemos que funcionou como um valor pelo menos uma vez - não há nenhuma análise ou proteção de cotação necessária aqui porque esta é uma cópia byte a byte do valor original.

A função primeiro avalia os nomes %code% e aguarda que as verificações sejam concluídas antes de %code% fornecer o aqui-doc alimentando-o no descritor de arquivo 3. Antes de originar, é isso que parece. É à prova de idiotas. E POSIX portátil. Bem, pelo menos o tratamento do \ 0null é POSIX portable - o sistema de arquivos / process é obviamente específico do Linux. E é por isso que existem duas funções.

    
por 19.04.2014 / 16:05
3

Usando source e processo de substituição :

source <(sed -r -e 's/([^\x00]*)\x00/export \n/g' /proc/1/environ)

Logo:

. <(sed -r -e 's/([^\x00]*)\x00/export \n/g' /proc/1/environ)

Usando eval e substituição de comando :

eval 'sed -r -e 's/([^\x00]*)\x00/export \n/g' /proc/1/environ'

A chamada sed pode ser substituída por uma chamada awk :

awk -vRS='\x00' '{ print "export", $0 }' /proc/1/environ

Mas não se esqueça de que não limpa nenhuma variável de ambiente que não esteja no pid 1.

    
por 16.04.2014 / 22:27
3

Vale notar que processos podem ter variáveis de ambiente que não são válidas. Variáveis Bash / Sh / * sh - o POSIX recomenda, mas não requer que as variáveis de ambiente tenham nomes correspondentes a ^[a-zA-Z0-9_][a-zA-Z0-9_]*$ .

Para gerar uma listagem de variáveis compatíveis com shell no ambiente de outro processo, no Bash:

function env_from_proc {
  local pid="$1" skipped=( )
  cat /proc/"$pid"/environ | while read -r -d "" record
  do
    if [[ $record =~ ^[a-zA-Z_][a-zA-Z0-9_]*= ]]
    then printf "export %q\n" "$record"
    else skipped+=( "$record" )
    fi
  done
  echo "Skipped non-shell-compatible vars: ${skipped[@]%%=*}" >&2
}

Da mesma forma, para carregá-los:

function env_from_proc {
  local pid="$1" skipped=( )
  while read -r -d "" record
  do
    if [[ $record =~ ^[a-zA-Z_][a-zA-Z0-9_]*= ]]
    then export "$(printf %q "$record")"
    else skipped+=( "$record" )
    fi
  done < /proc/"$pid"/environ
  echo "Skipped non-shell-compatible vars: ${skipped[@]%%=*}" >&2
}

Este problema surge apenas ocasionalmente, mas quando acontece ...

    
por 04.10.2015 / 01:26
0

Acho que isso é portátil POSIX:

. <<ENV /dev/stdin
    $(sed -n 'H;${x;s/\(^\|\x00\)\([^=]*.\)\([^\x00]*\)/\x27\x27\n/gp}' \
       /proc/$pid/environ)
ENV

Mas @Gilles faz um bom argumento - sed provavelmente irá lidar com valores nulos, mas talvez não. Então existe esse método portátil POSIX, também:

s=$$SED$$
sed 's/'\''/'$s'/;1s/^./'\''&/' </proc/"$$"/environ |
tr '
sed -z 's/^[^=]*./&'\''/;s/$/'\''\n/' </proc/"$$"/environ

  #BOTH METHODS OUTPUT:
' "'" | sed 's/'\''/&\n&/g' | sed '1d;$d;s/^\('\''\)\([^=]*.\)//;s/'$s'/'\\''/g'

Ainda assim, se você tem o GNU sed , só precisa fazer:

nsenter -m -u -i -n -p -t $PID /bin/bash 5<<ENV --rcfile=/dev/fd/5 
    $(sed -z 's/^[^=]*./&'\''/;s/$/'\''\n/' </proc/"$$"/environ)
ENV

Bem,POSIXportável,excetopelo/dev/...,quenãoéespecificado,masvocêpodeesperarqueasintaxesecomportenamaioriadosUnices.

Agora,seissotiveralgoavercom sua outra pergunta , você pode gostar de usá-lo assim:

sed '...;a\exec <>/dev/tty' /proc/$pid/environ | sh -i 

O aqui-doc é extremamente útil na medida em que evita que o shell trepe com qualquer uma das citações que trabalhamos tão duro para manipular no subshell e também nos fornece um caminho confiável para um arquivo <%> / em> em vez de, novamente, uma subshell ou uma variável shell. Outros aqui usam o .dot bashism que funciona da mesma maneira - só que é definitivamente um <(process substitution) anônimo, enquanto o POSIX só especifica um |pipe para o here-docs e então pode ser qualquer tipo de arquivo, embora, na prática , geralmente é um arquivo iohere . ( temp , por outro lado, usa anônimo dash, para o here-docs) . A coisa infeliz sobre a substituição de processos é que ela também é dependente do shell - o que pode ser um problema especialmente irritante se você estiver trabalhando com |pipes .

Isso também funciona com init , mas você perde o ambiente novamente no final, quando o estado |pipes evapora com sua subcamada. Então, novamente, isso funciona:

. <<ENV /dev/stdin
    $(sed -n 'H;${x;s/\(^\|\x00\)\([^=]*.\)\([^\x00]*\)/\x27\x27\n/gp}' \
       /proc/$pid/environ)
ENV

A própria instrução |pipe's funciona mantendo todas as linhas na memória até que ela atinja a última, momento em que realiza uma substituição global manipulando aspas e inserindo novas linhas onde apropriado ancorando nos nulos. Bastante simples na verdade.

Na imagem sed , você verá que eu optei por evitar a \ mess e adicionei a opção dash specific GNU a -r . Mas isso é apenas porque era menos para digitar. Funciona de qualquer forma, como você pode ver na imagem sed .

Aqui está o zsh :

Eaquiestázshfazendoamesmacoisa:

Mesmo escapes de terminal passam por incólumes:

    
por 17.04.2014 / 08:03
-2
eval \'(cat /proc/1/environ; echo) | tr '
eval \'(cat /proc/1/environ; echo) | tr '%pre%0' '\n'\'
0' '\n'\'
    
por 16.04.2014 / 22:23