Как печатать только определенные переменные (переменные оболочки и / или среды) в bash

57

Встроенная команда bash set, если она вызывается без аргументов, напечатает все переменные оболочки и среды, а также все определенные функции. Это делает вывод непригодным для людей и трудным для него grep.

Как заставить встроенную команду bash setпечатать только переменные, а не функции?

Существуют ли другие команды, которые печатают только переменные оболочки без функций?

Примечание: bash различает переменные оболочки и переменные окружения. см. здесь Различие между переменными среды и экспортированными переменными среды в bash

lesmana
источник

Ответы:

51

"Есть ли другие команды, которые печатают только переменные оболочки, без функций?"

В man bash, в разделе SHELL BUILTIN COMMANDS (в разделе set) говорится: «В режиме posix перечислены только переменные оболочки».

(set -o posix; set)

примечание: ()синтаксис порождает подоболочку, если вам не нравится разветвление, просто используйте более подробную версию

set -o posix; set; set +o posix
температура
источник
1
Безусловно, лучшее решение
Руслан
1
Есть множество обычно неинтересных переменных, начиная с _которых, от которых легко избавиться:(set -o posix ; set | grep -v ^_)
hyde
Это беспокоило меня слишком долго! Вычеркивание строк, начинающихся с _, недостаточно, если у вас есть многострочные значения, оно по-прежнему оставляет такт в значении. Поскольку set печатает строки по порядку, все строки _ будут в конце, поэтому вы можете просто обрезать результаты после первой строки _, используя:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Скотт
1
Просто примечание: скобки важны в (set -o posix; set). Без скобок поведение текущей оболочки Bash будет изменено в соответствии со стандартом Posix. (Не знаю, что это значит, но выглядит важно.) С скобками Bash остается неизменным.
Джон Ред
1
Побочные эффекты : Наборы POSIXLY_CORRECT=yи добавляет posixк$SHELLOPTS
Tom Hale
32

Вот некоторые обходные пути:

$ comm -3 <(declare | sort) <(declare -f | sort)

сломать:

  1. declare печатает каждую определенную переменную (экспортированную или нет) и функцию.
  2. declare -f печатает только функции.
  3. comm -3удалит все строки, общие для обоих. По сути это удалит функции, оставив только переменные.

Чтобы печатать только переменные, которые не экспортируются:

$ comm -3 <(comm -3 <(declare | sort) <(declare -f | sort)) <(env | sort)

Другой обходной путь:

$ declare -p

Это будет печатать только переменные, но с некоторыми уродливыми атрибутами.

declare -- BASH="/bin/bash"
declare -ir BASHPID=""
declare -A BASH_ALIASES='()'
declare -a BASH_ARGC='()'
...

Вы можете вырезать атрибуты, используя ... cut:

$ declare -p | cut -d " " -f 3

Недостатком является то, что значение IFS интерпретируется, а не отображается.

Для сравнения:

$ comm -3 <(declare | sort) <(declare -f | sort)
...
IFS=$' \t\n'
...
$ declare -p | cut -d " " -f 3
...
IFS="
"
...

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


Еще один обходной путь, использующий compgen:

$ compgen -v

Встроенный bash compgenпредназначался для использования в скриптах завершения. Для этого compgen -vперечислены все определенные переменные. Недостаток: в нем перечислены только имена переменных, а не значения.

Вот хак, чтобы также перечислить значения.

$ compgen -v | while read var; do printf "%s=%q\n" "$var" "${!var}"; done

Преимущество: это чисто решение Bash. Недостаток: некоторые значения перепутаны из-за интерпретации до конца printf. Также подоболочка из канала и / или цикла добавляет некоторые дополнительные переменные.

lesmana
источник
Разрушается ли ваш declare ...| cutканал, если для переменной нет атрибутов? Я предполагаю, что --используется в этом случае, но мне все еще непросто. sedможет быть безопаснее, то естьdeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
JMTD
+1 за compgen:)
RSFalcon7
13

typesetВстроенный странно имеет возможность показать только функции ( -f) , но не показывать только параметры. К счастью, typeset(или set) отображает все параметры до того, как все функции, имена функций и параметров не могут содержать символы новой строки или знаки равенства, а символы новой строки в значениях параметров заключаются в кавычки (они отображаются как \n). Таким образом, вы можете остановиться на первой строке, которая не содержит знака равенства:

set | awk -F '=' '! /^[0-9A-Z_a-z]+=/ {exit} {print $1}'

Это только печатает имена параметров; если вы хотите значения, измените print $1на print $0.

Обратите внимание, что при этом печатаются все имена параметров (здесь параметры и переменные используются как синонимы), а не только переменные окружения («переменная окружения» является синонимом «экспортируемых параметров»).

Также обратите внимание, что это предполагает, что нет переменной среды с именем, которое не соответствует ограничениям bash на имена параметров. Такие переменные не могут быть созданы внутри bash, но могут быть унаследованы от среды при запуске bash:

env 'foo ()
{
  =oops' bash
Жиль "ТАК - перестань быть злым"
источник
Отличное решение! Я даже не думал о том, чтобы просто остановиться на первой строке, у которой нет '='. +1
Стивен Д
Эквивалентно, с седом: set | sed '/=/!Q'
Тоби Спейт
7

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

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" 

В основном, я ищу строки, которые начинаются с букв, цифр или знаков препинания, за которыми следует «=». Из вывода, который я видел, он захватывает все переменные; Однако я сомневаюсь, что это очень портативно.

Если, как следует из вашего заголовка, вы хотите вычесть из этого списка экспортируемые переменные и, таким образом, получить список неэкспортированных переменных, то вы можете сделать что-то вроде этого

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars && env | sort | comm -23 ./setvars - 

Чтобы немного это изменить, вот что он делает:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars Надеемся, что он получает все переменные (как обсуждалось ранее), сортирует их и сохраняет результат в файле.
  2. && env | sort: После завершения предыдущей команды мы собираемся вызвать envи отсортировать ее вывод.
  3. | comm -23 ./setvars -Наконец, мы передаем отсортированный вывод envв commи используем -23опцию для вывода строк, уникальных для первого аргумента, в данном случае строк, уникальных для нашего вывода из set.

Когда вы закончите, вы можете очистить временный файл, который он создал с помощью команды rm ./setvars

Стивен Д
источник
Проблема с вашей командой не в переносимости (конечно, она специфична для bash, но на стороне grep проблем нет). Проблема в том, что вы получаете некоторые строки в определениях функций. Bash вставляет большую часть кода функции, но не весь, в частности, не текст внутри документов ( f () {|cat <<EOF|foo=bar|EOF|}где |представляет разрыв строки).
Жиль "ТАК - перестань быть злым"
6

Просто используйте команду env. Он не печатает функции.

unxnut
источник
1
Это происходит, если используется внутри скрипта.
DocSalvager
2

Попробуйте команду printenv:

$printenv
Эмилио Р.
источник
6
printenv и env печатают только экспортированные переменные (среды), а не неэкспортированные (оболочки) переменные.
Лри
@Lri Спасибо! Я задавался вопросом, как распечатать только экспортированные переменные.
Марк Стюарт
1

В bash это будет печатать только имена переменных:

compgen -v

Или, если значения также необходимы, используйте:

declare -p
Исаак
источник
Благодарю вас за ваши усилия. пожалуйста, обратите внимание, что я уже упоминал эту возможность в моем ответе unix.stackexchange.com/a/5691/1170 в любом случае имеет преимущество.
Лесмана
0

Дифференцирование на чистую оболочку, чтобы исключить также переменные из сценариев rc

## get mostly local vars
diff_env(){
    diff <(bash -cl 'set -o posix && set') \
        <(set -o posix && set && set +o posix) | \
        grep -E "^>|^\+" | \
        grep -Ev "^(>|\+|\+\+) ?(BASH|COLUMNS|LINES|HIST|PPID|SHLVL|PS(1|2)|SHELL|FUNC)" | \
        sed -r 's/^> ?|^\+ ?//'
}

версия с commбыла бы слишком запутанной

untore
источник
0

Принятый ответ великолепен. Я предлагаю альтернативу, которая не требует настройки режима POSIX:

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

set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo $a; done 

set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo ${a/=*}; done 

Обе работают, сбрасывая строки, пока не найдут строку без символа '=', что происходит, когда в списке указана первая функция. Последний обрезает назначение.

Wil
источник