Estou escrevendo um script para automatizar a criação de arquivos de configuração para Apache e PHP para meu próprio servidor web. Não quero usar GUIs como CPanel ou ISPConfig.
Eu tenho alguns modelos de arquivos de configuração Apache e PHP. O script Bash precisa ler modelos, fazer substituição de variáveis e gerar modelos analisados em alguma pasta. Qual o melhor jeito pra fazer isso? Eu posso pensar em várias maneiras. Qual é o melhor ou pode haver algumas maneiras melhores de fazer isso? Eu quero fazer isso no Bash puro (é fácil no PHP, por exemplo)
1) Como substituir espaços reservados $ {} em um arquivo de texto?
template.txt:
the number is ${i}
the word is ${word}
script.sh:
#!/bin/sh
#set variables
i=1
word="dog"
#read in template one line at the time, and replace variables
#(more natural (and efficient) way, thanks to Jonathan Leffler)
while read line
do
eval echo "$line"
done < "./template.txt"
BTW, como redireciono a saída para um arquivo externo aqui? Preciso escapar de algo se as variáveis contiverem, digamos, aspas?
2) Usando cat & sed para substituir cada variável pelo seu valor:
Dado template.txt:
The number is ${i}
The word is ${word}
Comando:
cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${word}/dog/"
Parece ruim para mim devido à necessidade de escapar de muitos símbolos diferentes e com muitas variáveis a linha será muito longa.
Você consegue pensar em outra solução elegante e segura?
fonte
Respostas:
Você pode usar isto:
para substituir todas as
${...}
seqüências de caracteres pelas variáveis de ambiente correspondentes (não esqueça de exportá-las antes de executar este script).Para o bash puro, isso deve funcionar (assumindo que as variáveis não contenham $ {...} strings):
. Solução que não trava se o RHS faz referência a alguma variável que faz referência a si mesma:
AVISO : Não conheço uma maneira de manipular corretamente as entradas com NULs no bash ou preservar a quantidade de novas linhas à direita. A última variante é apresentada como é porque os shells “amam” a entrada binária:
read
interpretará barras invertidas.read -r
não interpretará barras invertidas, mas ainda eliminará a última linha se não terminar com uma nova linha."$(…)"
irá retirar o máximo de novas linhas de arrasto, como não estão presentes, de modo que terminam…
com; echo -n a
e utilizaçãoecho -n "${line:0:-1}"
: este descarta o último carácter (que éa
) e conservas como muitas mudanças de linha de arrasto como havia na entrada (não incluindo).fonte
[^}]
para[A-Za-Z_][A-Za-z0-9_]
na versão bash para impedir que o shell fosse além da substituição estrita (por exemplo, se ele tentasse processar${some_unused_var-$(rm -rf $HOME)}
).$&
na solução perl""
: primeiro deixa${...}
intocado se não for substituído, depois o substitui por uma string vazia.while
loop ler a última linha, mesmo que não seja finalizada por uma nova linha, usewhile read -r line || [[ -n $line ]]; do
. Além disso, seuread
comando remove os espaços em branco iniciais e finais de cada linha; Para evitar isso, usewhile IFS= read -r line || [[ -n $line ]]; do
\
removendo-as).Experimentar
envsubst
fonte
envsubst
não é necessário ao usar um heredoc, pois o bash trata o heredoc como uma string literal entre aspas duplas e já interpola variáveis nele. É uma ótima opção quando você deseja ler o modelo de outro arquivo. Um bom substituto para os muito mais pesadosm4
.envsubst
é um utilitário gettext GNU e, na verdade, não é tão robusto (já que gettext é destinado à localização de mensagens humanas). Mais importante, ele não reconhece substituições $ {VAR} com escape de barra invertida (portanto, você não pode ter um modelo que use substituições $ VAR em tempo de execução, como um script de shell ou arquivo conf Nginx). Veja minha resposta para uma solução que lida com escapes de barra invertida.<<"EOF"
, o que não interpola variáveis (os terminadores citados são como aspas simples dos heredocs).cat template.txt | envsubst
envsubst era novo para mim. Fantástico.
Para o registro, usar um heredoc é uma ótima maneira de modelar um arquivo conf.
fonte
envsubst
coz ele salvou a minha do adicionalapt-get install gettext-base
na minha DockerfileConcordo com o uso do sed: é a melhor ferramenta para pesquisar / substituir. Aqui está a minha abordagem:
fonte
-i
opção (editar arquivos no local) que é semelhante à opção perl . Verifique a página de manual do seu sed .Eu acho que eval funciona muito bem. Ele lida com modelos com quebras de linha, espaços em branco e todos os tipos de itens do bash. Se você tem controle total sobre os modelos, é claro:
Este método deve ser usado com cuidado, é claro, pois eval pode executar código arbitrário. Executar isso como raiz está praticamente fora de questão. As aspas no modelo precisam ser escapadas, caso contrário elas serão consumidas
eval
.Você também pode usar aqui os documentos, se você preferir
cat
aecho
O @plockc provou uma solução que evita o problema de escape das citações do bash:
Edit: Removida parte sobre como executar isso como root usando sudo ...
Edit: Adicionado comentário sobre como as aspas precisam ser escapadas, adicionada a solução do plockc à mistura!
fonte
eval
edecho
/cat
comandos processa-los; tenteeval "echo \"'\$HOME'\""
.Eu tenho uma solução bash como mogsie, mas com heredoc em vez de herestring para permitir que você evite escapar aspas duplas
fonte
${param:?}
e texto de nidificação em torno de parâmetros opcionais. Exemplo:${DELAY:+<delay>$DELAY</delay>}
expande para nada quando DELAY está indefinido e <delay> 17 </delay> quando DELAY = 17._EOF_$$
.$trailing_newline
, ou usar$NL5
e garantir que ela seja expandida como 5 novas linhas.template.txt
iria trabalhar, a fim de preservar a novas linhas de fuga.eval
, setemplate.txt
contiverEOF
uma linha própria, ele encerrará prematuramente o documento aqui e, portanto, interromperá o comando. (Ponta do chapéu para @xebeche).Editar 6 de janeiro de 2017
Eu precisava manter aspas duplas no meu arquivo de configuração para que aspas duplas com escape duplo com sed ajude:
Não consigo pensar em seguir novas linhas, mas as linhas vazias são mantidas.
Embora seja um tópico antigo, a IMO descobri uma solução mais elegante aqui: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/
Todos os créditos para Grégory Pakosz .
fonte
eval "echo \"$(sed 's/\"/\\"/g' $1)\""
$variables
).Em vez de reinventar a roda, vá com o envsubst. Pode ser usado em praticamente qualquer cenário, por exemplo, criando arquivos de configuração a partir de variáveis de ambiente em contêineres do docker.
Se no mac, certifique-se de ter o homebrew , vincule-o no gettext:
./template.cfg
./.env:
./configure.sh
Agora basta usá-lo:
fonte
envsubst
realmente funciona.envsubst
não funciona em MacOS, você precisa instalá-lo usando homebrew:brew install gettext
.Uma versão mais longa mas mais robusta da resposta aceita:
Isso expande todas as instâncias
$VAR
ou${VAR}
para seus valores de ambiente (ou, se não estiverem definidos, a sequência vazia).Ele escapa adequadamente as barras invertidas e aceita um $ com escape da barra invertida para inibir a substituição (ao contrário do envsubst, que, ao que parece , não faz isso ).
Portanto, se seu ambiente é:
e seu modelo é:
o resultado seria:
Se você quiser apenas escapar barras invertidas antes de $ (você pode escrever "C: \ Windows \ System32" em um modelo inalterado), use esta versão ligeiramente modificada:
fonte
Eu teria feito dessa maneira, provavelmente menos eficiente, mas mais fácil de ler / manter.
fonte
sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
Se você deseja usar os modelos Jinja2 , consulte este projeto: j2cli .
Suporta:
fonte
Tomando a resposta do ZyX usando o bash puro, mas com a nova correspondência de regex de estilo e substituição indireta de parâmetros, ele se torna:
fonte
Se o uso de Perl for uma opção e você estiver satisfeito com expansões básicas apenas em variáveis de ambiente (em oposição a todas as variáveis de shell ), considere a resposta robusta de Stuart P. Bentley .
Esta resposta tem como objetivo fornecer uma solução somente para o bash que, apesar do uso,
eval
deve ser segura .Os objetivos são:
${name}
e$name
variáveis.$(...)
e sintaxe herdada`...`
)$((...))
e sintaxe herdada$[...]
).\
(\${name}
)."
e\
instâncias.Função
expandVars()
:Exemplos:
${HOME:0:10}
, desde que não contenham comandos incorporados ou substituições aritméticas, como${HOME:0:$(echo 10)}
$(
e`
instâncias são escapadas às cegas).${HOME
(fechamento ausente}
) BREAK a função.\$name
impede a expansão.\
não seguido por$
é preservado como está.\
instâncias adjacentes , deve dobrá-las ; por exemplo:\\
->\
- o mesmo que apenas\
\\\\
->\\
0x1
,0x2
,0x3
.eval
.Se você está procurando uma solução mais restritiva que ofereça suporte apenas a
${name}
expansões - por exemplo, com chaves obrigatórias , ignorando$name
referências - veja esta minha resposta .Aqui está uma versão aprimorada da
eval
solução somente para bash e livre da resposta aceita :As melhorias são:
${name}
e$name
variáveis.\
referências de variáveis em escape que não devem ser expandidas.eval
solução baseada acima,fonte
Aqui está outra solução pura do bash:
$ cat code
$ cat template
(com novas linhas à direita e aspas duplas)resultado
fonte
Aqui está outra solução: gere um script bash com todas as variáveis e o conteúdo do arquivo de modelo, esse script teria a seguinte aparência:
Se alimentarmos esse script no bash, ele produzirá a saída desejada:
Aqui está como gerar esse script e alimentá-lo no bash:
Discussão
cat
comando com HEREDOCSe você deseja redirecionar essa saída para um arquivo, substitua a última linha por:
fonte
Esta página descreve uma resposta com awk
fonte
Caso perfeito para shtpl . (projeto meu, por isso não é amplamente utilizado e carece de documentação. Mas aqui está a solução que ele oferece de qualquer maneira. Você pode testá-lo.)
Basta executar:
O resultado é:
Diverta-se.
fonte
Esta é a função bash pura ajustável ao seu gosto, usada na produção e não deve interferir em nenhuma entrada. Se quebrar - me avise.
fonte
Você também pode usar o bashible (que usa internamente a abordagem de avaliação descrita acima / abaixo).
Há um exemplo de como gerar um HTML a partir de várias partes:
https://github.com/mig1984/bashible/tree/master/examples/templates
fonte
Aqui está uma função bash que preserva os espaços em branco:
fonte
Aqui está um
perl
script modificado com base em algumas das outras respostas:Recursos (com base nas minhas necessidades, mas deve ser fácil de modificar):
fonte
Veja o script python de substituição de variáveis simples aqui: https://github.com/jeckep/vsubst
É muito simples de usar:
fonte
Minha humilde contribuição para esta maravilhosa pergunta.
Isso funciona basicamente usando o subshell para substituir envar, exceto que ele não usa eval e escapa citações simples e duplas explicitamente. Concatena as expressões var em uma única linha sem espaços para não confundir
sh
e depois passa o modelo paraecho
, permitindosh
manipular as substituições var. Ele preserva novas linhas, comentários, etc ... e você pode escapar\${like_this}
se não quiser que o var seja interpretado.${missing_var}
será substituído apenas pelo valor vazio.Muitas das outras respostas aqui são muito boas, mas eu queria algo muito simples e ele não precisa lidar literalmente com todos os casos possíveis dos casos de modelos que tenho atualmente.
Aproveitar!
fonte
Para acompanhar a resposta do plockc nesta página, aqui está uma versão adequada para traços, para aqueles que desejam evitar basismos.
fonte
Não pode ser mais simples do que:
$ person = Bob ./render template.txt> renderizado.txt
Consulte https://github.com/relaxdiego/renderest
fonte