Як створити джерело змінних оточуючих середовищ іншого процесу?


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), які також будуть обробляти змінні з цитатами в них належним чином.
Джон Кугельман підтримує Моніку

@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

10

У цій відповіді я припускаю систему, яка /proc/$pid/environповертає середовище процесу із вказаним PID, з нульовими байтами між визначеннями змінних. ( Отже, Linux, Cygwin або Solaris (?) ).

Зш

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 - чи Solaris має /proc/PID/environ, це не використовувало, але я відстаю від кривої функцій Solaris, так що це може бути і сьогодні. У Linux, утиліти GNU та BusyBox є безпечними для нуля, але мають попередження.

Якщо ми наполягаємо на портативності POSIX, жодна з текстових утиліт POSIX не обробляє нульові байти, тому це важко. Ось рішення, яке передбачає, що awk підтримує нульовий байт як розділювач запису (nawk та gawk do, як і 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ви могли побачити як вихід підстановки команди, так і результат 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
.

Немає жодних цитат щодо цього розширення - не має значення, цитуєте ви його чи ні. Це пояснюється тим, що значення прикусу надходять через нерозділене, за винятком однієї \newline, що створюється для кожного разу, 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джерело подає тут-док, який подає його на файл-дескриптор 3. Перед тим, як джерело, воно виглядає, як воно виглядає. Це нерозумно. І 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

Так, потрібен експорт. Я це виправлю.
Павло Шімерда

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
}

Ця проблема виникає лише зрідка, але коли вона виникає ...


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

Тут-doc надзвичайно корисний у тому, що він захищає оболонку від вивертання з будь-яким цитуванням, з яким ми так важко обробляємось у підкашті, а також надає нам надійний шлях до файлу, що.dot виходить із джерела, а не, знову ж таки, до підкашлю чи оболонки змінна. Інші тут використовують башизм, який працює приблизно так само - лише це, безумовно, анонім, тоді як POSIX вказує лише тут-docs, і таким чином він може бути будь-яким типом файлів, хоча, на практиці, це зазвичай файл. ( з іншого боку, використовує анонімний<(process substitution)|pipeioheretempdash,|pipes для докторантів тут) . Прикро, що стосується заміни процесу, це також залежність від оболонки - що може бути особливо прикрою проблемою, якщо ви працюєте з цим init.

Це також працює, |pipesзвичайно, але тоді ви знову втрачаєте середовище, зрештою, коли|pipe's держава випаровується зі своєю нижньою частиною. Потім знову це працює:

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

The sed оператор працює, зберігаючи кожен рядок в пам'яті до останнього, в цей час він виконує глобальну обробку заміни цитатами та вставляє нові рядки, де це доцільно, закріплюючи нулі. Досить просто насправді.

На dashмалюнку, який ви побачите, я вирішив уникнути \ mess та додав GNUпевний -rваріант доsed . Але це лише тому, що його було менше набирати. Це працює в будь-якому випадку, як ви бачите на zshзображенні.

Ось ось zsh :

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

А ось і ось dash робимо те саме:

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

Навіть термінальні втечі відбуваються через непошкоджені:

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


Це не портативно POSIX, оскільки sed не потрібен для обробки нульових байтів. (При цьому, портативність POSIX не є такою цікавою для цього питання, оскільки це стосується лише тих систем, які є /proc/PID/environ. Отже, питання полягає в тому, що підтримує Solaris sed - чи є у Solaris /proc/PID/environ, він не використовував, але я так за кривою на особливостях Соляріса, так що це може бути і в наш час.)
Жил "SO- перестань бути злим"

@ Gilles Ні. Але sedпотрібно обробляти ascii шістнадцятковими, з яких нульовий байт - один. Крім того, я насправді просто думав, якщо набагато простіший спосіб зробити це все-таки.
mikeserv

Ні, POSIX говорить, що "Вхідні файли мають бути текстовими файлами" (для sed та інших текстових утиліт) і визначає текстові файли як "файл, що містить символи, організовані в один або кілька рядків. Рядки не містять символів NUL (…) ”. І, до речі, \xNNсинтаксис не потрібен в POSIX, навіть не \OOOвісімковий синтаксис (в рядках C і в awk, так, але не в sed regexps).
Жил "ТАК - перестань бути злим"

@Gilles у вас є бал. Я передивився все, і не міг знайти того, що вважав, що міг би раніше. Тому я це робив інакше. Редагування зараз.
mikeserv

Наскільки я можу сказати, у Solaris /proc/PID/environврешті немає (у неї є кілька інших записів, подібних до Linux /proc/PID, але ні environ). Таким чином, портативне рішення не потрібно виходити за рамки інструментів Linux, тобто GNU sed або BusyBox sed. Обидва підтримують \x00у регулярному вираженні, тому ваш код є настільки портативним, скільки потрібно (але не POSIX). Хоча це занадто складно.
Жил "ТАК - перестань бути злим"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.