Làm thế nào để tôi tìm nguồn biến môi trường của một quá trình khác?


24

Nếu tôi kiểm tra /proc/1/environtôi có thể thấy một chuỗi các 1biến môi trường được phân tách bằng byte-byte . Tôi muốn mang những biến này vào môi trường hiện tại của tôi. Có cách nào làm dễ hơn không?

Các proctrang người đàn ông mang lại cho tôi một đoạn mà giúp được in ra mỗi biến môi trường trên cơ sở line-by-line (cat /proc/1/environ; echo) | tr '\000' '\n'. Điều này giúp tôi xác minh nội dung là chính xác, nhưng điều tôi thực sự cần làm là đưa các biến này vào phiên bash hiện tại của mình.

Làm thế nào để làm điều đó?

Câu trả lời:


23

Sau đây sẽ chuyển đổi từng biến môi trường thành một exportcâu lệnh, được trích dẫn chính xác để đọc vào trình bao (vì LS_COLORS, ví dụ, có khả năng có dấu chấm phẩy trong đó), sau đó nguồn nó.

[Các printftrong /usr/bin, không may, thường không hỗ trợ %q, vì vậy chúng tôi cần phải gọi một xây dựng trong bash.]

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

Tôi đề nghị . <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ), sẽ xử lý các biến với dấu ngoặc kép đúng.
John Kugelman hỗ trợ Monica

@JohnKugelman Cảm ơn rất nhiều vì sự cải tiến, sử dụng "$@"thay vì '{}'. Đối với những người thắc mắc về --đối số trong câu trả lời được cải thiện của mình: các đối số vị trí bash -c command_stringđược gán bắt đầu từ $0, trong khi "$@"mở rộng để bao gồm các đối số bắt đầu từ $1. Đối số --được gán cho $0.
Đánh dấu Plotnick

10

Trong bashbạn có thể làm như sau. Điều này sẽ làm việc cho tất cả các nội dung có thể có của các biến và tránh eval:

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

Điều này sẽ khai báo các biến đọc là biến shell trong shell đang chạy. Thay vào đó, để xuất các biến vào môi trường shell đang chạy:

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

10

Trong câu trả lời này, tôi giả sử một hệ thống /proc/$pid/environtrả về môi trường của quá trình với PID được chỉ định, với các byte rỗng giữa các định nghĩa biến. ( Vì vậy, Linux, Cygwin hoặc Solaris (?) ).

Zsh

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

(Khá đơn giản như zsh đi: một chuyển hướng đầu vào không có lệnh <FILE tương đương với cat FILE. Đầu ra của sự thay thế lệnh trải qua việc mở rộng tham số với các cờ ps:\000: có nghĩa là chia tách trên các byte null, và @có nghĩa là nếu toàn bộ điều đó nằm trong dấu ngoặc kép thì xử lý mỗi phần tử mảng như một trường riêng biệt (tổng quát hóa "$@").)

Bash, mksh

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

(Trong những vỏ, một delimiter trống truyền cho readkết quả trong rỗng byte là dải phân cách. Tôi sử dụng PWDnhư một tên biến tạm thời để tránh clobbering một biến mà có thể kết thúc được nhập khẩu. Trong khi bạn về mặt kỹ thuật có thể nhập khẩu PWDlà tốt, nó sẽ chỉ ở lại đó cho đến khi tiếp theo cd.)

POSIX

Tính di động POSIX không thú vị cho câu hỏi này, bởi vì nó chỉ áp dụng cho các hệ thống có /proc/PID/environ. Vì vậy, câu hỏi đặt ra là Solaris sed hỗ trợ gì - hoặc liệu Solaris có /proc/PID/environ, nó không được sử dụng nhưng tôi đang đi sau đường cong về các tính năng của Solaris để ngày nay có thể. Trên Linux, các tiện ích GNU và BusyBox đều không an toàn, nhưng có cảnh báo.

Nếu chúng tôi nhấn mạnh vào tính di động của POSIX, thì không có tiện ích văn bản POSIX nào được yêu cầu để xử lý byte rỗng, vì vậy điều này rất khó. Đây là một giải pháp giả định rằng awk hỗ trợ byte rỗng như dấu phân cách bản ghi (nawk và gawk làm, cũng như BusyBox awk, nhưng mawk thì không).

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

BusyBox awk (là phiên bản thường thấy trên các hệ thống nhúng Linux) không hỗ trợ byte rỗng nhưng không được đặt RSthành "\0"một BEGINkhối và không phải là cú pháp dòng lệnh ở trên; Tuy nhiên, nó hỗ trợ -v 'RS="\0"'. Tôi đã không điều tra lý do tại sao, điều này trông giống như một lỗi trong phiên bản của tôi (Debian wheezy).

(Bao bọc tất cả các bản ghi được phân tách bằng null trong các dấu ngoặc đơn "\047", sau khi thoát các dấu ngoặc đơn bên trong các giá trị.)

Hãy cẩn thận

Lưu ý rằng bất kỳ trong số này có thể cố gắng đặt các biến chỉ đọc (nếu shell của bạn có các biến chỉ đọc).


Cuối cùng tôi đã trở lại với điều này. Tôi đã tìm ra một phương tiện hoàn hảo để thực hiện điều này hoặc bất kỳ xử lý null nào trong tất cả các shell mà tôi biết về khá đơn giản. Xem câu trả lời mới của tôi.
mikeerv

6

Tôi đã đi vòng quanh với điều này. Tôi đã thất vọng với tính di động của byte null. Nó không phù hợp với tôi rằng không có cách nào đáng tin cậy để xử lý chúng trong vỏ. Vì vậy, tôi tiếp tục tìm kiếm. Sự thật là tôi đã tìm thấy một số cách để làm điều này, chỉ một vài trong số đó được ghi chú trong câu trả lời khác của tôi. Nhưng kết quả có ít nhất hai hàm shell hoạt động như thế này:

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

Đầu tiên tôi sẽ nói về việc \0phân định. Nó thực sự là khá dễ dàng để làm. Đây là chức năng:

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

Về cơ bản odlấy stdinvà ghi vào stdouttừng byte của nó, nó nhận được một thập lục phân trên mỗi dòng.

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

Tôi cá là bạn có thể đoán đó là cái gì \0null, phải không? Viết ra như thế thật dễ dàng để xử lý với bất kỳ sed . sedchỉ lưu hai ký tự cuối cùng trong mỗi dòng cho đến khi nó gặp null tại điểm mà nó thay thế các dòng mới trung gian bằng printfmã định dạng thân thiện và in chuỗi. Kết quả là một \0nullmảng phân cách của các chuỗi byte hex. Nhìn:

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
.

Tôi đã dẫn đường ở trên để teebạn có thể thấy cả đầu ra của lệnh susbstlation và kết quả printfxử lý. Tôi hy vọng bạn sẽ nhận thấy rằng phần con thực sự không được trích dẫn nhưng printfvẫn chỉ được phân tách tại \0nulldấu phân cách. Nhìn:

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
.

Không có trích dẫn nào về bản mở rộng đó - không quan trọng bạn có trích dẫn hay không. Điều này là do các giá trị khớp thông qua không \ntách rời ngoại trừ một ewline được tạo cho mỗi lần sedin một chuỗi. Chia tách từ không áp dụng. Và đó là những gì làm cho điều này có thể:

_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

Việc sử dụng chức năng ở trên _zedlmthoặc là ${pcat}một dòng suối chuẩn bị mã byte cho tìm nguồn cung ứng môi trường của bất kỳ quá trình có thể được tìm thấy trong /proc, hoặc trực tiếp .dot ${psrc}cùng trong vỏ hiện tại, hoặc không có tham số, để hiển thị một sản lượng chế biến của cùng với thiết bị đầu cuối như sethoặc printenvsẽ Tất cả bạn cần là một $pid- bất kỳ/proc/$pid/environ tập tin có thể đọc sẽ làm.

Bạn sử dụng nó như thế này:

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

Nhưng sự khác biệt giữa thân thiện với con ngườinguồn gốc là gì? Chà, sự khác biệt ở đây là điều làm cho câu trả lời này khác với mọi câu hỏi khác ở đây - bao gồm cả câu trả lời khác của tôi. Mỗi câu trả lời khác phụ thuộc vào trích dẫn shell theo cách này hay cách khác để xử lý tất cả các trường hợp cạnh. Nó chỉ đơn giản là không hoạt động tốt. Xin hãy tin tôi - Tôi đã TRIỆT. Nhìn:

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

KHÔNG có số lượng ký tự sôi nổi hoặc trích dẫn có chứa có thể phá vỡ điều này bởi vì các byte cho mỗi giá trị không được đánh giá cho đến khi ngay lập tức nội dung có nguồn gốc. Và chúng ta đã biết nó hoạt động như một giá trị ít nhất một lần - không cần phân tích cú pháp hoặc bảo vệ trích dẫn ở đây vì đây là bản sao từng byte của giá trị ban đầu.

Trước tiên, hàm sẽ đánh giá các $vartên và chờ kiểm tra hoàn tất trước khi .dottìm nguồn cung cấp tài liệu ở đây trên mô tả tệp 3. Trước khi nguồn đó trông giống như vậy. Đó là bằng chứng ngu ngốc. Và POSIX di động. Chà, ít nhất thì việc xử lý \ 0null là POSIX xách tay - hệ thống tập tin / process rõ ràng là đặc thù của Linux. Và đó là lý do tại sao có hai chức năng.


3

Sử dụng sourcexử lý thay thế :

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

Ngắn gọn:

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

Sử dụng evalthay thế lệnh :

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

Cuộc sedgọi có thể được thay thế bằng một awkcuộc gọi:

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

Nhưng đừng quên rằng nó không xóa bất kỳ biến môi trường nào không có trong pid 1.


Xuất khẩu có thừa trong câu trả lời của @ fr00tyl00p không? Nguyên nhân nếu không có vẻ khá quan trọng
Dane O'Connor

Có, xuất khẩu là cần thiết. Tôi sẽ sửa nó lên.
Pavel imerda

3
Tất cả các lệnh này làm nghẹt các giá trị có chứa dòng mới và (tùy thuộc vào lệnh) các ký tự khác.
Gilles 'SO- ngừng trở nên xấu xa'

Chính xác. Sẽ giữ câu trả lời để tham khảo nào.
Pavel imerda

3

Cần lưu ý rằng các quy trình có thể có các biến môi trường không phải là biến Bash / Sh / * sh hợp lệ - POSIX khuyến nghị nhưng không yêu cầu các biến môi trường có tên khớp ^[a-zA-Z0-9_][a-zA-Z0-9_]*$.

Để tạo danh sách các biến tương thích shell từ môi trường của quy trình khác, trong 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
}

Tương tự, để tải chúng:

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
}

Vấn đề này chỉ thỉnh thoảng xuất hiện nhưng khi nó ...


0

Tôi nghĩ rằng đây là POSIX di động:

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

Nhưng @Gilles làm cho một điểm tốt - sedcó thể sẽ xử lý null, nhưng có thể không. Vì vậy, có điều này (tôi thực sự nghĩ vậy lần này) thực sự là phương pháp di động POSIX:

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

Tuy nhiên, nếu bạn đã có GNU, sedbạn chỉ cần làm:

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

  #BOTH METHODS OUTPUT:

nhập mô tả hình ảnh ở đây

Chà, POSIX xách tay ngoại trừ /dev/...cái không được chỉ định nhưng bạn hoàn toàn có thể mong đợi cú pháp đó hoạt động giống nhau trên hầu hết các Thông báo.

Bây giờ nếu điều này có bất cứ điều gì liên quan đến câu hỏi khác của bạn , bạn có thể muốn sử dụng nó như thế này:

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

Ở đây-doc là cực kỳ hữu ích ở chỗ nó giữ cho vỏ từ Screwing với bất kỳ trích dẫn chúng tôi làm việc rất chăm chỉ để xử lý trong subshell và cũng cung cấp cho chúng ta một con đường đáng tin cậy để một .dotsourceable tập tin hơn là, một lần nữa, một subshell hoặc một vỏ biến. Những người khác ở đây sử dụng <(process substitution)bashism hoạt động theo cùng một cách - chỉ chắc chắn đó là ẩn danh |pipetrong khi POSIX chỉ chỉ định một ioheretài liệu ở đây và do đó, nó có thể là bất kỳ loại tệp nào, trong thực tế, đó thường là một temptệp. ( dash,mặt khác, không sử dụng ẩn danh |pipescho tài liệu ở đây) . Tuy nhiên, điều đáng tiếc về sự thay thế quá trình là nó cũng phụ thuộc vào vỏ - đây có thể là một vấn đề đặc biệt khó chịu nếu bạn làm việc cùng init.

Điều này cũng hoạt động với |pipestất nhiên, nhưng cuối cùng bạn lại mất môi trường khi |pipe'strạng thái bốc hơi với lớp vỏ phụ của nó. Sau đó, một lần nữa, điều này hoạt động:

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

Bản sedthân câu lệnh hoạt động bằng cách giữ mọi dòng trong bộ nhớ cho đến khi đến cuối cùng, tại thời điểm đó, nó thực hiện một trích dẫn xử lý thay thế toàn cầu và chèn các dòng mới khi thích hợp bằng cách thả neo vào null. Khá đơn giản thực sự.

Trong dashảnh, bạn sẽ thấy tôi đã chọn tránh es \ mess và thêm tùy chọn GNUcụ thể -rvào sed. Nhưng đó chỉ là nguyên nhân khiến nó ít gõ. Nó hoạt động theo cách nào đó, như bạn có thể thấy trong zshhình ảnh.

Đây là zsh:

nhập mô tả hình ảnh ở đây

Và đây là dashcách làm tương tự:

nhập mô tả hình ảnh ở đây

Ngay cả lối thoát thiết bị đầu cuối cũng đi qua vô sự:

nhập mô tả hình ảnh ở đây


Đây không phải là POSIX-Portable vì không bắt buộc phải xử lý byte rỗng. (Đó được cho biết, POSIX tính di động mà không phải là thú vị cho câu hỏi này, bởi vì nó chỉ áp dụng cho các hệ thống có /proc/PID/environVì vậy, câu hỏi là gì Solaris sed hỗ trợ -. Hay Solaris có /proc/PID/environ, nó đã không sử dụng đến nhưng tôi cách đằng sau đường cong trên các tính năng của Solaris vì vậy ngày nay nó có thể.)
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles Không. Nhưng sedđược yêu cầu xử lý thập lục phân ascii, trong đó byte null là một. Bên cạnh đó, tôi thực sự chỉ nghĩ nếu một cách dễ dàng hơn nhiều để làm điều này vẫn còn.
mikeerv

Không, POSIX nói rằng, Các tập tin đầu vào sẽ là các tập tin văn bản, (đối với sed và các tiện ích văn bản khác) và định nghĩa các tập tin văn bản là tập tin có chứa các ký tự được tổ chức thành một hoặc nhiều dòng. Các dòng không chứa các ký tự NUL (Nhận). Và bằng cách này, \xNNcú pháp không được yêu cầu trong POSIX, thậm chí không phải là \OOOcú pháp bát phân (trong chuỗi C và trong awk, có, nhưng không phải trong biểu thức chính quy sed).
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles bạn đã có một điểm. Tôi nhìn khắp nơi và tôi không thể tìm thấy những gì tôi nghĩ tôi có thể trước đây. Vì vậy, tôi đã làm nó khác nhau. Chỉnh sửa ngay.
mikeerv

Theo như tôi có thể nói, Solaris không có /proc/PID/environgì cả (nó có một số mục giống như Linux khác /proc/PID, nhưng không phải environ). Vì vậy, một giải pháp di động không cần phải vượt ra ngoài các công cụ Linux, có nghĩa là GNU sed hoặc BusyBox sed. Cả hai đều hỗ trợ \x00trong một biểu thức chính quy, vì vậy mã của bạn có thể di động khi cần (nhưng không phải là POSIX). Nó quá phức tạp mặc dù.
Gilles 'SO- ngừng trở nên xấu xa'

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.