다른 프로세스의 환경 변수를 어떻게 소싱합니까?


24

검사 /proc/1/environ하면 null 바이트로 구분 된 프로세스 1환경 변수 문자열을 볼 수 있습니다 . 이 변수를 현재 환경으로 가져오고 싶습니다. 이 작업을 수행하는 쉬운 방법이 있습니까?

proc매뉴얼 페이지 나에게 라인별로 각각의 환경 변수를 출력 할 데 도움이되는 미리보기를 제공합니다 (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)따옴표가있는 변수도 올바르게 처리하는 것이 좋습니다 .
John Kugelman은 Monica

@JohnKugelman "$@"대신을 사용하여 개선해 주셔서 감사 '{}'합니다. 에 대해 궁금 사람들을 위해 --자신의 개선 대답 인수 : 위치 인수하기 bash -c command_string시작 할당 $0하면서, "$@"팽창에서 시작 인수를 포함하는 $1. 인수가 --에 할당됩니다 $0.
Mark Plotnick

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변수 정의 사이에 null 바이트를 사용하여 지정된 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)

(이 셸에서 빈 구분 기호가 전달되면 readnull 바이트가 구분 기호가됩니다. PWD가져 오기 PWD로 끝날 수있는 다른 변수를 방해하지 않기 위해 임시 변수 이름으로 사용 합니다. 기술적으로 가져올 수는 있지만 계속 가져올 때까지만 유지됩니다. 다음 cd.)

POSIX

POSIX 이식성은이 질문에 흥미롭지 않습니다. 왜냐하면이 시스템은 /proc/PID/environ. 그래서 문제는 솔라리스 sed가 지원하는 것, 또는 솔라리스의 지원 여부입니다. /proc/PID/environ하지만 사용하지 않았지만 요즘에는 솔라리스 기능에 뒤쳐져 있습니다. Linux에서 GNU 유틸리티와 BusyBox는 모두 널 안전하지만 경고가 있습니다.

POSIX 이식성을 고집한다면 POSIX 텍스트 유틸리티 중 어느 것도 널 바이트를 처리 할 필요가 없으므로 이것은 어렵다. 다음은 awk가 레코드 구분 기호로 null 바이트를 지원한다고 가정하는 솔루션입니다 (BuckyBox awk와 마찬가지로 nawk 및 gawk는 수행하지만 mawk는 그렇지 않습니다).

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

(일반적으로 내장형 리눅스 시스템에서 발견 된 버전)의 비지 박스 AWK 지원 NULL의 바이트이지만 설정하지 않는 RS"\0"(A)에 BEGIN없는 명령 행 구 위에 블록과; 그러나 그것은 지원 -v 'RS="\0"'합니다. 나는 왜 내 버전 (Debian wheezy)의 버그처럼 보이는지 조사하지 않았습니다.

( 작은 따옴표를 값 내부로 이스케이프 한 후 모든 행에서 널로 분리 된 레코드를 작은 따옴표로 묶으십시오 "\047".)

경고

이 중 하나라도 읽기 전용 변수를 설정하려고 시도 할 수 있습니다 (쉘에 읽기 전용 변수가있는 경우).


나는 마침내 이것으로 돌아왔다. 나는 내가 알고있는 모든 쉘 에서이 처리 또는 null 처리를 수행하는 절대적인 방법을 알아 냈습니다. 내 새로운 답변을 참조하십시오.
mikeserv

6

나는 이것으로 빙글 빙글 갔다. null 바이트의 이식성에 좌절했습니다. 쉘에서 처리 할 수있는 확실한 방법이 없다는 것은 나와 함께 잘 앉아 있지 않았습니다. 그래서 나는 계속 찾고 있었다. 진실은 내가 이것을 할 수있는 몇 가지 방법을 찾았다는 것인데, 그중 두 가지만이 내 대답에 나와 있습니다. 그러나 결과는 다음과 같이 작동하는 적어도 두 개의 쉘 함수였습니다.

_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각 바이트가 라인 당 진수 한 수신한다.

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? 이 취급이 용이하다는처럼 서면 어느 sed . sed중간 줄 바꿈을 printf친숙한 형식 코드로 바꾸고 문자열을 인쇄 하는 null이 발생할 때까지 각 줄의 마지막 두 문자를 저장 합니다. 결과는 \0null구분 된 16 진 바이트 문자열 배열입니다. 보기:

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
.

해당 확장에 대한 인용문도 없습니다-인용 여부는 중요하지 않습니다. 문자열을 인쇄 할 \n때마다 생성 된 하나의 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파일 설명자 3에 공급 된 here-doc을 소싱 하기 전에 검사가 완료 될 때까지 기다립니다 . 바보입니다. 그리고 POSIX 휴대용. 적어도 \ 0null 처리는 POSIX 이식 가능합니다. / process 파일 시스템은 분명히 Linux에 따라 다릅니다. 이것이 두 가지 기능이있는 이유입니다.


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의 답변에서 내보내기가 중복됩니까? 그렇지 않다면 아주 중요한 것 같습니다
Dane O'Connor

예, 수출이 필요합니다. 내가 고칠 게
Pavel Šimerda

3
이러한 모든 명령은 줄 바꿈이 포함 된 값과 명령에 따라 다른 문자를 포함합니다.
질 'SO-정지 존재 악마'

옳은. 어쨌든 참조를 위해 답변을 유지합니다.
Pavel Šimerda

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을합니다. 아마도 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:

여기에 이미지 설명을 입력하십시오

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)은 거의 같은 방식으로 작동 하는 bashism을 사용합니다 -POSIX 는 here-docs |pipe만 지정 iohere하므로 실제로는 파일입니다.하지만 실제로는 temp파일입니다. ( dash,반면, |pipeshere-docs 에는 익명 을 사용합니다 ) . 그러나 프로세스 대체에 대한 불행한 점은 쉘에 의존하기 때문에 작업하는 경우 특히 성가신 문제 일 수 있습니다 init.

이것은 물론 작동 |pipes하지만 |pipe's상태가 하위 셸과 함께 증발 하면 결국 환경을 다시 잃게됩니다 . 그런 다음 다시 작동합니다.

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

sed문 자체가 마지막에 도달 할 때까지 세계가 인용하고 삽입 줄 바꿈을 처리하는 대신 수행하는 시간에 메모리에 모든 라인을 유지하여 작동 여기서 널 (null)에 고정이 적절. 정말 간단합니다.

에서 dash사진 당신은 내가 \ 혼란을 피하고 추가하기로 볼 수 있습니다 GNU특정의 -r에 옵션을 sed. 그러나 그것은 타이핑하기가 덜한 원인입니다. zsh이미지 에서 볼 수 있듯이 어느 쪽이든 작동합니다 .

여기 있습니다 zsh:

여기에 이미지 설명을 입력하십시오

그리고 여기에도 dash같은 일이 있습니다.

여기에 이미지 설명을 입력하십시오

터미널 이스케이프조차도 흠없는 상태로 나옵니다.

여기에 이미지 설명을 입력하십시오


sed가 널 바이트를 처리하는 데 필요하지 않기 때문에 POSIX 이식 가능하지 않습니다. POSIX 이식성은이 시스템에 적용되는 시스템에만 적용되기 때문에이 질문에 흥미롭지 않다 /proc/PID/environ. 따라서 질문은 솔라리스 sed가 지원하는 것, 또는 솔라리스의 지원 여부에 따라 달라 /proc/PID/environ졌다. 요즘은 솔라리스 기능의 곡선 뒤에 숨어 있습니다.)
Gilles 'SO-Stop

@Gilles No. 그러나 sedASCII 16 진수를 처리해야하며, 그 중 널 바이트는 1입니다. 게다가, 나는 실제로이 작업을 훨씬 쉽게 수행 할 수 있는지 생각했습니다.
mikeserv

POSIX는 “입력 파일은 텍스트 파일이어야하며” (sed 및 기타 텍스트 유틸리티 용) 텍스트 파일 을“하나 이상의 행으로 구성된 문자를 포함하는 파일”로 정의합니다 . 줄에 NUL 문자 (…)가 포함되어 있지 않습니다.” 그리고 \xNNPOSIX에서는 \OOO8 진수 구문 조차 필요하지 않습니다 (C 문자열 및 awk, 예, sed regexp에는 해당되지 않음).
Gilles 'SO- 악마 그만해'

@Gilles 당신은 요점을 얻었다. 나는 온통 쳐다 보았고 내가 전에 생각했던 것을 찾을 수 없었다. 그래서 나는 다르게했습니다. 지금 수정 중입니다.
mikeserv

내가 알 수있는 /proc/PID/environ한 , 솔라리스에는 결국 이 없습니다 (에 리눅스와 같은 다른 항목이 /proc/PID있지만에는 없습니다 environ). 따라서 이식 가능한 솔루션은 결국 Linux 도구를 넘어 설 필요가 없습니다. 즉 GNU sed 또는 BusyBox sed를 의미합니다. 둘 다 \x00정규 표현식을 지원 하므로 코드는 POSIX가 아닌 필요에 따라 이식 가능합니다. 그래도 지나치게 복잡합니다.
질 'SO- 악마 그만'

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.