私が調べれば/proc/1/environ
nullバイトで区切られたプロセスの文字列が表示されます1
の環境変数。これらの変数を現在の環境に取り入れたいのですが。これを行う簡単な方法はありますか?
proc
manページには、各環境変数を行ごとに出力するのに役立つスニペットが用意されています(cat /proc/1/environ; echo) | tr '\000' '\n'
。これは、内容が正しいことを確認するのに役立ちますが、実際に必要なことは、これらの変数を現在のbashセッションに取り込むことです。
それ、どうやったら出来るの?
以下は、各環境変数をexport
ステートメントに変換し、シェルへの読み取り用に適切に引用されます(たとえば、LS_COLORS
にはセミコロンが含まれている可能性があるため)。
[残念ながら、/usr/bin
のprintf
は、通常%q
をサポートしていないため、bash
に組み込まれているものを呼び出す必要があります。]
. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
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
この回答では、/proc/$pid/environ
が指定されたPIDを持つプロセスの環境を返し、変数定義の間にnullバイトがあるシステムを想定しています。 ( したがって、Linux、CygwinまたはSolaris(?) )。
export "${(@ps:\000:)$(</proc/$pid/environ)}"
(zshのようにかなり単純: コマンドなしの入力リダイレクト<FILE
はcat FILE
と同等です。コマンド置換の出力は flagsps:\000:
は「nullバイトで分割」を意味し、@
は「全体が二重引用符で囲まれている場合、各配列要素を個別のフィールドとして扱う」("$@"
を一般化する) 。)
while IFS= read -r -d "" PWD; do export "$PWD"; done </proc/$pid/environ
PWD=$(pwd)
(これらのシェルでは、空の区切り文字をread
に渡した結果、nullバイトがセパレーターになりました。一時的な変数名としてPWD
を使用して、インポートされる可能性のある別の変数を壊さないようにします。技術的にはPWD
もインポートできますが、次のcd
までしか配置されません。)
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'
}
基本的にod
はstdin
を取り、その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
が文字列を出力するたびに生成される\n
ewlineを除いて、バイト値が分離されずに渡されるためです。単語分割は適用されません。そしてそれがこれを可能にするものです:
_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}
しかし、human friendlyとsourcableの違いは何ですか?さて、違いがあるので、この答えはここの他のどの答えとも異なります-私の他のものも含めて。他のすべての答えは、すべての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つの機能がある理由です。
プロセスには、有効な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
}
この問題はまれにしか発生しませんが、発生すると...
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にない環境変数はクリアされないことを忘れないでください。
これは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:
_
さて、指定されていない_/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
は次のとおりです。
そして、ここでdash
を変化させて同じことを行います:
ターミナルエスケープでさえ無傷でやって来る: