Відповіді:
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
(Або принаймні щось на основі цього :-))
VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;
. Це також видасть вари у готовому для збереження форматі. Список буде містити змінні, які змінили сценарій (залежить, чи бажано це)
source
, ви повинні зробити це з результатом declare -p
.
before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
compgen -v
У ньому перелічені всі змінні, включаючи локальні. Я дізнався це зі списку змінних, ім'я якого відповідає певній схемі , і використав його у своєму сценарії .
compgen -v
перелічені також глобальні змінні, які не були локальними. Не впевнений, чи це давня помилка чи бажана поведінка.
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
eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
Якщо ви можете післяобробити процес (як уже згадувалося), ви можете просто 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
Ось щось подібне до відповіді @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
установка опції? Якщо ви знаєте про такі варіанти, будь ласка, додайте коментар ...
З точки зору безпеки, або @ 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
.
Спробуйте це: set | egrep "^\w+="
(з | less
трубопроводами або без них )
Перше запропоноване рішення, ( set -o posix ; set ) | less
працює, але має недолік: воно передає керуючі коди терміналу, тому вони не відображаються належним чином. Так, наприклад, якщо є (ймовірно)IFS=$' \t\n'
змінне, ми можемо бачити:
IFS='
'
… Замість цього.
Моє egrep
рішення відображає це (і, зрештою, інші подібні) належним чином.
bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A
дисплеї A=B"
-> Fail!
Я, мабуть, вкрав відповідь в той час як раніше ... все одно дещо інакше, як функція:
##
# 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"
}
Простий спосіб зробити це - використовувати строгий режим 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. Або хоча б щось на цьому базується!
Трохи запізнюємось на вечірку, але ось ще одна пропозиція:
#!/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
-o posix
теперішнім diff має містити лише змінні.