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


24

調べる/proc/1/environと、プロセス1の環境変数のヌルバイトで区切られた文字列を見ることができます。これらの変数を現在の環境に持ち込みたいです。これを行う簡単な方法はありますか?

procmanページは私に、ライン毎に各環境変数をプリントアウトすることができますスニペットを提供します(cat /proc/1/environ; echo) | tr '\000' '\n'。これにより、内容が正しいことを確認できますが、実際に必要なのは、これらの変数を現在のbashセッションにソースすることです。

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

回答:


23

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

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

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

. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)引用符を含む変数も適切に処理することをお勧めします。
ジョンクーゲルマンは、モニカをサポートします14

@JohnKugelmanの"$@"代わりにを使用して、改善してくれてありがとう'{}'--彼の改善された答えの引数について疑問に思う人のために:でbash -c command_string始まる位置の引数はで始まり、$0"$@"始まる引数を含むように展開されます$1。引数--はに割り当てられ$0ます。
マークPlotnick 14

10

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

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

Zsh

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

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

バッシュ、mksh

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

(これらのシェルでは、空の区切り文字が渡されると、ヌルバイトreadがセパレータになります。PWD一時変数名として使用して、インポートされる可能性のある別の変数が上書きされるのを防ぎます。技術的にインポートすることもできますがPWD、次cd。)

POSIX

POSIXの移植性は、この質問に対してはそれほど興味深いものではありません/proc/PID/environ。だから、問題はSolaris sedが何をサポートしているか/proc/PID/environ、あるいはSolarisが何を使用しているかということです。しかし、それは使用していませんでした。Linuxでは、GNUユーティリティとBusyBoxは両方ともnullセーフですが、注意が必要です。

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

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

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

(値内の単一引用符をエスケープした後、すべての行をヌルで区切られたレコードを単一引用符"\047"でラップします。)

注意事項

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


私はついにこれに戻ってきました。私は、これを行う簡単な方法、または私が知っているすべてのシェルでヌルを簡単に処理する方法を見つけました。私の新しい答えをご覧ください。
mikeserv

6

これでぐるぐる回りました。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'
}

基本的odstdinstdout受信した各バイトを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正しいか推測できると思いますよね?anyで 簡単に扱えるように書かれていますsedsednullが見つかるまで各行の最後の2文字を保存するだけで、その時点で中間改行をわかりprintfやすいフォーマットコードに置き換え、文字列を出力します。結果は、\0null16進バイト文字列の区切り配列です。見て:

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コマンドの置換の出力と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
.

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

_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- すべての読み込み可能な/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} 

しかし、人間に優しいもの調達可能なものの違いは何ですか?さて、ここでの違いは、この答えを他のすべてのものとは違うものにしていることです-私の他のものも含めて。他のすべての答えは、すべてのエッジケースを処理するために何らかの方法でシェルクォートに依存しています。それは単にうまく機能しません。私を信じてください- 私はトライしました。見て:

_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各値のバイトは、コンテンツが供給されて非常にインスタントまで評価されないので、ファンキーな文字の量や引用含んでいたが、これを破ることはできません。そして、それが少なくとも一度は値として機能したことは既に知っています。これは、元の値のバイト単位のコピーであるため、ここでは解析や引用の保護は必要ありません。

関数は最初に$var名前を評価し、チェックが完了するのを待って.dotから、file-descriptor 3 に渡されたhere-docをソースします。それは誰にでもできることです。そしてPOSIXポータブル。少なくとも、\ 0nullの処理はPOSIXポータブルです-/ processファイルシステムは明らかにLinux固有です。そして、それが2つの機能がある理由です。


3

使用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にない環境変数はクリアされないことを忘れないでください。


@ fr00tyl00pの回答でエクスポートは冗長ですか?そうでない場合、それはかなり重要だと思われる
デーンオコナー14

はい、エクスポートが必要です。修正します。
パベルシメルダ14

3
これらのコマンドはすべて、改行や(コマンドに応じて)他の文字を含む値を抑制します。
ジル 'SO-悪であるのをやめる' 14

正しい。とにかく参照用の答えを保持します。
パベルシメルダ14

3

プロセスは、有効な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
}

この問題はときどき発生しますが、発生した場合は...


0

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

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

しかし、@ Gillesは良い点を示します- sedおそらくヌルを処理しますが、そうでないかもしれません。したがって、実際には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:

ここに画像の説明を入力してください

さて、POSIXポータブル/dev/...は、指定されていないものを除きますが、ほとんどの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難しいクォートのいずれかでシェルがねじ込むのを防ぎ、サブシェルまたはシェルではなく、ソース可能なファイルへの信頼できるパスを提供するという点で非常に役立ちます変数。ここの他の人たち<(process substitution)はほとんど同じように機能するバシズムを使用しています-それは間違いなく匿名で|pipeあり、POSIX iohereはhere-docs のみを指定しているため、実際には通常はtempファイルです。dash,一方、|pipeshere-docsにはanonymous を使用します)。ただし、プロセスの置換に関する不幸な点は、シェルに依存していることですinit

|pipesもちろんこれも機能しますが、|pipe'sサブシェルで状態が蒸発すると、最終的に再び環境を失います。そして再び、これは動作します:

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

sed文自体は、それが世界的な引用の取り扱いとヌルに固定することによって、適切な改行を挿入する置き換えを行い、その時点で、それが最後に到達するまで、メモリ内のすべての行を保持することによって動作します。本当に簡単です。

dash絵私は、\混乱を避けるために選んだと追加表示されますGNU特定の-rためのオプションをsed。しかし、それは単に入力する回数が少なかったためです。zsh画像でわかるように、どちらの方法でも機能します。

ここにありzshます:

ここに画像の説明を入力してください

そして、ここでもdash同じことをやっています:

ここに画像の説明を入力してください

ターミナルエスケープでさえ無傷で通過します。

ここに画像の説明を入力してください


ヌルバイトを処理するためにsedは必要ないため、これはPOSIXに移植できません。(とはいえ、POSIXの移植性は、この質問ではそれほど興味深いものではありません。なぜなら、それ/proc/PID/environはSolarisのsedがサポートするもの、つまりSolarisがサポートしているかどうかという/proc/PID/environことです。 Solaris上の曲線の背後にある、それは、今日かもしれないのでます)。
ジル「SO-停止されて悪」

@Gillesいいえ。ただしsed、ASCIIバイトの16進数を処理するために必要です。そのうち、ヌルバイトは1です。その上、私は実際に、これを行うはるかに簡単な方法がまだあるかどうかを考えました。
mikeserv 14

いいえ、POSIXは「入力ファイルはテキストファイルでなければならない」(sedおよびその他のテキストユーティリティ用)と言い、テキストファイルを「1行以上に編成された文字を含むファイル」と定義しています。行にはNUL文字(…)」が含まれていません。ちなみに、\xNNPOSIXでは構文は必要ありません\OOO。8進構文でも(C文字列やawk では必要ですが、sed正規表現では必要ありません)。
ジル 'SO-悪であるのをやめる' 14

@Gillesあなたがポイントを持っています。私はあちこち見て、以前に考えていたものを見つけることができませんでした。だから私は違うやり方をしました。編集中。
mikeserv 14

私の知る限り、Solarisには何もありません/proc/PID/environ(にはLinuxに似たエントリがいくつかありますが/proc/PID、にはありませんenviron)。そのため、移植可能なソリューションは、結局Linuxツールを超える必要はありません。つまり、GNU sedまたはBusyBox sedです。どちらも\x00正規表現でサポートされているため、コードは必要に応じて移植可能です(POSIXではありません)。ただし、非常に複雑です。
ジル 'SO-悪であるのをやめる' 14

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.