web-dev-qa-db-ja.com

別のプロセスの環境変数を取得するにはどうすればよいですか?

私が調べれば/proc/1/environ nullバイトで区切られたプロセスの文字列が表示されます1の環境変数。これらの変数を現在の環境に取り入れたいのですが。これを行う簡単な方法はありますか?

proc manページには、各環境変数を行ごとに出力するのに役立つスニペットが用意されています(cat /proc/1/environ; echo) | tr '\000' '\n'。これは、内容が正しいことを確認するのに役立ちますが、実際に必要なことは、これらの変数を現在のbashセッションに取り込むことです。

それ、どうやったら出来るの?

24
Dane O'Connor

以下は、各環境変数をexportステートメントに変換し、シェルへの読み取り用に適切に引用されます(たとえば、LS_COLORSにはセミコロンが含まれている可能性があるため)。

[残念ながら、/usr/binprintfは、通常%qをサポートしていないため、bashに組み込まれているものを呼び出す必要があります。]

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

bashでは、次のことができます。これは、変数のすべての可能なコンテンツに対して機能し、evalを回避します。

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

これにより、実行中のシェルで読み取り変数がシェル変数として宣言されます。代わりに、実行中のシェル環境に変数をエクスポートするには:

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

この回答では、/proc/$pid/environが指定されたPIDを持つプロセスの環境を返し、変数定義の間にnullバイトがあるシステムを想定しています。 ( したがって、Linux、CygwinまたはSolaris(?) )。

Zsh

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

(zshのようにかなり単純: コマンドなしの入力リダイレクト<FILEcat FILEと同等です。コマンド置換の出力は flagsps:\000:は「nullバイトで分割」を意味し、@は「全体が二重引用符で囲まれている場合、各配列要素を個別のフィールドとして扱う」("$@"を一般化する) 。)

バッシュ、mksh

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

(これらのシェルでは、空の区切り文字をreadに渡した結果、nullバイトがセパレーターになりました。一時的な変数名としてPWDを使用して、インポートされる可能性のある別の変数を壊さないようにします。技術的にはPWDもインポートできますが、次のcdまでしか配置されません。)

POSIX

POSIXの移植性は、/proc/PID/environを備えたシステムにのみ適用されるため、この質問にはそれほど興味深いものではありません。したがって、問題はSolaris sedが何をサポートするか、またはSolarisが/proc/PID/environを持っているかどうかであり、それを使用していませんでしたが、私はSolaris機能のカーブに遅れをとっているので、今日はそうかもしれません。 Linuxでは、GNUユーティリティとBusyBoxはどちらもnullセーフですが、警告があります。

POSIXの移植性を主張する場合、nullバイトを処理するために必要なPOSIXテキストユーティリティはないため、これは困難です。以下は、awkがレコード区切り文字としてnullバイトをサポートすることを前提とするソリューションです(BusyBox awkと同様に、nawkとgawkはサポートしますが、mawkはサポートしません)。

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

BusyBox awk(組み込みLinuxシステムで一般的に見られるバージョンです)はnullバイトをサポートしますが、RSブロックでBEGIN"\0"に設定しません。上記のコマンドライン構文ではサポートしません。ただし、-v 'RS="\0"'はサポートされています。私はなぜ調査していません、これは私のバージョン(Debian wheezy)のバグのように見えます。

(すべて折り返す  値内の単一引用符をエスケープした後の、単一引用符"\047"でのnull区切りのレコード)

注意事項

これらのいずれかが読み取り専用変数を設定しようとする可能性があることに注意してください(シェルに読み取り専用変数がある場合)。

私はこれでくるくると行きました。 nullバイトの移植性に不満を感じていました。シェルでそれらを処理するための信頼できる方法がなかったのは、私にはうまくいかなかった。だから私は探し続けました。真実は私がこれを行うためのいくつかの方法を見つけたことですが、私の他の回答ではそのうちのいくつかしか述べられていません。しかし、結果は次のように機能する少なくとも2つのシェル関数でした。

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

最初に、\0区切りについて説明します。それは実際にはかなり簡単です。これが関数です:

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

基本的にodstdinを取り、そのstdoutに、受信した各バイトを1行あたり16進数で1バイトずつ書き込みます。

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

きっと\0nullはどちらでしょうか。 anysedを使用すると簡単に処理できます。 sedは、nullが発生するまで各行の最後の2文字を保存するだけで、その時点で中間の改行をprintfフレンドリーなフォーマットコードに置き換えて文字列を出力します。結果は、16進バイト文字列の\0null区切り配列です。見て:

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
.

上記をteeにパイプして、コマンドsusbstitutionの出力とprintfの処理結果の両方を確認できるようにしました。サブシェルも実際には引用されていませんが、printf\0null区切り文字でのみ分割されることに気付くでしょう。見て:

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
.

その展開についても引用符はありません。引用するかどうかは関係ありません。これは、sedが文字列を出力するたびに生成される\newlineを除いて、バイト値が分離されずに渡されるためです。単語分割は適用されません。そしてそれがこれを可能にするものです:

_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

上記の関数は_zedlmtを使用して、${pcat}にあるプロセスの環境ソーシング用に準備されたバイトコードのストリームを/procするか、直接.dot${psrc}現在のシェルと同じか、パラメータなしで、setまたはprintenvのように同じ処理済みの出力を端末に表示します。必要なのは$pid-any読み取り可能/proc/$pid/environファイルだけです。

次のように使用します。

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

しかし、hum​​an friendlysourcableの違いは何ですか?さて、違いがあるので、この答えはここの他のどの答えとも異なります-私の他のものも含めて。他のすべての答えは、すべてのEdgeケースを処理するために何らかの方法でシェルの引用に依存しています。それは単にうまくいかないだけです。信じてください-試してみました。見て:

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

[〜#〜] no [〜#〜]ファンキーな文字または含まれる引用の量は、コンテンツが供給される瞬間まで各値のバイトが評価されないため、これを壊す可能性があります。そして、それが少なくとも1回は値として機能することはすでにわかっています。これは、元の値のバイト単位のコピーであるため、解析や引用の保護は必要ありません。

この関数は、最初に$var名を評価し、.dotがヒアドキュメントをファイル記述子3に提供する前にチェックが完了するのを待ちます。ソースが提供される前は、次のようになります。フールプルーフです。 POSIXポータブル。まあ、少なくとも\ 0nullの処理はPOSIX移植可能です-/ processファイルシステムは明らかにLinux固有です。そしてそれが2つの機能がある理由です。

6
mikeserv

プロセスには、有効なBash/Sh/* sh変数ではない環境変数が含まれている可能性があることに注意してください。POSIXは、環境変数の名前が^[a-zA-Z0-9_][a-zA-Z0-9_]*$

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
}

同様に、それらをロードするには:

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
}

この問題はまれにしか発生しませんが、発生すると...

3
solidsnack

sourceおよびプロセス置換の使用:

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

まもなく:

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

evalおよびコマンド置換の使用:

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

sed呼び出しはawk呼び出しに置き換えることができます:

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

ただし、pid 1にない環境変数はクリアされないことを忘れないでください。

3
Pavel Šimerda

これはPOSIXポータブルだと思います:

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

しかし、@ Gillesは良い点を作ります-sedはおそらくnullを処理しますが、そうでないかもしれません。だからこれがあります(私は本当に今回はそう思う)実際にはPOSIXポータブルメソッドもあり:

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

それでも、GNU sedを取得している場合は、次のことを行うだけです。

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

  #BOTH METHODS OUTPUT:
_

enter image description here

さて、指定されていない_/dev/..._を除いてPOSIXポータブルですが、ほとんどのUnicesでその構文が同じように動作することが期待できます。

これが 他の質問 と関係がある場合は、次のように使用できます。

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

Here-docは、サブシェルで処理するのが難しいためにシェルが引用することを妨げないようにし、_.dot_ sourceableへの信頼できるパスを提供するという点で非常に役立ちます)fileではなく、サブシェルまたはシェル変数です。他の人は<(process substitution) bashismを使用しますが、これはほとんど同じ方法で機能します-間違いなく匿名の_|pipe_ですが、POSIXはhere-docsに対してiohereのみを指定しているため、どのような種類のファイルでも、実際には、通常はtempファイルです。 (_dash,_一方、ヒアドキュメントには匿名_|pipes_を使用します)。ただし、プロセス置換の不幸な点は、シェルに依存することでもあります。これは、initを使用している場合は特に厄介な問題になる可能性があります。

もちろん、これは_|pipes_でも機能しますが、最後に_|pipe's_状態がサブシェルで蒸発すると、環境が再び失われます。次に、これはうまくいきます:

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

sedステートメント自体は、最後の行に達するまでメモリ内のすべての行を保持することで機能します。最後に到達すると、引用を処理するグローバル置換を実行し、nullにアンカーすることで改行を挿入します。かなりシンプルです。

dashの写真を見ると、\混乱を避け、GNU固有の_-r_オプションをsedに追加したことがわかります。しかし、それは単にタイプするのが少なかったからです。 zshイメージで確認できるように、どちらの方法でも機能します。

zshは次のとおりです。

enter image description here

そして、ここでdashを変化させて同じことを行います:

enter image description here

ターミナルエスケープでさえ無傷でやって来る:

enter image description here

0
mikeserv