Estou tentando descobrir como fazer bash (forçar?) Expandir variáveis em uma string (que foi carregada de um arquivo).
Eu tenho um arquivo chamado "something.txt" com o conteúdo:
hello $FOO world
Então eu corro
export FOO=42
echo $(cat something.txt)
isso retorna:
hello $FOO world
Não expandiu $ FOO, embora a variável tenha sido definida. Não consigo avaliar ou fornecer o arquivo - pois ele tentará executá-lo (não é executável como está - só quero a string com as variáveis interpoladas).
Alguma ideia?
eval
.eval
e é importante estar ciente das implicações.Respostas:
Tropecei no que penso ser a resposta a esta pergunta: o
envsubst
comando.Caso ainda não esteja disponível em sua distro, está no
Pacote GNU
gettext
.@Rockallite - Eu escrevi um pequeno script de wrapper para cuidar do problema '\ $'.
(Aliás, há um "recurso" de envsubst, explicado em https://unix.stackexchange.com/a/294400/7088 para expandir apenas algumas das variáveis na entrada, mas concordo que escapar das exceções é muito mais conveniente.)
Aqui está o meu script:
#! /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 "$@"
fonte
$
escape de cifrão ( ), por exemploecho '\$USER' | envsubst
, produzirá o nome de usuário atual com uma barra invertida como prefixo, não$USER
.brew link --force gettext
Muitas das respostas usando
eval
eecho
tipo de trabalho, mas quebram em várias coisas, como várias linhas, tentativa de escape de metacaracteres shell, escapes dentro do modelo que não se destina a ser expandido por bash, etc.Eu tive o mesmo problema e escrevi esta função shell, que pelo que eu posso dizer, lida com tudo corretamente. Isso ainda removerá apenas as novas linhas finais do modelo, por causa das regras de substituição de comandos do bash, mas nunca achei que isso fosse um problema, desde que tudo o mais permanecesse intacto.
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" }
Por exemplo, você pode usá-lo assim com um
parameters.cfg
que é realmente um script de shell que apenas define variáveis e umtemplate.txt
que é um modelo que usa essas variáveis:. parameters.cfg printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
Na prática, eu uso isso como uma espécie de sistema de template leve.
fonte
eval
armadilhas usuais . Tente adicionar; $(eval date)
no final de qualquer linha dentro do arquivo de entrada e ele executará odate
comando.podes tentar
echo $(eval echo $(cat something.txt))
fonte
echo -e "$(eval "echo -e \"`<something.txt`\"")"
se precisar que novas linhas sejam mantidas.template.txt
for um texto. Esta operação é perigosa porque executa (avalia) comandos se eles forem.Você não quer imprimir cada linha, você quer avaliá- la para que o Bash possa realizar substituições de variáveis.
FOO=42 while read; do eval echo "$REPLY" done < something.txt
Consulte
help eval
ou o manual do Bash para obter mais informações.fonte
eval
é perigoso ; você não apenas se$varname
expande (como fariaenvsubst
), mas$(rm -rf ~)
também.Outra abordagem (que parece nojenta, mas estou colocando aqui de qualquer maneira):
Grave o conteúdo de something.txt em um arquivo temporário, com uma instrução echo envolvida nele:
something=$(cat something.txt) echo "echo \"" > temp.out echo "$something" >> temp.out echo "\"" >> temp.out
em seguida, origine-o de volta em uma variável:
RESULT=$(source temp.out)
e $ RESULT terá tudo expandido. Mas parece tão errado!
fonte
Se você deseja apenas que as referências de variáveis sejam expandidas (objetivo que eu tinha para mim), você poderia fazer o seguinte.
contents="$(cat something.txt)" echo $(eval echo \"$contents\")
(As aspas escapadas em torno de $ contents são a chave aqui)
fonte
eval "echo \"$$contents\""
e ela preservou o espaço em brancoSolução seguinte:
permite a substituição de variáveis que são definidas
deixa os marcadores de posição de variáveis inalterados que não são definidos. Isso é especialmente útil durante implantações automatizadas.
suporta a substituição de variáveis nos seguintes formatos:
${var_NAME}
$var_NAME
relata quais variáveis não são definidas no ambiente e retorna o código de erro para tais casos
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
fonte
Se something.txt tiver apenas uma linha, um
bash
método (uma versão mais curta da resposta "icky" de Michael Neale ), usando substituição de processo e comando :FOO=42 . <(echo -e echo $(<something.txt))
Resultado:
Observe que
export
não é necessário.Se something.txt tem uma ou mais linhas, um GNU valuate método:
sed
e
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
fonte
sed
avaliação que melhor se adequava ao meu propósito. Eu precisava produzir scripts de modelos, que teriam valores preenchidos de variáveis exportadas em outro arquivo de configuração. Ele lida com o escape adequado para a expansão do comando, por exemplo, colocar\$(date)
no modelo para obter$(date)
a saída. Obrigado!$ 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.
fonte
foo=45 file=something.txt # in a file is written: Hello $foo world! eval echo $(cat $file)
fonte
envsubst
é uma ótima solução (veja a resposta de LenW) se o conteúdo que você está substituindo for de tamanho "razoável".No meu caso, precisei substituir o conteúdo de um arquivo para substituir o nome da variável.
envsubst
requer que o conteúdo seja exportado como variáveis de ambiente e o bash tem um problema ao exportar variáveis de ambiente com mais de um megabyte ou mais.solução awk
Usando a solução da cuonglm de uma questão diferente:
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
Esta solução funciona até para arquivos grandes.
fonte
O seguinte funciona:
bash -c "echo \"$(cat something.txt)"\"
fonte
$foo
, mas também inseguras$(rm -rf ~)
.