Come posso trovare le variabili d'ambiente di un altro processo?


24

Se esamino /proc/1/environposso vedere una stringa delimitata da byte nulli di 1variabili d'ambiente del processo . Vorrei portare queste variabili nel mio ambiente attuale. C'è un modo semplice per farlo?

Il proc pagina man mi dà uno snippet che aiuta a stampare ogni variabile d'ambiente riga per riga (cat /proc/1/environ; echo) | tr '\000' '\n'. Questo mi aiuta a verificare che i contenuti siano corretti, ma quello che devo davvero fare è procurarmi queste variabili nella mia sessione bash corrente.

Come lo faccio?

Risposte:


23

Quanto segue convertirà ogni variabile di ambiente in exportun'istruzione, correttamente quotata per la lettura in una shell (perchéLS_COLORS , ad esempio, è probabile che contenga dei punti e virgola), quindi la fonte.

[Il printfa /usr/bin, purtroppo, in genere non supporta %q, quindi abbiamo bisogno di chiamare quello costruito in bash.]

. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)

Suggerisco . <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ), che gestirà correttamente anche le variabili con virgolette.
John Kugelman sostiene Monica il

@JohnKugelman Grazie mille per il miglioramento, usando "$@"invece di '{}'. Per coloro che si chiedono l' --argomento nella sua risposta migliorata: gli argomenti posizionali a cui bash -c command_stringvengono assegnati a partire da $0, mentre si "$@"espande per includere gli argomenti a partire da $1. L'argomento --viene assegnato a $0.
Mark Plotnick,

10

In bashpuoi fare quanto segue. Questo funzionerà per tutti i possibili contenuti delle variabili ed evita eval:

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

Questo dichiarerà le variabili di lettura come variabili di shell nella shell in esecuzione. Per esportare invece le variabili nell'ambiente shell in esecuzione:

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

10

In questa risposta, presumo un sistema in cui /proc/$pid/environrestituisce l'ambiente del processo con il PID specificato, con byte null tra le definizioni delle variabili. ( Quindi Linux, Cygwin o Solaris (?) ).

zsh

export "${(@ps:\000:)$(</proc/$pid/environ)}"

(Abbastanza semplice come va zsh: an reindirizzamento di input senza comando <FILE equivale a cat FILE. L'output della sostituzione del comando subisce l'espansione dei parametri con i flag che ps:\000: significano "diviso su byte nulli" e che @significa "se il tutto è racchiuso tra virgolette doppie, allora trattate ogni elemento dell'array come un campo separato ”(generalizzazione "$@").)

Bash, mksh

while IFS= read -r -d "" PWD; do export "$PWD"; done </proc/$pid/environ
PWD=$(pwd)

(In queste shell, un delimitatore vuoto è passato a readrisultati in byte nulli che sono separatori. Uso PWDun nome di variabile temporaneo per evitare di intasare un'altra variabile che potrebbe finire per essere importata. Mentre si potrebbe importare anche tecnicamente PWD, rimarrebbe solo fino a quando il prossimo cd.)

POSIX

La portabilità POSIX non è così interessante per questa domanda, perché si applica solo ai sistemi che lo hanno /proc/PID/environ. Quindi la domanda è: cosa supporta Solaris sed - o se Solaris lo ha fatto /proc/PID/environ, non lo era, ma sono molto indietro rispetto alle caratteristiche di Solaris, quindi oggi potrebbe esserlo. Su Linux, i programmi di utilità GNU e BusyBox sono entrambi null-safe, ma con avvertimenti.

Se insistiamo sulla portabilità POSIX, nessuna delle utility di testo POSIX è richiesta per gestire byte null, quindi questo è difficile. Ecco una soluzione che presuppone che awk supporti un byte null come delimitatore del record (come fanno nawk e gawk, come fa Awk BusyBox, ma non fa mawk).

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

BusyBox awk (che è la versione che si trova comunemente sui sistemi Linux incorporati) supporta byte nulli ma non è impostato RSsu"\0" un BEGINblocco e non la sintassi riga precedente di comando; tuttavia supporta -v 'RS="\0"'. Non ho studiato il perché, questo sembra un bug nella mia versione (Debian wheezy).

(Avvolgi tutte le linee di record separati da null tra virgolette singole "\047", dopo aver evitato le virgolette singole all'interno di valori.)

Avvertenze

Attenzione che uno di questi potrebbe tentare di impostare variabili di sola lettura (se la shell ha variabili di sola lettura).


Alla fine sono tornato a questo. Ho capito un modo infallibile di fare questo o qualsiasi altro trattamento nullo in tutte le conchiglie che conosco abbastanza semplicemente. Vedi la mia nuova risposta
Mikeserv,

6

Sono andato in giro con questo. Ero frustrato dalla portabilità dei byte null. Non mi andava bene che non esistesse un modo affidabile per gestirli in una shell. Quindi ho continuato a cercare. La verità è che ho trovato diversi modi per farlo, solo un paio dei quali sono indicati nella mia altra risposta. Ma i risultati sono stati almeno due funzioni shell che funzionano in questo modo:

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

Per prima cosa parlerò della \0delimitazione. In realtà è abbastanza facile da fare. Ecco la funzione:

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

Praticamente odprende stdine scrive su stdoutogni byte che riceve in esadecimale uno per riga.

printf 'This\0is\0a\0lot\0\of\0\nulls.' |
    od -t x1 -w1 -v
    #output
0000000 54
0000001 68
0000002 69
0000003 73
0000004 00
0000005 69
0000006 73
    #and so on

Scommetto che puoi indovinare qual è il \0null, giusto? Scritto in questo modo è facile da gestire con qualsiasi sed . sedsalva solo gli ultimi due caratteri in ogni riga fino a quando non incontra un valore null a quel punto sostituisce le newline intermedie con printfun codice di formato intuitivo e stampa la stringa. Il risultato è una \0nullmatrice delimitata di stringhe di byte esadecimali. Guarda:

printf %b\\n $(printf 'Fewer\0nulls\0here\0.' |
    _zedlmt | tee /dev/stderr)
    #output
\x46\x65\x77\x65\x72
\x6e\x75\x6c\x6c\x73
\x68\x65\x72\x65
\x2e
Fewer
nulls
here
.

Ho analizzato quanto sopra in teemodo da poter vedere sia l'output del comando susbstitution che il risultato printfdell'elaborazione. Spero che noterai che la subshell in realtà non è nemmeno quotata ma è printfancora divisa solo dal \0nulldelimitatore. Guarda:

printf %b\\n $(printf \
        "Fe\n\"w\"er\0'nu\t'll\\'s\0h    ere\0." |
_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
.

Neanche citazioni su quell'espansione - non importa se la citate o no. Ciò è dovuto al fatto che i valori del morso vengono \ntrasmessi senza separazione, ad eccezione di una ewline generata ogni volta che sedstampa una stringa. La divisione delle parole non si applica. E questo è ciò che lo rende possibile:

_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

Gli usi funzione sopra _zedlmtad sia ${pcat}un flusso preparato di codice byte per l'ambiente approvvigionamento di qualsiasi processo che può essere trovato in /proc, o direttamente .dot ${psrc}lo stesso nella shell corrente, o senza un parametro, per visualizzare un'uscita elaborata stessi al terminale come seto printenvvolontà. Tutto ciò che serve è un $pid- qualsiasi/proc/$pid/environ file leggibile farà.

Lo usi in questo modo:

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

Ma qual è la differenza tra umano amichevole e sourcable ? Bene, la differenza è ciò che rende questa risposta diversa dalle altre qui - compresa la mia altra. Ogni altra risposta dipende dalla quotazione della shell in un modo o nell'altro per gestire tutti i casi limite. Semplicemente non funziona così bene. Per favore, credimi, ho provato. Guarda:

_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")

NESSUN numero di caratteri funky o citazioni contenute può interrompere questo perché i byte per ciascun valore non vengono valutati fino all'istante in cui il contenuto viene fornito. E sappiamo già che ha funzionato come valore almeno una volta: qui non è necessaria alcuna protezione di analisi o quotazione perché si tratta di una copia byte per byte del valore originale.

La funzione valuta innanzitutto i $varnomi e attende che vengano completati i controlli prima di .dotapprovvigionare il here-doc alimentandolo sul descrittore di file 3. Prima che lo generi è come appare. È a prova di folle. E POSIX portatile. Bene, almeno la gestione \ 0null è POSIX portatile - il filesystem / process è ovviamente specifico per Linux. Ed è per questo che ci sono due funzioni.


3

Utilizzo sourceed elaborazione della sostituzione :

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

In breve:

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

Utilizzo evale sostituzione comandi :

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

La sedchiamata può essere sostituita con una awkchiamata:

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

Ma non dimenticare che non cancella alcuna variabile d'ambiente che non sia nel pid 1.


L'esportazione è ridondante nella risposta di @ fr00tyl00p? Perché se non sembra abbastanza importante
Dane O'Connor,

Sì, è necessaria l'esportazione. Lo aggiusterò.
Pavel Šimerda,

3
Tutti questi comandi soffocano su valori che contengono newline e (a seconda del comando) altri caratteri.
Gilles 'SO- smetti di essere malvagio' il

Corretta. Manterrà comunque la risposta come riferimento.
Pavel Šimerda,

3

Vale la pena notare che i processi possono avere variabili di ambiente che non sono variabili Bash / Sh / * sh valide - POSIX consiglia ma non richiede che le variabili di ambiente abbiano nomi corrispondenti ^[a-zA-Z0-9_][a-zA-Z0-9_]*$.

Per generare un elenco di variabili compatibili con la shell dall'ambiente di un altro processo, in 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
}

Allo stesso modo, per caricarli:

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
}

Questo problema si presenta solo occasionalmente ma quando lo fa ...


0

Penso che questo sia POSIX portatile:

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

Ma @Gilles ha ragione: sedprobabilmente gestirà i null, ma forse no. Quindi c'è questo (lo penso davvero questa volta) in realtà anche il metodo portatile POSIX:

s=$$SED$$
sed 's/'\''/'$s'/;1s/^./'\''&/' </proc/"$$"/environ |
tr '\0' "'" |
sed 's/'\''/&\n&/g' |
sed '1d;$d;s/^\('\''\)\([^=]*.\)/\2\1/;s/'$s'/'\\\''/g'

Tuttavia, se hai GNU seddevi solo fare:

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

  #BOTH METHODS OUTPUT:

inserisci qui la descrizione dell'immagine

Bene, POSIX portatile è diverso da quello /dev/...non specificato ma ci si può aspettare che la sintassi si comporti allo stesso modo sulla maggior parte degli Unices.

Ora, se questo ha qualcosa a che fare con l'altra tua domanda , ti piacerebbe usarlo in questo modo:

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

Il here-doc è estremamente utile in quanto evita che la shell si rovini con una qualsiasi delle citazioni che lavoriamo così duramente da gestire nella subshell e ci fornisce anche un percorso affidabile per un file.dot reperibile piuttosto che, ancora, una subshell o una shell variabile. Altri qui usano il bashismo che funziona più o meno allo stesso modo - solo che è sicuramente un anonimo mentre POSIX specifica solo un documento qui e quindi può essere qualsiasi tipo di file, anche se, in pratica, di solito è un file. ( d'altra parte, usa anonimo per here-docs) . La cosa sfortunata nella sostituzione del processo, tuttavia, è anche dipendente dalla shell - che potrebbe essere un problema particolarmente fastidioso se stai lavorando con<(process substitution)|pipeioheretempdash,|pipesinit .

Questo funziona |pipesovviamente anche con , ma alla fine si perde di nuovo l'ambiente quando lo |pipe'sstato evapora con la sua subshell. Quindi di nuovo, questo funziona:

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

L' sedistruzione stessa funziona mantenendo ogni riga in memoria fino a quando non raggiunge l'ultima, momento in cui esegue una sostituzione globale gestendo la quotazione e inserendo le nuove righe ove appropriato ancorando sui null. Abbastanza semplice davvero.

Nell'immagine dashvedrai che ho scelto di evitare il \ mess e ho aggiunto l' opzione GNUspecifica -ra sed. Ma questo è solo perché era meno da digitare. Funziona in entrambi i modi, come puoi vedere zshnell'immagine.

Ecco zsh:

inserisci qui la descrizione dell'immagine

E qui sta dashfacendo la stessa cosa varia:

inserisci qui la descrizione dell'immagine

Anche le fughe dei terminali arrivano incolumi:

inserisci qui la descrizione dell'immagine


Questo non è POSIX-portatile perché sed non è richiesto per gestire byte null. (Detto questo, la portabilità POSIX non è così interessante per questa domanda, perché si applica solo ai sistemi che hanno /proc/PID/environ. Quindi la domanda è cosa supporta Solaris sed - o se Solaris ha /proc/PID/environ, non lo usava ma io sono così dietro la curva delle funzioni di Solaris, quindi potrebbe essere al giorno d'oggi.)
Gilles 'SO- smetti di essere malvagio'

@Gilles No. Ma sedè necessario per gestire ASCII esadecimali, di cui il byte null è uno. Inoltre, in realtà ho pensato solo a un modo molto più semplice di farlo ancora.
Mikeserv,

No, POSIX afferma che "I file di input devono essere file di testo" (per sed e altre utilità di testo) e definisce i file di testo come "file che contiene caratteri organizzati in una o più righe. Le righe non contengono caratteri NUL (...) ". E comunque la \xNNsintassi non è richiesta in POSIX, nemmeno la \OOOsintassi ottale (nelle stringhe C e in awk, sì, ma non in sed regexps).
Gilles 'SO- smetti di essere malvagio' il

@Gilles hai ragione. Ho guardato dappertutto e non sono riuscito a trovare quello che pensavo di poter fare prima. Quindi l'ho fatto diversamente. Modifica ora.
Mikeserv,

Per quanto posso dire, /proc/PID/environdopo tutto Solaris non ha (ha diverse altre voci simili a Linux /proc/PID, ma non environ). Quindi una soluzione portatile non ha bisogno di andare oltre gli strumenti di Linux, il che significa GNU sed o BusyBox sed. Entrambi supportano \x00in regexp, quindi il tuo codice è portatile quanto necessario (ma non POSIX). È troppo complesso però.
Gilles 'SO- smetti di essere malvagio' il

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.