Установить переменные окружения в bash (или других)

9

Я хочу, чтобы мой скрипт прочитал файл, содержащий пары ключ / значение переменных среды, чтобы установить, а затем установить их.

Пока у меня есть это:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
    export $key="$val"
done

И я хочу прочитать файл так:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

Мне нужны только эти переменные в области действия в течение всего сценария, поэтому мне не нужно решать проблему установки переменной в сценарии для родительской области действия .

Из моего тестирования я думаю, что моя проблема связана с export $key="$val", поэтому я думаю, что мне просто нужна замена для этой линии.

palswim
источник
немного не по теме, но так как я застрял при попытке имитировать ваш сценарий, я делюсь ... когда беру ключ ... использование key=${kv%%=*}лучше, чем key=${kv%=*}потому, что %% и ## отличаются от% и # для того, является ли удаленная часть самая короткая или самая длинная съемная часть
pulkitsinghal

Ответы:

10

export $key=$valдолжно работать очень хорошо в bash. Я подозреваю, что ваша проблема - труба ( |). Все команды в конвейере выполняются в подоболочке. В вашем примере, экземпляр того, bashчто выполняется ваш сценарий будет разветвить 2 подоболочки: один для cat $1и другой для while read .... Переменные назначаются и экспортируются в подоболочке, выполняющей цикл while, а затем все они быстро выбрасываются при выходе из подоболочки. Один из способов исправить это - вообще не создавать подоболочки. Вместо бесполезного использования cat , попробуйте вместо этого перенаправление:

while read ...; do
   ...
done < "$1"

BashFAQ 24 объясняет это гораздо более подробно.

Альтернативный подход состоит в том, чтобы просто получить исходный файл с добавлением экспорта:

. <(sed '/^export/!s/^/export /' "$1")

Это <( )процесс замещения.

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

jw013
источник
Я использовал перенаправление ранее; что-то еще, должно быть, не работало, когда я изменил сценарий для использования cat. Но скрипт с перенаправлением теперь работает, спасибо!
Palswim
Бесполезное использование кошки считается вредным!
Скотт
2

( jw013 уже дал правильный ответ , но я хочу указать еще несколько вопросов.)

Правая часть конвейера работает в собственной оболочке в bash (как и большинство других оболочек, за исключением ATT ksh и zsh). Таким образом, вы устанавливаете переменные среды в подоболочке, которая выполняет цикл, но не в основном процессе оболочки.

Здесь есть простое решение, которое должно заменить бесполезное использование catперенаправления:

while read …; do …; done <"$1"

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

grep '^foo_' "$1" | {
  while read …; do …; done
  do_something
}

Обратите внимание, что, как правило, while read line; do …не совсем перебирать строки ввода. См. Почему while IFS= readиспользуется так часто, а не IFS=; while read..? для объяснения. Правильная идиома для перебора входных строк

while IFS= read -r line; do 

Здесь удаление начальных и конечных пробелов не имеет значения, так как не должно быть никаких заданных вами входных данных, но вам может потребоваться -rизбегать расширения с обратной косой чертой. Возможно, while read lineэто действительно правильный способ чтения ваших входных данных (но я подозреваю, что только если значения переменных не содержат символов новой строки). Также возможно, что ваш формат ввода является неоднозначным или более сложным для анализа. Проверьте, что произойдет, если значение одной из переменных содержит символ новой строки, двойную кавычку или обратную косую черту.

Одна точка, в которой вы определенно искажаете ввод, - это когда вы извлекаете значение:

val=`echo ${kv#*=} | sed 's/^"\|"$//g'`

Во- первых, вы должны использовать двойные кавычки подстановки переменных: echo "${kv#*=}"; в противном случае оболочка выполняет разбиение по словам и генерацию имени файла (то есть сглаживание) на нем. Во-вторых, echoэто не надежный способ печати строки, потому что некоторые строки, начинающиеся с a -, обрабатываются как параметры, а некоторые оболочки выполняют расширение обратной косой черты для аргумента. Надежный и портативный способ печати строки printf %s "${kv#*=}". Кроме того, если значение занимает несколько строк, программа sed будет работать с каждой отдельно, что неправильно. Это поправимо, но есть более простой способ, который заключается в использовании манипуляции строки средства оболочечного: val=${kv#*=}; val=${val#\"}; val=${val%\"}.

Предполагая, что ваш ввод правильно проанализирован с помощью простого read, вот более простой способ его проанализировать, воспользовавшись IFSнастройкой для разделения каждой строки:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
Жиль "ТАК - перестань быть злым"
источник
0

Другим хорошим вариантом может быть размещение exportв файле и его источник:

export XAUTHLOCALHOSTNAME="localhost"
export DISPLAY=":0"
export XAUTHORITY="/tmp/some-XAuthority"

а потом:

. "$1"
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
источник
0

/ окр

EXPORT=value
#NOTEXPORT=notvalue

скрипт

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
Лукаш Куровски
источник