Принуждение bash к расширению переменных в строке, загруженной из файла


Я пытаюсь понять, как сделать bash (force?) разверните переменные в строке (которая была загружена из файла).

у меня есть файл с названием "Что-то.txt" с содержание:

hello $FOO world

Я тогда бегу

export FOO=42
echo $(cat something.txt)

возвращает:

   hello $FOO world

он не расширил $FOO, хотя переменная была установлена. Я не могу eval или источник файла - как он будет пытаться выполнить его (это не исполняемый файл, как это - я просто хочу строку с переменными интерполированный.)

какие идеи?

12 56

12 ответов:

я наткнулся на то, что я думаю, что ответ на этот вопрос:.

envsubst < something.txt

если он еще не доступен в вашем дистрибутиве, он находится в

пакет 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 [[  = -h ]]  ;then echo -e >&2  "$Usage" ; exit 1 ;fi
if [[  = -sl ]] ;then  sl='\'  ; shift ;fi

sed 's/\$/${EnVsUbDolR}/g' |  EnVsUbDolR=$sl$  envsubst  "$@"

многие ответы с помощью eval и echo вид работы, но перерыв на различные вещи, такие как несколько строк, пытаясь избежать оболочки мета-символов, убегает внутри шаблона не предназначен для расширения bash и т.д.

у меня была такая же проблема, и я написал эту функцию оболочки, которая, насколько я могу судить, обрабатывает все правильно. Это по-прежнему будет удалять только конечные новые строки из шаблона из-за правил замены команд bash, но я никогда обнаружил, что это проблема, пока все остальное остается нетронутым.

apply_shell_expansion() {
    declare file=""
    declare data=$(< "$file")
    declare delimiter="__apply_shell_expansion_delimiter__"
    declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
    eval "$command"
}

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

. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt

на практике я использую это как своего рода легкий шаблон системы.

вы можете попробовать

echo $(eval echo $(cat something.txt))

вы не хотите печатать каждую строку, вы хотите оценить это так, что Bash может выполнять переменные замены.

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt

посмотреть help eval или руководство по Bash для получения дополнительной информации.

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

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

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out

затем верните его обратно в переменную:

RESULT=$(source temp.out)

и $результат будет иметь все это расширено. Но это кажется таким неправильным !

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

contents="$(cat something.txt)"
echo $(eval echo \"$contents\")

(экранированные кавычки вокруг $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
  1. bash метод (однострочный вариант ответ Майкла Нила), используя процесс & команду:

    FOO=42 . <(echo -e echo $(<something.txt))
    

    выход:

    hello 42 world
    

    отметим, что export не требуется.

  2. GNUsedоценить способ, если что-то.txt много строк:

    FOO=42 sed 's/"/\\"/g;s/.*/echo "&"/e' something.txt
    
$ 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.

envsubst - отличное решение (см. ответ LenW), если контент, который вы заменяете, имеет "разумную" длину.

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

awk solution

используя cuonglm решение от a другой вопрос:

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

Это решение работает даже для больших файлов.

foo=45
file=something.txt       # in a file is written: Hello $foo world!
eval echo $(cat $file)

следующие работы: bash -c "echo \"$(cat something.txt)"\"