Я пытаюсь понять, как заставить bash (принудительно?) Расширять переменные в строке (которая была загружена из файла).
У меня есть файл с названием something.txt с содержанием:
hello $FOO world
Я тогда бегу
export FOO=42
echo $(cat something.txt)
это возвращает:
hello $FOO world
Он не расширил $ FOO, даже если переменная была установлена. Я не могу выполнить оценку или источник файла - поскольку он попытается выполнить его (он не исполняемый как есть - мне просто нужна строка с интерполированными переменными).
Любые идеи?
eval
.eval
и важно осознавать последствия.Ответы:
Я наткнулся на то, что, как мне кажется, является ответом на этот вопрос:
envsubst
команду.Если он еще не доступен в вашем дистрибутиве, он находится в
Пакет GNU
gettext
.@Rockallite - Я написал небольшой сценарий-оболочку, чтобы решить проблему '\ $'.
(Кстати, есть «особенность» envsubst, описанная на https://unix.stackexchange.com/a/294400/7088 для расширения только некоторых переменных во входных данных, но я согласен, что экранирование исключений намного сложнее удобный.)
Вот мой сценарий:
#! /bin/bash ## -*-Shell-Script-*- CmdName=${0##*/} Usage="usage: $CmdName runs envsubst, but allows '\$' to keep variables from being expanded. With option -sl '\$' keeps the back-slash. Default is to replace '\$' with '$' " if [[ $1 = -h ]] ;then echo -e >&2 "$Usage" ; exit 1 ;fi if [[ $1 = -sl ]] ;then sl='\' ; shift ;fi sed 's/\\\$/\${EnVsUbDolR}/g' | EnVsUbDolR=$sl\$ envsubst "$@"
источник
$
экранирование знака доллара ( ), напримерecho '\$USER' | envsubst
, выводит текущее имя пользователя с префиксом обратной косой черты, а не$USER
.brew link --force gettext
Многие ответы с использованием
eval
иecho
виды работы, но разбиваются о различных вещах, таких как несколько строк, попытки экранирования метасимволов оболочки, экранирование внутри шаблона, не предназначенного для расширения с помощью bash и т. Д.У меня была такая же проблема, и я написал эту функцию оболочки, которая, насколько я могу судить, обрабатывает все правильно. Это по-прежнему удаляет из шаблона только завершающие символы новой строки из-за правил подстановки команд bash, но я никогда не обнаруживал, что это проблема, пока все остальное остается нетронутым.
apply_shell_expansion() { declare file="$1" declare data=$(< "$file") declare delimiter="__apply_shell_expansion_delimiter__" declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter" eval "$command" }
Например, вы можете использовать его таким образом с a,
parameters.cfg
который на самом деле является сценарием оболочки, который просто устанавливает переменные, иtemplate.txt
с шаблоном, который использует эти переменные:. parameters.cfg printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
На практике я использую это как своего рода облегченную систему шаблонов.
источник
eval
подводные камни. Попробуйте добавить; $(eval date)
в конец любой строки во входном файле, и он запуститdate
команду.можешь попробовать
echo $(eval echo $(cat something.txt))
источник
echo -e "$(eval "echo -e \"`<something.txt`\"")"
если вам нужно сохранить новые строки.template.txt
есть текст. Эта операция опасна, потому что она выполняет (оценивает) команды, если они есть.Вы не хотите печатать каждую строку, вы хотите оценить ее, чтобы Bash мог выполнять замену переменных.
FOO=42 while read; do eval echo "$REPLY" done < something.txt
См.
help eval
Или руководство по Bash для получения дополнительной информации.источник
eval
опасно ; вы не только$varname
расширяетесь (как если бы это былоenvsubst
), но$(rm -rf ~)
также и.Другой подход (который кажется неприятным, но я все равно помещаю его здесь):
Запишите содержимое something.txt во временный файл, обернув его оператором echo:
something=$(cat something.txt) echo "echo \"" > temp.out echo "$something" >> temp.out echo "\"" >> temp.out
затем верните его обратно в переменную:
RESULT=$(source temp.out)
и в $ RESULT все это будет развернуто. Но это кажется таким неправильным!
источник
Если вы хотите, чтобы были расширены только ссылки на переменные (цель, которую я поставил перед собой), вы можете сделать следующее.
contents="$(cat something.txt)" echo $(eval echo \"$contents\")
(Ключевыми здесь являются экранированные кавычки вокруг $ contents)
источник
eval "echo \"$$contents\""
и она сохранила пробелыСледующее решение:
позволяет заменять переменные, которые определены
оставляет неизменными заполнители переменных, которые не определены. Это особенно полезно при автоматическом развертывании.
поддерживает замену переменных в следующих форматах:
${var_NAME}
$var_NAME
сообщает, какие переменные не определены в среде, и возвращает код ошибки для таких случаев
TARGET_FILE=someFile.txt; ERR_CNT=0; for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do VAR_VALUE=${!VARNAME}; VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' ); VAR_VALUE2=${!VARNAME2}; if [ "xxx" = "xxx$VAR_VALUE2" ]; then echo "$VARNAME is undefined "; ERR_CNT=$((ERR_CNT+1)); else echo "replacing $VARNAME with $VAR_VALUE2" ; sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE}; fi done if [ ${ERR_CNT} -gt 0 ]; then echo "Found $ERR_CNT undefined environment variables"; exit 1 fi
источник
Если something.txt имеет только одну строку,
bash
метод (более короткая версия «неприглядного» ответа Майкла Нила ), использующий подстановку процесса и команды :FOO=42 . <(echo -e echo $(<something.txt))
Выход:
Обратите внимание, что в
export
этом нет необходимости.Если something.txt содержит одну или несколько строк, метод оценки GNU :
sed
e
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
источник
sed
оценку, которая лучше всего соответствует моей цели. Мне нужно было создать сценарии из шаблонов, значения которых будут заполняться из переменных, экспортированных в другой файл конфигурации. Он правильно обрабатывает экранирование для расширения команды, например, вставьте\$(date)
в шаблон для получения$(date)
вывода. Благодарность!$ eval echo $(cat something.txt) hello 42 world $ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17) Copyright (C) 2007 Free Software Foundation, Inc.
источник
foo=45 file=something.txt # in a file is written: Hello $foo world! eval echo $(cat $file)
источник
envsubst
- отличное решение (см. ответ LenW), если контент, который вы заменяете, имеет «разумную» длину.В моем случае мне нужно было заменить содержимое файла, чтобы заменить имя переменной.
envsubst
требует, чтобы содержимое экспортировалось как переменные среды, и у bash возникает проблема при экспорте переменных среды, размер которых превышает мегабайт или около того.решение awk
Используя решение cuonglm из другого вопроса:
needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.) needle_file="doc1_base64.txt" # Will be substituted for the needle haystack=$requestfile1 # File containing the needle out=$requestfile2 awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out
Это решение работает даже с большими файлами.
источник
Следующие работы:
bash -c "echo \"$(cat something.txt)"\"
источник
$foo
, но и небезопасные$(rm -rf ~)
.