Як перерахувати змінні, оголошені в скрипті в 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 posixтеперішнім diff має містити лише змінні.
ezpz

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

1
@ErikAronesty Якщо ви хочете відтворити середовище source, ви повинні зробити це з результатом declare -p.
Шість

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

1
@Caesar Ні, я пробував лише непусті масиви. Ви маєте рацію у випадку порожніх масивів - вони не надруковані.
Socowi

40
compgen -v

У ньому перелічені всі змінні, включаючи локальні. Я дізнався це зі списку змінних, ім'я якого відповідає певній схемі , і використав його у своєму сценарії .


2
На жаль, compgen -vперелічені також глобальні змінні, які не були локальними. Не впевнений, чи це давня помилка чи бажана поведінка.
Michał Górny

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

Це повинно надрукувати всі назви змінних оболонок. Ви можете отримати список до і після пошуку свого файлу так само, як і "встановити", щоб відрізняти, які змінні є новими (як пояснено в інших відповідях). Але майте на увазі, що таке фільтрування з 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 та версії oneliner (з одним 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

BTW: Проблема "Тіно" вирішена. Навіть незважаючи на те, що 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: Враховуючи деякі інші відповіді на ОП, я залишаюсь <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"-> Fail!
Тіно

Дивний тест, який, мабуть, передбачає лише псевдо-змінну "_" (останній аргумент попередньої команди), але не підводить інших, особливо 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.