Вероятно, я что-то упускаю, но пытаюсь сохранить переменные среды с символами новой строки в файл .env. В идеале я хочу сделать это со всеми доступными переменными env (выходные данные env).
Возьмите эту переменную:
export MY_VAR = "Line 1
Line 2
Line 3"
Как сохранить это в файле .env, который будет выглядеть так: переменные.env
MY_VAR=Line 1\nLine2\nLine3
И как мне сделать это со всеми доступными переменными env, например, из env
? Я попробовал это:
while IFS= read -r -d '' line; do
line=$(echo -n "$line" | sed '$!s/$/\\n/' | tr -d '\n')
echo "$line" >> variables.env
done < <(env -0)
Но это также не работает для \r.
Я думаю, вам следует для пояснения предоставить таблицу, показывающую, какие символы переменной вы хотите отобразить в какое печатное представление. Очевидно, вы хотите преобразовать шестнадцатеричный 0A в \n и, возможно, шестнадцатеричный 0D в \r. но как бы вы, например, преобразули шестнадцатеричное число 0B?
Моя цель состоит в том, чтобы проглотить его с помощью ReadInConfig() golang viper, чтобы сам файл env не мог содержать несколько строк, поскольку это нарушает правила файла .env. В идеале это все строки в стиле C, как вы сказали, jwd, но символы новой строки определенно сломают файл.
Судя по комментарию @jepner к моему ответу, очевидно, что существует стандартный пакет NPM для использования файлов .env
. Я не знаю, имели ли вы это в виду или вы используете это имя в более широком смысле, но вы можете взглянуть на него. npmjs.com/package/dotenv
С env
это сделать сложно. Вывод env
по умолчанию принципиально неоднозначен при наличии символов новой строки. После export FOO=$'zero\nBAR=one'
, env
выведет две строки, FOO=zero
и BAR=one
, которые невозможно отличить от двух переменных $FOO
и $BAR
.
$ export FOO=$'zero\nBAR=one' ; export BAR=one ; env | grep -e FOO -e BAR
FOO=zero
BAR=one
BAR=one
$
Если вы используете версию команды env
GNU coreutils, у нее есть опция -0
или --null
, которая заканчивает каждую строку вывода нулевым символом, а не символом новой строки. Это однозначно, поскольку имена и значения переменных среды не могут содержать нулевые символы (нулевой символ завершает строку).
env -0 > .env
Если в вашей команде env
нет этой опции, вы можете написать программу (возможно, на C или Perl), которая делает то же самое.
Вы спросили о сохранении среды в файл .env
. Вы не просили прочитать это потом. Вероятно, вы можете использовать bash read -r -d $'\0'
для чтения строк из файла. (Я сам не совсем понял, как это работает. Вам, вероятно, придется поиграть с $IFS
, чтобы избежать разделения слов.)
.env
файлы не имеют той же семантики, что и оболочка, поэтому чтение их из оболочки мало чем отличается от чтения с любого другого языка: вам нужно написать правильный парсер.
@chepner Есть ли какой-то стандартный формат для файлов .env
? Если так, то я не знал об этом. Можете ли вы предоставить ссылку?
Насколько мне известно, существует только стандарт де-факто, определяющий, как функционирует оригинальная(?) библиотека JavaScript.
@chepner Хм. Я никогда об этом не слышал и не предполагаю, что ФП имеет в виду именно это. .env
кажется разумным именем для файла, в котором хранится среда, не обязательно в каком-либо стандартном формате. Я оставлю комментарий по вопросу.
После того, как вы сохранили env в файле (как показал @Keith Thompson), вы можете выполнить программу в этой среде примерно так:
#! /usr/bin/env python3
import subprocess
with open(".env") as file:
env = dict(filter(lambda e:len(e)==2, map(lambda kv:tuple(kv.split(" = ")), map(bytes.decode, file.read().encode().split(b"\x00")))))
subprocess.run(["mycommand"], env=env)
Это разделение на NUL-байты и построение набора пар (ключ, значение).
Этого может быть достаточно для того, что вы пытаетесь сделать:
$ cat tst.sh
#!/usr/bin/env bash
export MY_VAR = "Line 1
Line 2
Line 3"
export MY_VAR2=17
regexp = "^[$]?'(.*)'$"
while IFS= read -r -d '' line; do
printf -v line '%q' "$line"
# or you could do this if your bash version is 4.4 or newer:
# line = "${line@Q}"
if [[ "$line" =~ $regexp ]]; then
line = "${BASH_REMATCH[1]}"
fi
printf '%s\n' "$line"
done < <(env -0)
$ ./tst.sh | grep MY_VAR
MY_VAR=Line 1\nLine 2\nLine 3
MY_VAR2=17
Это возвращает line 11: ${line@Q}: bad substitution
в терминале bash.
Получите новую версию bash, так как ваша очень устарела (эта функция была представлена почти 10 лет назад в bash 4.4, сейчас мы работаем с bash 5.2), но я добавил альтернативный способ сделать по сути то же самое.
Отличный звонок — не знал, что мой vscode bash настолько устарел. Обновление исправило это, но также спасибо за версию с обратной совместимостью.
Каков формат этого финального
.env
файла? Вы просто хотите, чтобы символы новой строки были закодированы как\n
? Вам нужны полные строки в стиле C (включая\t
,\r
и т. д.)? Это формат, который где-то задокументирован? Существует множество способов кодирования текста, сохраняющих символы новой строки и другие специальные символы; будет полезно узнать, чего вы хотите, более подробно.