Как перечислить переменные, объявленные в скрипте в bash?


98

В моем скрипте на bash много переменных, и мне нужно что-то сделать, чтобы сохранить их в файл. Мой вопрос в том, как перечислить все переменные, объявленные в моем скрипте, и получить такой список:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

Ответы:


151

set выводит переменные, но, к сожалению, также выводит определения функций.

К счастью, режим POSIX выводит только переменные:

( set -o posix ; set ) | less

Подключение по конвейеру lessили перенаправление туда, где вам нужны параметры.

Итак, чтобы получить переменные, объявленные только в скрипте:

( set -o posix ; set ) >/tmp/variables.before
source script
( set -o posix ; set ) >/tmp/variables.after
diff /tmp/variables.before /tmp/variables.after
rm /tmp/variables.before /tmp/variables.after

(Или хотя бы что-то на этом основании :-))


Вы редактировали, пока я писал. Хороший вызов с -o posixnow a diff будет содержать только переменные.
ezpz

8
Без использования временных файлов: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Это также выведет вары в формате, готовом к сохранению. В список будут включены переменные, которые скрипт изменил (
смотря,

1
@ErikAronesty Если вы хотите воссоздать среду с помощью source, вы должны иметь возможность сделать это с помощью вывода declare -p.
Шесть,

2
Если вы хотите сравнить среду до и после с помощью команды diff (или любой другой подобной команды), не прибегая к использованию временных файлов, вы можете сделать это следующим образом:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Шесть

1
@Caesar Нет, я пробовал только непустые массивы. В случае пустых массивов вы правы - они не печатаются.
Socowi

41
compgen -v

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


2
К сожалению, compgen -vперечислены также глобальные переменные, которые не были установлены локально. Не уверен, что это давняя ошибка или желаемое поведение.
Michał Górny

11
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

Это должно напечатать все имена переменных оболочки. Вы можете получить список до и после получения вашего файла, как и с помощью «set», чтобы определить, какие переменные являются новыми (как описано в других ответах). Но имейте в виду, что такая фильтрация с помощью diff может отфильтровать некоторые переменные, которые вам нужны, но присутствовали до источника вашего файла.

В вашем случае, если вы знаете, что имена ваших переменных начинаются с «VARIABLE», вы можете создать свой скрипт и сделать:

for var in ${!VARIABLE@}; do
   printf "%s%q\n" "$var=" "${!var}"
done

ОБНОВЛЕНИЕ: для чистого решения BASH (внешние команды не используются):

for i in _ {a..z} {A..Z}; do
   for var in `eval echo "\\${!$i@}"`; do
      echo $var
      # you can test if $var matches some criteria and put it in the file or ignore
   done 
done

1
+1 и единственная версия (с одним eval):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj

4

Основываясь на некоторых из приведенных выше ответов, это сработало для меня:

before=$(set -o posix; set | sort);

исходный файл :

comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq) 

3

Если вы можете setвыполнить пост-обработку (как уже упоминалось), вы можете просто разместить вызов в начале и в конце вашего скрипта (каждый для другого файла) и провести различие между двумя файлами. Поймите, что это все еще будет содержать некоторый шум.

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

store() {
    export ${1}="${*:2}"
    [[ ${STORED} =~ "(^| )${1}($| )" ]] || STORED="${STORED} ${1}"
}

store VAR1 abc
store VAR2 bcd
store VAR3 cde

for i in ${STORED}; do
    echo "${i}=${!i}"
done

Что дает

VAR1=abc
VAR2=bcd
VAR3=cde

2

Вот что-то похожее на ответ @GinkgoFr, но без проблем, выявленных @Tino или @DejayClayton, и более надежное, чем умный set -o posixбит @DouglasLeeder :

+ function SOLUTION() { (set +o posix; set) | sed -ne '/^\w\+=/!q; p;'; }

Разница в том, что это решение ОСТАНАВЛИВАЕТСЯ после первого неизменяемого отчета, например, первой функции, о которой сообщает set

Кстати: проблема "Тино" решена. Несмотря на то, что POSIX отключен, а функции передаются через set, sed ...часть решения разрешает только отчеты с переменными (например, VAR=VALUEстроки). В частности, A2он не попадает в выходные данные ложным образом.

+ function a() { echo $'\nA2=B'; }; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A9=999

И: Проблема "DejayClayton" решена (встроенные символы новой строки в значениях переменных не нарушают вывод - каждый VAR=VALUEвыводит одну строку):

+ A1=$'111\nA2=222'; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A1=$'111\nA2=222'
A9=999

ПРИМЕЧАНИЕ. Решение, предоставленное @DouglasLeeder, страдает от проблемы «DejayClayton» (значения со встроенными символами новой строки). Ниже показано A1неправильное изображение, которое A2вообще не должно отображаться.

$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999

НАКОНЕЦ: Я не думаю, что версия имеет bashзначение, но может. Я провел тестирование / разработку на этом:

$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)

POST-SCRIPT: Учитывая некоторые другие ответы на OP, я на <100% уверен, что set всегда преобразует символы новой строки в пределах значения \n, на которое полагается это решение, чтобы избежать проблемы "DejayClayton". Может, это современное поведение? Или вариация времени компиляции? Или параметр set -oили shoptпараметр? Если вам известны такие варианты, оставьте комментарий ...


0

Попробуйте использовать скрипт (назовем его "ls_vars"):

  #!/bin/bash
  set -a
  env > /tmp/a
  source $1
  env > /tmp/b
  diff /tmp/{a,b} | sed -ne 's/^> //p'

chmod + x it и:

  ls_vars your-script.sh > vars.files.save

0

С точки зрения безопасности, либо @ akostadinov - х ответ или @ JuvenXu - х ответ предпочтительнее полагаться на неструктурированной выходе setкоманды из - за следующего потенциального недостатка безопасности:

#!/bin/bash

function doLogic()
{
    local COMMAND="${1}"
    if ( set -o posix; set | grep -q '^PS1=' )
    then
        echo 'Script is interactive'
    else
        echo 'Script is NOT interactive'
    fi
}

doLogic 'hello'   # Script is NOT interactive
doLogic $'\nPS1=' # Script is interactive

Вышеупомянутая функция doLogicиспользуется setдля проверки наличия переменной, PS1чтобы определить, является ли сценарий интерактивным или нет (неважно, если это лучший способ достичь этой цели; это всего лишь пример).

Однако вывод setнеструктурирован, а это означает, что любая переменная, содержащая новую строку, может полностью испортить результаты.

Это, конечно, потенциальная угроза безопасности. Вместо этого используйте либо поддержку Bash для косвенного раскрытия имени переменной, либо compgen -v.


0

Попробуйте это: set | egrep "^\w+="| lessтрубопроводом или без него )

Первое предложенное решение ( set -o posix ; set ) | lessработает, но имеет недостаток: оно передает управляющие коды на терминал, поэтому они не отображаются должным образом. Так, например, если есть (вероятно)IFS=$' \t\n' переменное, мы можем видеть:

IFS='
'

…вместо.

Мое egrepрешение отображает это (и, в конечном итоге, другие похожие) правильно.


это было 8 лет, вы знаете
lauriys

Проголосовали против, потому что это просто неправильное bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A отображение A=B"-> Ошибка!
Тино

Странный тест, который, кажется, подразумевает только псевдопеременную "_" (последний аргумент предыдущей команды), но не терпит неудачу на других, особенно IFS. Запуск под "bash -c" также может сделать его несущественным. Вам следовало по крайней мере оставаться нейтральным, а не голосовать против.
GingkoFr

0

Я, наверное, украл ответ какое-то время назад ... во всяком случае, немного отличается от функции:

    ##
    # usage source bin/nps-bash-util-funcs
    # doEchoVars
    doEchoVars(){

        # if the tmp dir does not exist
        test -z ${tmp_dir} && \
        export tmp_dir="$(cd "$(dirname $0)/../../.."; pwd)""/dat/log/.tmp.$$" && \
        mkdir -p "$tmp_dir" && \
        ( set -o posix ; set )| sort >"$tmp_dir/.vars.before"


        ( set -o posix ; set ) | sort >"$tmp_dir/.vars.after"
        cmd="$(comm -3 $tmp_dir/.vars.before $tmp_dir/.vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
        echo -e "$cmd"
    } 

0

Команда printenv:

printenvпечатает environment variablesвместе со своими значениями.

Удачи...


0

Простой способ сделать это - использовать строгий режим bash , установив системные переменные среды перед запуском вашего скрипта, и использовать diff для сортировки только тех из вашего скрипта:

# Add this line at the top of your script :
set > /tmp/old_vars.log

# Add this line at the end of your script :
set > /tmp/new_vars.log

# Alternatively you can remove unwanted variables with grep (e.g., passwords) :
set | grep -v "PASSWORD1=\|PASSWORD2=\|PASSWORD3=" > /tmp/new_vars.log

# Now you can compare to sort variables of your script :
diff /tmp/old_vars.log /tmp/new_vars.log | grep "^>" > /tmp/script_vars.log

Теперь вы можете получить переменные вашего скрипта в /tmp/script_vars.log. Или хотя бы что-то на этом основании!


0

Немного поздно на вечеринку, но вот еще одно предложение:

#!/bin/bash

set_before=$( set -o posix; set | sed -e '/^_=*/d' )

# create/set some variables
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c

set_after=$( set -o posix; unset set_before; set | sed -e '/^_=/d' )
diff  <(echo "$set_before") <(echo "$set_after") | sed -e 's/^> //' -e '/^[[:digit:]].*/d'

Diff + СЭД командной строка трубопроводов выводит весь сценарий определенных переменные в нужном формате (как указано в посте ОП в):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.