Встановити змінні середовища з файлу пар ключів / значень


511

TL; DR: Як я можу експортувати набір пар ключів / значень із текстового файлу в середовище оболонки?


Для запису нижче - оригінальна версія питання із прикладами.

Я пишу сценарій в bash, який аналізує файли з 3 змінними у певній папці, це одна з них:

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

Цей файл зберігається в ./conf/prac1

Мій сценарій minientrega.sh потім аналізує файл за допомогою цього коду:

cat ./conf/$1 | while read line; do
    export $line
done

Але коли я виконую minientrega.sh prac1в командному рядку, він не встановлює змінні середовища

Я також спробував використовувати, source ./conf/$1але ця проблема все ще стосується

Можливо, є якийсь інший спосіб зробити це, мені просто потрібно використовувати змінні середовища файлу, який я передаю, як аргумент мого сценарію.



Те саме з Ruby: stackoverflow.com/questions/2139080/… , дорогоцінний камінь, який це робить: github.com/bkeepers/dotenv
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Це велике запитання, але воно є надто специфічним, з конкретними назвами змінних ("MINIENTREGA_FECHALIMITE"? Що це означає?) Та цифрами (3). Загальне питання просто: "Як експортувати набір пар ключів / значень із текстового файлу в середовище оболонки".
Дан Даскалеску

Крім того, на це вже відповіли на unix.SE і, мабуть, там більше теми.
Дан Даскалеску

Відповіді:


208

Проблема з вашим підходом полягає exportв тому, що whileцикл відбувається в під оболонці, і ця змінна не буде доступною в поточній оболонці (батьківській оболонці циклу while).

Додати exportкоманду в сам файл:

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"

Потім потрібно джерело файлу в поточній оболонці, використовуючи:

. ./conf/prac1

АБО

source ./conf/prac1

4
Хоча читання файлу рядок за рядком і проходить кожну лінію , щоб exportне є ідеальним, проблема також може бути вирішена просто використовуючи перенаправлення вводу на петлі: while read line; do ... ; done < ./conf/$1.
чепнер

4
А якщо це не з файлу, використовуйте< <(commands that generate output)
o11c

5
У вас є більш чисте рішення , я маю перевагуset -o allexport
heralight

2
Якщо ви користуєтесь цим файлом .env між системами, вставка exportможе порушити його на такі речі, як Java, SystemD або інші інструменти
FilBot3

1
awk '{print "export " $0}' envfileкоманда зручності для попереднього експорту до початку кожного рядка
Шардж

887

Це може бути корисно:

export $(cat .env | xargs) && rails c

Причина, чому я використовую це, якщо я хочу перевірити .envречі на своїй консолі рейки.

gabrielf придумав хороший спосіб зберегти змінні місцевими. Це вирішує потенційну проблему при переході від проекту до проекту.

env $(cat .env | xargs) rails

Я це перевірив bash 3.2.51(1)-release


Оновлення:

Щоб ігнорувати рядки, які починаються з цього #, використовуйте це (завдяки коментарю Піта ):

export $(grep -v '^#' .env | xargs)

І якщо ви хочете, щоб unsetусі змінні, визначені у файлі, використовуйте це:

unset $(grep -v '^#' .env | sed -E 's/(.*)=.*/\1/' | xargs)

Оновлення:

Для обробки значень з пробілами використовуйте:

export $(grep -v '^#' .env | xargs -d '\n')

для систем GNU - або:

export $(grep -v '^#' .env | xargs -0)

на системах BSD.


6
Дякую, мені подобається, що для цього не потрібно нічого попередньо додавати до файлу - дозволяє сумісність з форматом Foreman (Procfile) .env.
natevw

29
Я придумав рішення: env $ (cat .env | xargs) rails
gabrielf

4
Здається, це не спрацьовує, якщо будь-яке значення env має пробіли, хоча я насправді не впевнений, який найкращий / бажаний спосіб вказати значення з пробілами. github.com/ddollar/foreman/isissue/56 каже, що це має працювати так, export $(cat .env)але я не знаю, як це зробити з пробілами.
Dan Benamy

6
@BenjaminWheeler GNU linux має -dвстановити роздільник, тому я намагаюся env $(cat .env | xargs -d '\n') rails, але він все ще помилки з файлом, не знайденим, якщо .envє пробіли. Будь-яка ідея, чому це не працює?
Бейлі Паркер

19
Ось коротша варіаціяeval $(cat .env) rails
manalang

412

-o allexportдозволяє експортувати всі наступні визначення змінних. +o allexportвідключає цю функцію.

set -o allexport
source conf-file
set +o allexport

9
Працює як шарм! Навіть якщо у .envфайлі є коментарі. Дякую!
Слава Фомін II

9
І в одному рядкуset -o allexport; source conf-file; set +o allexport
HarlemSquirrel

1
Це прекрасний спосіб читати у файлі властивостей, коли плагін Jenkins EnvInject не працює. Дякую!
Тереза ​​Пітерс

21
@CMCDragonkai, для POSIX це було б set -a; . conf-file; set +a.
Чарльз Даффі

3
Цей метод працює, якщо змінні середовища мають пробіли. Багато хто з інших цього не робить. Хоча метод eval () робить, я також отримую трохи
дивного

137
set -a
. ./env.txt
set +a

Якщо env.txtтак:

VAR1=1
VAR2=2
VAR3=3
...

Пояснення -a еквівалентний алекспорту . Іншими словами, кожне призначення змінної в оболонці експортується у навколишнє середовище (для використання у кількох дочірніх процесах). Додаткову інформацію можна знайти в комплекті вбудованої документації :

-a     Кожна змінна або функція, яка створюється чи змінюється, отримує атрибут експорту та позначається для експорту в оточення наступних команд.

Використання "+", а не "-" призводить до вимкнення цих параметрів. Параметри також можна використовувати при виклику оболонки. Поточний набір опцій можна знайти в $ -.


Чи можете ви пояснити -a та + a?
Отто

11
@Otto -aеквівалентно allexport. Іншими словами, кожне призначення змінної в оболонці exportперетворюється на навколишнє середовище (для використання у кількох дочірніх процесах). Також дивіться цю статтю gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
Dan Kowalczyk

33

allexportВаріант згадується в декількох інших відповідей тут, для яких set -aє ярлик. Пошук .env дійсно кращий, ніж циклічний перегляд рядків та експорт, тому що він дозволяє коментувати, порожні рядки та навіть змінні середовища, що генеруються командами. Мій .bashrc включає в себе наступне:

# .env loading in the shell
dotenv () {
  set -a
  [ -f .env ] && . .env
  set +a
}

# Run dotenv on login
dotenv

# Run dotenv on every new directory
cd () {
  builtin cd $@
  dotenv
}

3
Це виглядає приємно, але ви вивантажуєте змінні середовища, поки виходите з каталогу?
Бастіан Вентур

1
Я не знімаю змінні, і це ніколи не було проблемою. Мої програми, як правило, використовують імена змінних, які є чіткими, і якщо є перекриття, я встановлю їх у цьому .env з VAR=.
gsf

26
eval $(cat .env | sed 's/^/export /')

1
Використання eval $(cat .env | sed 's/^[^$]/export /')дозволяє мати порожні рядки для кращої читабельності.
Маріо Угер

2
Я вважаю, що це cat .env | sed 's/^[^$]/export /'позбавляє початкового символу. Тобто для файлу, який A=foo\nB=bar\nя отримую export =foo\nexport =bar\n. Це працює краще для пропуску порожніх рядків: cat .env | sed '/^$/! s/^/export /'.
Оуен С.

(Я також зауважу, заради гольфістів коду UNIX, який вам не потрібен catв будь-якому випадку: eval $(sed 's/^/export /' .env)працює так само добре.)
Оуен С.

21

Я знайшов найефективніший спосіб:

export $(xargs < .env)

Пояснення

Коли у нас є такий .envфайл:

key=val
foo=bar

бігти xargs < .envдістанетьсяkey=val foo=bar

тому ми отримаємо export key=val foo=barі це саме те, що нам потрібно!

Обмеження

  1. Він не обробляє випадки, коли значення мають пробіли. Такі команди, як env, створюють цей формат. - @Shardj

3
Він не обробляє випадки, коли значення мають пробіли. Такі команди, як envвиробляють цей формат.
Шардж

19

Ось ще одне sedрішення, яке не працює eval і не вимагає рубіну:

source <(sed -E -n 's/[^#]+/export &/ p' ~/.env)

Це додає експорт, зберігаючи коментарі в рядках, починаючи з коментаря.

.енв зміст

A=1
#B=2

зразок виконання

$ sed -E -n 's/[^#]+/export &/ p' ~/.env
export A=1
#export B=2

Я вважаю це особливо корисним при створенні такого файлу для завантаження у файл системного блоку, сEnvironmentFile .


не підтримує кілька рядків в OSX
Abdennour TOUMI

17

Я відповів користувачеві на відповідь user4040650, тому що він є простим, і він дозволяє коментарі у файлі (тобто рядки, що починаються з #), що для мене дуже бажано, оскільки можна додавати коментарі, що пояснюють змінні. Просто переписування в контексті оригінального питання.

Якщо скрипт викликається так, як зазначено:, minientrega.sh prac1тоді minientrega.sh може мати:

set -a # export all variables created next
source $1
set +a # stop exporting

# test that it works
echo "Ficheros: $MINIENTREGA_FICHEROS"

З встановленої документації було вилучено наступне :

Цей вбудований настільки складний, що він заслуговує на власний розділ. Набір дозволяє змінювати значення параметрів оболонки та встановлювати позиційні параметри, або відображати імена та значення змінних оболонок.

set [--abefhkmnptuvxBCEHPT] [-o-ім'я опції] [аргумент…] set [+ abefhkmnptuvxBCEHPT] [+ o name-name] [аргумент ...]

Якщо не надано жодних параметрів чи аргументів, набір відображає імена та значення всіх змінних оболонок та функцій, відсортованих відповідно до поточного локалу, у форматі, який може бути використаний у якості введення для встановлення чи скидання заданих змін. Змінні лише для читання неможливо скинути. У режимі POSIX перераховані лише змінні оболонки.

Коли параметри надаються, вони встановлюють або скасовують атрибути оболонки. Опції, якщо вони вказані, мають таке значення:

-a Кожна змінна або функція, яка створюється чи змінюється, отримує атрибут експорту та позначається для експорту в оточення наступних команд.

І це також:

Використання "+", а не "-" призводить до вимкнення цих параметрів. Параметри також можна використовувати при виклику оболонки. Поточний набір опцій можна знайти в $ -.


14

Поліпшення відповіді Сіласа Павла

експорт змінних в підзарядку робить їх локальними для команди.

(export $(cat .env | xargs) && rails c)


Тоді ви можете використовувати це, (set -a; source dev.env; set +a; rails c)щоб мати також переваги пошуку (наприклад, сценарій може виконуватися).
wacha

12

SAVE=$(set +o | grep allexport) && set -o allexport && . .env; eval "$SAVE"

Це дозволить зберегти / відновити початкові параметри, якими б вони не були.

Використання set -o allexportмає перевагу в тому, щоб правильно пропускати коментарі без регулярного вираження.

set +oсам виводить усі ваші поточні параметри у форматі, який може пізніше виконати bash. Також зручно: set -oсам по собі виводить усі ваші поточні параметри у зручному для людини форматі.


2
Я, мабуть, exec env -i bashочистить існуюче середовище перед тим, як викликати, evalякщо вам потрібно скасувати змінні, які встановлені лише в .env.
b4hand

11

Найкоротший шлях, який я знайшов:

Ваш .envфайл:

VARIABLE_NAME="A_VALUE"

Тоді просто

. ./.env && echo ${VARIABLE_NAME}

Бонус: Оскільки це короткий однолінійний package.jsonфайл , він дуже корисний у файлі

  "scripts": {
    "echo:variable": ". ./.env && echo ${VARIABLE_NAME}"
  }

Як щодо того, якщо у вас багато змінних?
Madeo

@Madeo ви можете додати скільки завгодно ліній, так само, як і рядокVARIABLE_NAME="A_VALUE"
Flavien

9

Простіше:

  1. захопити вміст файлу
  2. видаліть порожні рядки (просто, якщо ви розділили деякі речі)
  3. видалити будь-які коментарі (просто випадок, коли ви додали кілька ...)
  4. додати exportдо всіх рядків
  5. eval вся справа

eval $(cat .env | sed -e /^$/d -e /^#/d -e 's/^/export /')

Інший варіант (вам не потрібно бігати eval(завдяки @Jaydeep)):

export $(cat .env | sed -e /^$/d -e /^#/d | xargs)

Нарешті, якщо ви хочете зробити своє життя дійсно легким, додайте це до свого ~/.bash_profile:

function source_envfile() { export $(cat $1 | sed -e /^$/d -e /^#/d | xargs); }

(ПЕРЕКОНАЙТЕ, ЩО ВИ ВИСТАВЛЯєте ВАШІ НАЛАШТУВАННЯ БАШУ !!! source ~/.bash_profileабо .. просто створіть нову вкладку / вікно та вирішіть проблему) ви називаєте це так:source_envfile .env


2
Я повинен був прочитати .env текст з gitlab секретної змінної для трубопроводу: на основі вашого рішення ця команда працювала для мене source <( echo $(sed -E -n 's/[^#]+/ &/ p' <(echo "${2}" | tr -d '\r')) );. Якось gitlab зберігає секретну змінну з поверненням каретки Windows, тому мені довелося виправити це tr -d '\r'.
metanerd

6

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

. ./minientrega.sh

Також може виникнути проблема з cat | while readпідходом. Я б рекомендував використовувати підхід while read line; do .... done < $FILE.

Ось робочий приклад:

> cat test.conf
VARIABLE_TMP1=some_value

> cat run_test.sh
#/bin/bash
while read line; do export "$line";
done < test.conf
echo "done"

> . ./run_test.sh
done

> echo $VARIABLE_TMP1
some_value

На відміну від більшості інших відповідей, це рішення не оцінюється test.confяк файл сценарію. Це робить його кращим. Безпечніше не допускати сценаріїв, якщо вам це справді не потрібно, особливо якщо хтось не усвідомлює, що це відбувається (або забуває).
meustrus

5

Спираючись на інші відповіді, ось спосіб експортувати лише підмножину рядків у файл, включаючи значення з пробілами на кшталт PREFIX_ONE="a word":

set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a

5

Ось мій варіант:

  with_env() {
    (set -a && . ./.env && "$@")
  }

порівняно з попередніми рішеннями:

  • він не просочує змінні за межами області (значення з .envне піддаються виклику)
  • не клобує setваріантів
  • повертає код виходу виконаної команди
  • використовує позі, сумісний set -a
  • використовує .замість того, sourceщоб уникнути башизму
  • команда не викликається, якщо .envзавантаження не вдається
with_env rails console

Ви також можете запустити Inline (змінні піддаються вашому поточному сеансу терміналу): set -a && . ./.env && "$@" && echo "your comand here"
Giovanne Afonso

4

Я працюю з docker-compose та .envфайлами на Mac, і хотів імпортувати .envв свою баш-оболонку (для тестування), і тут найкраща відповідь наштовхується на таку змінну:

.env

NODE_ARGS=--expose-gc --max_old_space_size=2048

Рішення

Тож я закінчив використовувати evalта загорнув env var defs в окремі лапки.

eval $(grep -v -e '^#' .env | xargs -I {} echo export \'{}\')

Версія Bash

$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.


2

У мене є проблеми з запропонованими раніше рішеннями:

  • Рішення @ anubhava робить написання файлів конфігурації bash дуже дратує, а також - можливо, ви не хочете завжди експортувати свою конфігурацію.
  • Рішення @Silas Paul ламається, коли у вас є змінні, які мають пробіли чи інші символи, які добре працюють у цитованих значеннях, але $()створюють безлад.

Ось моє рішення, яке все ще є досить жахливим IMO - і не вирішує проблему "експорту лише до однієї дитини", яку вирішує Silas (хоча ви, ймовірно, можете запустити її в підрозділі, щоб обмежити область застосування):

source .conf-file
export $(cut -d= -f1 < .conf-file)

2

Мої вимоги були:

  • простий .env файл без exportпрефіксів (для сумісності з dotenv)
  • підтримуючі значення в лапках: TEXT = "альфа-браво Чарлі"
  • підтримуючі коментарі з префіксом # і порожні рядки
  • універсальний як для mac / BSD, так і для linux / GNU

Повна робоча версія, складена з відповідей вище:

  set -o allexport
  eval $(grep -v '^#' .env | sed 's/^/export /')
  set +o allexport

1
який сенс "-o allexport", якщо ви все одно додаєте їх до "експорту"?
il - ya

2

По-перше, створіть файл середовища, який матиме всю пару ключових значень оточуючих середовищ, як показано нижче, і назвав його всім, що вам подобається, в моєму випадку його env_var.env

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

Потім створіть скрипт, який буде експортувати всі змінні середовища для середовища python, як показано нижче, та назвіть його як export_env.sh

#!/usr/bin/env bash

ENV_FILE="$1"
CMD=${@:2}

set -o allexport
source $ENV_FILE
set +o allexport

$CMD

Цей скрипт буде використовувати перший аргумент як файл середовища, а потім експортує всю змінну середовища у цей файл, а потім запустить команду після цього.

ВИКОРИСТАННЯ:

./export_env.sh env_var.env python app.py

1

Я натрапив на цю нитку, коли я намагався повторно використовувати Docker --env-files в оболонці. Їх формат не сумісний з bash, але він простий: name=valueні цитування, ні підміна. Вони також ігнорують порожні рядки та #коментарі.

Я не міг би отримати його сумісним з posix, але ось такий, який повинен працювати в bash-подібних оболонках (тестується в zsh на OSX 10.12.5 і bash на Ubuntu 14.04):

while read -r l; do export "$(sed 's/=.*$//' <<<$l)"="$(sed -E 's/^[^=]+=//' <<<$l)"; done < <(grep -E -v '^\s*(#|$)' your-env-file)

Він не буде обробляти три випадки у прикладі з документів, пов'язаних вище:

  • bash: export: `123qwe=bar': not a valid identifier
  • bash: export: `org.spring.config=something': not a valid identifier
  • і він не буде обробляти найпростіший синтаксис (голий FOO)

1

Білі пробіли у значенні

Тут є багато чудових відповідей, але я виявив, що всі вони не мають підтримки для білого простору в ціні:

DATABASE_CLIENT_HOST=host db-name db-user 0.0.0.0/0 md5

Я знайшов два рішення, які працюють з такими значеннями, підтримуючи порожні рядки та коментарі.

Один заснований на SED і @ Хав'єр-BUZZI відповідь :

source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x "&"/g' .env)

І один з рядком читання в циклі на основі відповіді @ john1024

while read -r line; do declare -x "$line"; done < <(egrep -v "(^#|^\s|^$)" .env)

Ключовим тут є використання declare -xта розміщення рядків у подвійних лапках. Я не знаю, чому, але коли ви переформатуєте код циклу на кілька рядків, він не спрацює - я не баш програміст, я просто згуртував їх разом, це для мене все ще магія :)


1
Мені довелося модифікувати sedрішення, щоб змусити його працювати. Але спочатку кілька пояснень: -eце короткий термін --expression, який просто говорить про sedте, які операції потрібно зробити. -e /^$/dвидаляє порожні рядки з виводу (не файл). -e /^#/dвидаляє коментарі bash (рядки, які починаються з #) з виводу. 's/.*/declare -x "&"/g'замінює (замінює) інші рядки на declare -x "ENV_VAR="VALUE"". Коли ви джерело цього, принаймні для мене, це не спрацювало. Натомість мені довелося використовувати source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x &/g' .env), щоб зняти зайву "обгортку.
jcasner

Я не використовую ENV_VAR="lorem ipsum", ENV_VAR=lorem ipsumбез цитат у файлі .env. Зараз я не впевнений, чому, але це, ймовірно, було проблематично в інших інструментах, які аналізують цей файл. І замість цього lorem ipsumя закінчився "lorem ipsum"цінністю - цитатами. Thx для пояснень :)
Janusz Skonieczny

1
Якби це був мій вибір, я б не використовував ENV_VAR="lorem ipsum"жодного. У моєму випадку використання мій провайдер хостингу генерує цей файл на основі деяких параметрів конфігурації, які я встановив, і вони вставляють подвійні лапки. Отже, я змушений обійтись. Дякуємо за вашу допомогу тут - врятували мені багато часу, намагаючись самостійно опрацювати правильні sedваріанти!
jcasner

1

спробуйте щось подібне

for line in `cat your_env_file`; do if [[ $line != \#* ]];then export $line; fi;done

1
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t

Як це працює

  1. Створення тимчасового файлу.
  2. Запишіть усі поточні значення змінних середовища у тимчасовий файл.
  3. Увімкнути експорт усіх оголошених змінних у сценарії джерел до навколишнього середовища.
  4. Прочитати .envфайл. Всі змінні будуть експортовані в поточне середовище.
  5. Вимкнути експорт усіх оголошених змінних у сценарії джерел до навколишнього середовища.
  6. Прочитайте вміст тимчасового файлу. Кожен рядок мав declare -x VAR="val"би експортувати кожну зі змінних у середовище.
  7. Видаліть темп-файл.
  8. Видаліть назву файлу temp для змінної.

Особливості

  • Зберігає значення змінних, вже встановлених у середовищі
  • .env може мати коментарі
  • .env може мати порожні рядки
  • .envне вимагає спеціального колонтитула або колонтитула, як в інших відповідях ( set -aі set +a)
  • .envне вимагає наявності exportдля кожного значення
  • однолінійний

0

Якщо ви отримуєте помилку, оскільки одна із змінних містить значення, яке містить пробіли, ви можете спробувати скинути bash's IFS(Internal Field Separator), \nщоб дозволити bash інтерпретувати cat .envрезультат як список параметрів для envвиконуваного файлу.

Приклад:

IFS=$'\n'; env $(cat .env) rails c

Дивись також:


0

Мій файл .env виглядає так:

DATABASE_URI="postgres://sa:***@localhost:5432/my_db"
VARIABLE_1="SOME_VALUE"
VALIABLE_2="123456788"

Використовуючи способи @henke , експортоване значення в кінцевому підсумку містить лапки"

"postgres://sa:***@localhost:5432/my_db"
"SOME_VALUE"
"123456788"

Але я хочу, щоб експортоване значення містило лише:

postgres://sa:***@localhost:5432/my_db
SOME_VALUE
123456788

Щоб виправити це, я редагую команду для видалення лапок:

export $(grep -v '^#' dev.env | tr --delete '"' | xargs -d '\n')

0

Він справляється з пробілами на RHS і пропускає "дивні" параметри, такі як визначення модуля bash (з "()" у них):

echo "# source this to set env vars" > $bld_dir/.env
env | while read line; do
    lhs="${line%%=*}"
    rhs="${line#*=}"
    if [[ "$lhs" =~ ^[0-9A-Za-z_]+$ ]]; then
        echo "export $lhs=\"$rhs\"" >> $bld_dir/.env
    fi
done
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.