сохранить и восстановить переменные оболочки - PullRequest
6 голосов
/ 11 июля 2010

У меня есть два сценария оболочки, которые я хотел бы вызвать из программы на Си.Я хотел бы, чтобы переменные оболочки, установленные в первом скрипте, были видны во втором.Вот как это выглядело бы:

a.sh:

var=blah
<save vars>

b.sh:

<restore vars>
echo $var

Лучшее, что я до сих пор придумал, этовариант в «set> / tmp / vars» для сохранения переменных и «eval $ (cat / tmp / vars)» для их восстановления.«Eval» задыхается, когда пытается восстановить переменную, доступную только для чтения, поэтому мне нужно отключить их.Список этих переменных доступен через "Declare-R".Но есть некоторые переменные, которые не отображаются в этом списке, но все еще не могут быть установлены в eval, например, BASH_ARGC.Так что мне тоже нужно их отключить.

На данный момент мое решение кажется очень хрупким и подверженным ошибкам, и я не уверен, насколько оно переносимо.Есть ли лучший способ сделать это?

Ответы [ 5 ]

5 голосов
/ 11 июля 2010

Один из способов избежать установки проблемных переменных - хранить только те, которые изменились во время выполнения каждого скрипта. Например,

a.sh:

set > /tmp/pre
foo=bar
set > /tmp/post
grep -v -F -f/tmp/pre /tmp/post > /tmp/vars

b.sh:

eval $(cat /tmp/vars)
echo $foo

/ tmp / vars содержит это:

PIPESTATUS=([0]="0")
_=
foo=bar

Очевидно, что первые две строки не оказывают отрицательного влияния.

2 голосов
/ 11 июля 2010

Если вы можете использовать общий префикс в именах переменных, вот один из способов сделать это:

# save the variables
yourprefix_width=1200
yourprefix_height=2150
yourprefix_length=1975
yourprefix_material=gravel
yourprefix_customer_array=("Acme Plumbing" "123 Main" "Anytown")
declare -p $(echo ${!yourprefix@}) > varfile

# load the variables
while read -r line
do
    if [[ $line == declare\ * ]]
    then
        eval "$line"
    fi
done < varfile

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

Преимущество использования declare заключается в том, что оно более безопасно, чем простое использование eval.

При необходимости вы можете отфильтровать переменные, помеченные как доступные только для чтения, или выбрать переменные, помеченные для экспорта.

Другие интересные команды (некоторые могут отличаться в зависимости от версии Bash):

  • export - без аргументов, перечисляет все экспортируемые переменные в формате declare
  • declare -px - аналогично предыдущей команде
  • declare -pr - список переменных только для чтения
0 голосов
/ 05 мая 2012

Я искал что-то похожее и тоже не мог его найти, поэтому я сделал два сценария ниже.Для начала просто скажите shellstate, а затем, по крайней мере, set -i и set -o emacs, что reset_shellstate для вас не подходит.Я не знаю, как спросить bash, какие переменные он считает особенными.

~/bin/reset_shellstate:

#!/bin/bash
__="$PWD/shellstate_${1#_}"
trap    '
    declare -p      >"'"$__"'"
    trap            >>"'"$__"'"
    echo cd \""$PWD"\"      >>"'"$__"'"     # setting PWD did this already, but...
    echo set +abefhikmnptuvxBCEHPT  >>"'"$__"'"
    echo set -$-    >>"'"$__"'"     # must be last before sed, see $s/s//2 below
    sed -ri '\''
            $s/s//2
            s,^trap --,trap,
            /^declare -[^ ]*r/d
            /^declare -[^ ]* [A-Za-z0-9_]*[^A-Za-z0-9_=]/d
            /^declare -[^ ]* [^= ]*_SESSION_/d
            /^declare -[^ ]* BASH[=_]/d
            /^declare -[^ ]* (DISPLAY|GROUPS|SHLVL|XAUTHORITY)=/d
            /^declare -[^ ]* WINDOW(ID|PATH)=/d
            '\''    "'"$__"'"
    shopt -op       >>"'"$__"'"
    shopt -p        >>"'"$__"'"
    declare -f      >>"'"$__"'"
    echo "Shell state saved in '"$__"'"
    ' 0
unset __

~/bin/shellstate:

#!/bin/bash
shellstate=shellstate_${1#_}
test -s $shellstate || reset_shellstate $1
shift
bash --noprofile --init-file shellstate_${1#_} -is "$@"
exit $?
0 голосов
/ 14 июля 2010

В качестве альтернативы сохранению и восстановлению состояния оболочки можно заставить программу C и программу оболочки работать параллельно: программа C запускает программу оболочки, которая запускает a.sh, затем уведомляет программу C (возможно, передавая некоторую информацию оно извлекается из выполнения a.sh), и когда программа на C готова к большему, она сообщает программе оболочки, что она запускает b.sh. Программа оболочки будет выглядеть так:

. a.sh
echo "information gleaned from a"
arguments_for_b=$(read -r)
. b.sh

И общая структура программы на С будет такой:

  • установить две пары труб, одну для C-> shell и одну для shell-> C
  • вилка, exec оболочка
  • чтение информации, полученной из a на оболочке-> C pipe
  • больше обработки
  • записать аргументы для b в C-> shell pipe
  • дождаться окончания дочернего процесса
0 голосов
/ 11 июля 2010

Если a.sh может вызвать b.sh, он перенесется, если они экспортированы.Или если родитель установит все необходимые значения, а затем вызовет оба.Это самый безопасный и надежный метод, который я могу придумать.

Не уверен, что это принятая догма, но:

bash -c 'export foo=bar; env > xxxx'
env `cat xxxx` otherscript.sh

В другом сценарии env будет напечатан в xxxx ...

Обновление:

Также обратите внимание:

man execle

О том, как установить переменные окружения для другого системного вызова изнутри C, если вам нужно это сделать.А:

man getenv

и http://www.crasseux.com/books/ctutorial/Environment-variables.html

...