Eu quero canalizar a saída de um arquivo "template" para o MySQL, o arquivo tendo variáveis como ${dbName}
intercaladas. Qual é o utilitário de linha de comando para substituir essas instâncias e despejar a saída na saída padrão?
Eu quero canalizar a saída de um arquivo "template" para o MySQL, o arquivo tendo variáveis como ${dbName}
intercaladas. Qual é o utilitário de linha de comando para substituir essas instâncias e despejar a saída na saída padrão?
Respostas:
Sed !
Dado template.txt:
O número é $ {i} A palavra é $ {word}
nós apenas temos que dizer:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Agradecemos a Jonathan Leffler pela dica de passar vários -e
argumentos para a mesma sed
invocação.
cat
. Tudo que você precisa é sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
espera-se um texto escapado, que é um aborrecimento.
Aqui está uma solução da yottatsa em uma pergunta semelhante que substitui apenas variáveis como $ VAR ou $ {VAR} e é uma breve abordagem
i=32 word=foo envsubst < template.txt
É claro que se eu e o word estamos no seu ambiente, é apenas
envsubst < template.txt
No meu Mac, parece que ele foi instalado como parte do gettext e do MacGPG2
Aqui está uma melhoria para a solução do mogsie em uma pergunta semelhante: minha solução não exige que você escale aspas duplas, o mogsie faz, mas o dele é único!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
O poder dessas duas soluções é que você obtém apenas alguns tipos de expansões de shell que não ocorrem normalmente $ ((...)), `...` e $ (...), embora a barra invertida seja uma aqui, mas você não precisa se preocupar com o fato de a análise ter um bug e executar várias linhas.
envsubst
não funciona se seus envares não forem exportados.
envsubst
, como o nome sugere, reconhece apenas variáveis de ambiente , não variáveis de shell . Também vale a pena notar que envsubst
é um utilitário GNU e, portanto, não está pré-instalado ou disponível em todas as plataformas.
Use /bin/sh
. Crie um pequeno script de shell que defina as variáveis e analise o modelo usando o próprio shell. Assim (edite para lidar com as novas linhas corretamente):
the number is ${i}
the word is ${word}
#!/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"
#sh script.sh
the number is 1
the word is dog
bash
comando na entrada será executado. Se o modelo for: "as palavras são; rm -rf $ HOME", você perderá os arquivos.
read
comando, conforme escrito, apara os espaços em branco à esquerda e à direita de cada linha e 'come' \
caracteres., (C) use somente se você confie ou controle a entrada, porque as substituições de comandos ( `…`
ou $(…)
) incorporadas na entrada permitem a execução de comandos arbitrários devido ao uso de eval
. Finalmente, há uma pequena chance de echo
confundir o início de uma linha com uma de suas opções de linha de comando.
Eu estava pensando sobre isso de novo, dado o interesse recente, e acho que a ferramenta em que estava pensando originalmente era m4
o processador de macro para ferramentas automáticas. Então, em vez da variável especificada originalmente, você usaria:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
para esse uso simples de substituição / modelagem de variáveis, conforme mencionado em outras respostas. m4
é uma ótima ferramenta, mas é um pré-processador completo com muito mais recursos e, portanto, complexidade que pode não ser necessária se você simplesmente quiser substituir algumas variáveis.
template.txt
Variable 1 value: ${var1}
Variable 2 value: ${var2}
data.sh
#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"
parser.sh
#!/usr/bin/env bash
# args
declare file_data=$1
declare file_input=$2
declare file_output=$3
source $file_data
eval "echo \"$(< $file_input)\"" > $file_output
./parser.sh data.sh template.txt parsed_file.txt
parsed_file.txt
Variable 1 value: value 1
Variable 2 value: value 2
`…`
ou$(…)
) incorporadas na entrada permitem a execução de comandos arbitrários devido ao uso de eval
e a execução direta do código do shell devido ao uso de source
. Além disso, as aspas duplas na entrada são descartadas silenciosamente e echo
podem confundir o início de uma linha com uma de suas opções de linha de comando.
Aqui está uma função Bash robusta que, apesar de ser usada, eval
deve ser segura.
Todas ${varName}
as referências de variáveis no texto de entrada são expandidas com base nas variáveis do shell de chamada.
Nada mais é expandido: nem referências de variáveis cujos nomes não estão entre {...}
(como $varName
), nem substituições de comandos ( $(...)
e sintaxe herdada `...`
), nem substituições aritméticas ( $((...))
e sintaxe herdada $[...]
).
Para tratar a $
como um literal, \
escape-o; por exemplo:\${HOME}
Observe que a entrada é aceita apenas via stdin .
Exemplo:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
Código fonte da função:
expandVarsStrict(){
local line lineEscaped
while IFS= read -r line || [[ -n $line ]]; do # the `||` clause ensures that the last line is read even if it doesn't end with \n
# Escape ALL chars. that could trigger an expansion..
IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4')
# ... then selectively reenable ${ references
lineEscaped=${lineEscaped//$'\4'{/\${}
# Finally, escape embedded double quotes to preserve them.
lineEscaped=${lineEscaped//\"/\\\"}
eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$'
done
}
A função assume que nenhuma 0x1
, 0x2
, 0x3
, e0x4
caracteres de controlo estão presentes na entrada, porque esses caracteres. são usados internamente - como a função processa o texto , isso deve ser uma suposição segura.
eval
, é bastante seguro.
"
corretamente!)
${FOO:-bar}
ou apenas produza algo se estiver definido - ${HOME+Home is ${HOME}}
. Eu suspeito que com um pouco de extensão também pode retornar códigos de saída para variáveis faltando ${FOO?Foo is missing}
, mas atualmente não tldp.org/LDP/abs/html/parameter-substitution.html tem uma lista de estes se isso ajuda
Criar rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
E template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Renderize o modelo:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
, você o executará como código.
eval "echo \"$(cat $1)\""
Funciona bem !
aqui está minha solução com perl com base na resposta anterior, substitui as variáveis de ambiente:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Se você está aberto a usar Perl , essa seria minha sugestão. Embora haja provavelmente alguns especialistas em sed e / ou AWK que provavelmente sabem como fazer isso com muito mais facilidade. Se você tiver um mapeamento mais complexo com mais do que apenas dbName para suas substituições, poderá estendê-lo com bastante facilidade, mas é melhor colocá-lo em um script Perl padrão nesse momento.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Um script Perl curto para fazer algo um pouco mais complicado (manipular várias chaves):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Se você nomear o script acima como substituir-script, ele poderá ser usado da seguinte maneira:
replace-script < yourfile | mysql
Aqui está uma maneira de fazer com que o shell substitua você, como se o conteúdo do arquivo fosse digitado entre aspas duplas.
Usando o exemplo de template.txt com o conteúdo:
The number is ${i}
The word is ${word}
A linha a seguir fará com que o shell interpole o conteúdo do template.txt e grave o resultado no padrão.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Explicação:
i
e word
são passados como variáveis de ambiente incluídas na execução de sh
.sh
executa o conteúdo da cadeia de caracteres pela qual é passada.echo "
' + " $(cat template.txt)
" + ' "
'"
, " $(cat template.txt)
" se torna a saída de cat template.txt
.sh -c
se torna:
echo "The number is ${i}\nThe word is ${word}"
,i
e word
são as variáveis de ambiente especificadas.'$(rm -rf ~)'$(rm -rf ~)
as aspas literais no arquivo do modelo corresponderão às que você adicionou antes da expansão.
'$(echo a)'$(echo a)
. Produz 'a'a
. A principal coisa que está acontecendo é que a primeira echo a
dentro da '
está sendo avaliada, o que pode não ser o que você espera desde que está presente '
, mas é o mesmo comportamento de incluir '
em uma "
sequência de caracteres citada.
"
string entre aspas (incluindo $(...)
) é o ponto.
${varname}
, e não outras, expansões de alto risco à segurança.
echo "
, seguida por uma string de aspas duplas com os contornos literais de template.txt
, seguida por outra string literal "
, todas concatenadas em um único argumento passado para sh -c
. Você está certo que '
não pode ser correspondido (já que foi consumido pelo shell externo em vez de passado para o interno), mas "
certamente pode, portanto, um modelo contendo Gotcha"; rm -rf ~; echo "
pode ser executado.
file.tpl:
The following bash function should only replace ${var1} syntax and ignore
other shell special chars such as `backticks` or $var2 or "double quotes".
If I have missed anything - let me know.
script.sh:
template(){
# usage: template file.tpl
while read -r line ; do
line=${line//\"/\\\"}
line=${line//\`/\\\`}
line=${line//\$/\\\$}
line=${line//\\\${/\${}
eval "echo \"$line\"";
done < ${1}
}
var1="*replaced*"
var2="*not replaced*"
template file.tpl > result.txt
\$(date)
while IFS= read -r line; do
como read
comando, caso contrário, você removerá os espaços em branco à esquerda e à direita de cada linha de entrada. Além disso, echo
pode confundir o início de uma linha com uma de suas opções de linha de comando, por isso é melhor usá-lo printf '%s\n'
. Finalmente, é mais seguro fazer aspas duplas ${1}
.
Sugiro usar algo como Sigil : https://github.com/gliderlabs/sigil
Ele é compilado em um único binário, por isso é extremamente fácil de instalar nos sistemas.
Em seguida, você pode fazer uma linha simples como a seguinte:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Isso é muito mais seguro eval
e fácil do que usar regex oused
cat
e usar <my-file.conf.template
para fornecer sigil
um identificador de arquivo real em vez de um FIFO.
Eu encontrei este tópico enquanto me perguntava a mesma coisa. Isso me inspirou a isso (cuidado com os bastidores)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
is$(< file)
eval echo "\"$(cat FILE)\""
mas isso ainda pode não ser suficiente, pois as aspas duplas na entrada são descartadas.
`…`
ou $(…)
) incorporadas na entrada permitem a execução de comandos arbitrários devido ao uso de eval
.
Muitas opções aqui, mas achei que eu jogaria o meu na pilha. Ele é baseado em perl, segmenta apenas variáveis do formato $ {...}, pega o arquivo para processar como argumento e gera o arquivo convertido no stdout:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Claro que eu não sou realmente uma pessoa perl, então poderia facilmente haver uma falha fatal (funciona para mim).
Env::import();
linha - a importação está implícita use
. Além disso, sugiro não construir a saída inteira na memória primeiro: basta usar em print;
vez de $text .= $_;
dentro do loop e soltar o print
comando pós-loop .
Isso pode ser feito no próprio bash se você tiver controle do formato do arquivo de configuração. Você só precisa originar (".") O arquivo de configuração, em vez de colocá-lo em um subconjunto. Isso garante que as variáveis sejam criadas no contexto do shell atual (e continuem a existir) em vez do subshell (onde a variável desaparece quando o subshell sai).
$ cat config.data
export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA
export parm_user=pax
export parm_pwd=never_you_mind
$ cat go.bash
. config.data
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
Se o seu arquivo de configuração não puder ser um script de shell, você pode simplesmente 'compilá-lo' antes de executá-lo (a compilação depende do seu formato de entrada).
$ cat config.data
parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL
parm_user=pax # user name
parm_pwd=never_you_mind # password
$ cat go.bash
cat config.data
| sed 's/#.*$//'
| sed 's/[ \t]*$//'
| sed 's/^[ \t]*//'
| grep -v '^$'
| sed 's/^/export '
>config.data-compiled
. config.data-compiled
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
No seu caso específico, você pode usar algo como:
$ cat config.data
export p_p1=val1
export p_p2=val2
$ cat go.bash
. ./config.data
echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1"
$ bash go.bash
select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1
Então canalize a saída do go.bash no MySQL e pronto, espero que você não destrua seu banco de dados :-).
go.bash
), você tem a ponta errada - eles não fazem parte da solução, são apenas uma maneira de mostrar que as variáveis estão sendo definido corretamente.