Как мне получить переменные окружения другого процесса?

24

Если я исследую, /proc/1/environя вижу строку с разделенными нулем байтами 1переменных среды процесса . Я хотел бы перенести эти переменные в мою текущую среду. Есть простой способ сделать это?

Страница procman дает мне фрагмент, который помогает распечатывать каждую переменную среды построчно (cat /proc/1/environ; echo) | tr '\000' '\n'. Это помогает мне проверить правильность содержимого, но что мне действительно нужно, это добавить эти переменные в мою текущую сессию bash.

Как я могу это сделать?

Дейн О'Коннор
источник

Ответы:

23

Следующее преобразует каждую переменную окружения в exportоператор, правильно заключенный в кавычки для чтения в оболочку (поскольку LS_COLORS, например, в нем, вероятно, есть точки с запятой), а затем получает его источник.

[ printfВ /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), который будет правильно обрабатывать переменные с кавычками.
Джон Кугельман поддерживает Монику
@JohnKugelman Большое спасибо за улучшение, используя "$@"вместо '{}'. Для тех, кто интересуется --аргументом в его улучшенном ответе: позиционные аргументы bash -c command_stringприсваиваются начиная с $0, а "$@"расширяются и включают аргументы, начинающиеся с $1. Аргумент --присваивается $0.
Марк Плотник
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
Graeme
источник
10

В этом ответе я предполагаю систему, в которой /proc/$pid/environвозвращается среда процесса с указанным PID с нулевыми байтами между определениями переменных. ( Так что Linux, Cygwin или Solaris (?) ).

Zsh

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

(Довольно просто, как в zsh: перенаправление ввода без команды <FILE эквивалентно cat FILE. Вывод подстановки команды подвергается расширению параметра с флагами, ps:\000: означающими «разделить на нулевые байты» и @означающими «если все это в двойных кавычках, тогда обрабатывать каждый элемент массива как отдельное поле »(обобщение "$@").

Баш, мкш

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 не использовал, но я сильно отстаю от кривой возможностей Solaris, так что в настоящее время это возможно. В Linux утилиты GNU и BusyBox являются нулевыми, но с оговорками.

Если мы настаиваем на переносимости POSIX, ни одна из текстовых утилит POSIX не требуется для обработки нулевых байтов, так что это сложно. Вот решение, которое предполагает, что awk поддерживает нулевой байт в качестве разделителя записей (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) поддерживает нулевые байты, но не устанавливает RSзначение "\0"в BEGINблоке, а не синтаксис командной строки выше; Однако это поддерживает -v 'RS="\0"'. Я не исследовал почему, это похоже на ошибку в моей версии (Debian wheezy).

(Оберните все строки разделенными нулями записями в одинарные кавычки "\047", после экранирования одинарных кавычек внутри значений.)

Предостережения

Помните, что любой из них может попытаться установить переменные только для чтения (если ваша оболочка имеет переменные только для чтения).

Жиль "ТАК - перестань быть злым"
источник
Я наконец-то вернулся к этому. Я нашел надежные средства для выполнения этого или любой нулевой обработки во всех оболочках, о которых я знаю, довольно просто. Смотрите мой новый ответ.
mikeserv
6

Я ходил по кругу с этим. Я был разочарован переносимостью нулевых байтов. Мне не понравилось, что не было надежного способа справиться с ними в скорлупе. Так что я продолжал искать. Правда в том, что я нашел несколько способов сделать это, только несколько из которых отмечены в моем другом ответе. Но результатом были как минимум две функции оболочки, которые работают так:

_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дружественным кодом формата и печатает строку. Результатом является \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
.

Никаких кавычек на это расширение тоже - не важно, цитируете ли вы это или нет. Это связано с тем, что значения прикуса проходят неразделенными, за исключением того, что одна электронная \nлиния создается для каждого раза, когда 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")

Никакое количество символов в стиле фанк или содержащихся в кавычках не может сломать это, потому что байты для каждого значения не оцениваются до самого момента получения контента. И мы уже знаем, что оно работало как значение хотя бы один раз - здесь нет необходимости в синтаксическом анализе или защите кавычек, потому что это побайтная копия исходного значения.

Функция сначала оценивает $varимена и ожидает завершения проверок, прежде чем .dotисточник here-doc загрузит его в файл-дескриптор 3. Прежде чем получить его, он выглядит так. Это глупо. И POSIX портативный. Ну, по крайней мере, обработка \ 0null является переносимой POSIX - файловая система / process, очевидно, специфична для Linux. И вот почему есть две функции.

mikeserv
источник
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? Причина, если нет, это кажется довольно важным
Дейн О'Коннор
Да, экспорт нужен. Я исправлю это.
Павел Шимерда
3
Все эти команды подавляют значения, содержащие символы новой строки и (в зависимости от команды) другие символы.
Жиль "ТАК - перестать быть злым"
Правильный. Будет держать ответ для справки в любом случае.
Павел Шимерда
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
}

Эта проблема возникает только изредка, но когда это происходит ...

solidsnack
источник
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 portable, за исключением того, /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

Документ .dotHere-Doc чрезвычайно полезен тем, что не позволяет оболочке обойтись без какой-либо цитаты, которую мы так усердно обрабатываем в подоболочке, а также обеспечивает нам надежный путь к исходному файлу, а не, опять же, подоболочке или оболочке. переменная. Другие здесь используют <(process substitution)bashism, который работает во многом таким же образом - только он определенно является анонимным, |pipeтогда как POSIX определяет только iohereдля here-docs, и поэтому это может быть файл любого типа, хотя на практике это обычно tempфайл. ( dash,с другой стороны, |pipesдля анонимных документов здесь используется анонимность ) . К сожалению, при замене процесса он также зависит от оболочки, что может быть особенно неприятно, если вы работаете с ним init.

Это также работает, |pipesконечно, но затем вы снова теряете среду, когда |pipe'sсостояние испаряется с его подоболочкой. Опять же, это работает:

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

Сам sedоператор работает, удерживая каждую строку в памяти до тех пор, пока не достигнет последней, и тогда он выполняет глобальную обработку замены, заключая в кавычки и вставляя новые строки, где это уместно, путем привязки к нулям. Довольно просто на самом деле.

На dashкартинке вы увидите, что я решил отказаться от \ mess и добавил GNUконкретную -rопцию в sed. Но это только потому, что это было меньше, чтобы печатать. Это работает в любом случае, как вы можете видеть на zshкартинке.

Вот zsh:

введите описание изображения здесь

И здесь мы dashделаем то же самое:

введите описание изображения здесь

Даже терминальные выходы проходят невредимыми:

введите описание изображения здесь

mikeserv
источник
Это не является POSIX-переносимым, потому что sed не требуется для обработки нулевых байтов. (Как говорится, переносимость POSIX не столь интересна для этого вопроса, потому что она применима только к системам, которые имеют /proc/PID/environ. Таким образом, вопрос в том, что поддерживает Solaris sed - или, если есть /proc/PID/environ, Solaris , он не использовал, но я способ за кривой на особенностях Solaris, чтобы это могло быть в настоящее время.)
Жиль "ТАК - перестань быть злым"
@ Жиль Нет. Но sedтребуется для обработки шестнадцатеричной ASCII, из которых нулевой байт является одним. Кроме того, я на самом деле просто подумал, что гораздо проще сделать это до сих пор.
mikeserv
Нет, POSIX говорит, что «входные файлы должны быть текстовыми файлами» (для sed и других текстовых утилит) и определяет текстовые файлы как «файл, содержащий символы, организованные в одну или несколько строк. Строки не содержат символов NUL (…) ». И, кстати, \xNNсинтаксис не требуется в POSIX, даже в \OOOвосьмеричном синтаксисе (в строках C и в awk, да, но не в регулярных выражениях sed).
Жиль "ТАК - перестань быть злым"
@ Жиль, у тебя есть смысл. Я огляделся и не смог найти то, что думал раньше. Так что я сделал это по-другому. Редактирование сейчас.
mikeserv
Насколько я могу судить, в /proc/PID/environконце концов Solaris не имеет (в нем есть несколько других записей, подобных Linux /proc/PID, но нет environ). Таким образом, портативное решение не обязательно должно выходить за рамки инструментов Linux, то есть GNU sed или BusyBox sed. Оба поддерживают \x00регулярные выражения, поэтому ваш код настолько переносим, ​​насколько это необходимо (но не POSIX). Это слишком сложно, хотя.
Жиль "ТАК - перестань быть злым"
-2
eval \`(cat /proc/1/environ; echo) | tr '\000' '\n'\`
Мартин Драуцбург
источник
1
Это не работает, если любое значение содержит какой-либо специальный символ оболочки.
Жиль "ТАК - перестань быть злым"