Pergunta

Eu quero canalizar a saída de um arquivo de "modelo" para o MySQL, o arquivo tendo variáveis ​​como ${dbName} intercalado.Qual é o utilitário de linha de comando para substituir essas instâncias e despejar a saída na saída padrão?

Foi útil?

Solução

Sed!

Dado template.txt:

The number is ${i}
The word is ${word}

só nos resta dizer:

sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt

Obrigado ao Jonathan Leffler pela dica para passar em múltiplos -e argumentos para o mesmo sed invocação.

Outras dicas

Atualizar

Aqui está uma solução de Yottatsa em uma pergunta semelhante que substitui apenas variáveis ​​​​como $VAR ou ${VAR} e é uma breve linha

i=32 word=foo envsubst < template.txt

Claro se eu e palavra estão em seu ambiente, então é só

envsubst < template.txt

No meu Mac parece que foi instalado como parte do obter texto e de MacGPG2

Resposta Antiga

Aqui está uma melhoria para a solução de mogsie em uma pergunta semelhante, minha solução não exige que você escale aspas duplas, como a de mogsie, mas a dele é de uma só linha!

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 é um caractere de escape aqui, mas você não precisa se preocupar se a análise tem um bug e ela executa várias linhas perfeitamente.

Usar /bin/sh.Crie um pequeno script de shell que defina as variáveis ​​e, em seguida, analise o modelo usando o próprio shell.Assim (edite para lidar com novas linhas corretamente):

Arquivo modelo.txt:

the number is ${i}
the word is ${word}

Arquivo 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"

Saída:

#sh script.sh
the number is 1
the word is dog

Estava pensando nisso novamente, dado o interesse recente, e acho que a ferramenta em que estava pensando originalmente era m4, o processador macro para ferramentas automáticas.Então, em vez da variável que especifiquei originalmente, você usaria:

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"

modelo.txt

Variable 1 value: ${var1}
Variable 2 value: ${var2}

dados.sh

#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"

analisador.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

arquivo_parsed.txt

Variable 1 value: value 1
Variable 2 value: value 2

aqui está minha solução com perl baseada na resposta anterior, substitui variáveis ​​de ambiente:

perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile

Aqui está um função Bash robusta que - apesar de usar eval - deve ser seguro de usar.

Todos ${varName} 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 são não encerrado em {...} (como $varName), nem substituições de comando ($(...) e sintaxe herdada `...`), nem substituições aritméticas ($((...)) e sintaxe herdada $[...]).

Para tratar um $ como literal, \-escapar disso;por exemplo.:\${HOME}

Observe que a entrada só é aceita 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 não 0x1, 0x2, 0x3, e 0x4 caracteres de controle estão presentes na entrada, porque esses caracteres.são usados ​​internamente - já que a função processa texto, essa deve ser uma suposição segura.

Se você estiver aberto a usar Perl, essa seria minha sugestão.Embora existam provavelmente alguns sed e/ou AWK especialistas 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 facilmente, mas também poderá colocá-lo em um script Perl padrão nesse ponto.

perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql

Um pequeno script Perl para fazer algo um pouco mais complicado (lidar com múltiplas 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 script de substituição, ele poderá ser usado da seguinte maneira:

replace-script < yourfile | mysql

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

arquivo.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

Eu sugeriria usar algo como Sigilo: https://github.com/gliderlabs/sigil

Ele é compilado em um único binário, por isso é extremamente fácil de instalar nos sistemas.

Então você pode fazer uma linha simples como a seguinte:

cat my-file.conf.template | sigil -p $(env) > my-file.conf

Isto é muito mais seguro do que eval e mais fácil do que usar regex ou sed

Encontrei este tópico enquanto me perguntava a mesma coisa.Isso me inspirou nisso (cuidado com os crases)

$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world

Muitas opções aqui, mas resolvi jogar a minha na pilha.É baseado em Perl, tem como alvo apenas variáveis ​​no formato ${...}, pega o arquivo para processar como um argumento e gera o arquivo convertido em stdout:

use Env;
Env::import();

while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }

print "$text";

É claro que não sou realmente uma pessoa perl, então pode facilmente haver uma falha fatal (embora funcione para mim).

Isso pode ser feito no próprio bash se você tiver controle do formato do arquivo de configuração.Você só precisa fornecer (".") o arquivo de configuração em vez de subdividi-lo.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ê poderia 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

Em seguida, canalize a saída do go.bash para o MySQL e pronto, espero que você não destrua seu banco de dados :-).

Aqui está uma maneira de fazer com que o shell faça a substituição para você, como se o conteúdo do arquivo fosse digitado entre aspas duplas.

Usando o exemplo de template.txt com conteúdo:

The number is ${i}
The word is ${word}

A linha a seguir fará com que o shell interpole o conteúdo de template.txt e grave o resultado na saída 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 com escopo para a execução de sh.
  • sh executa o conteúdo da string que é passada.
  • Strings escritas uma ao lado da outra tornam-se uma string, essa string é:
    • 'echo "' + "$(cat template.txt)" + '"'
  • Como a substituição é entre ", "$(cat template.txt)"torna-se a saída de cat template.txt.
  • Então o comando executado por sh -c torna-se:
    • echo "The number is ${i}\nThe word is ${word}",
    • onde i e word são as variáveis ​​de ambiente especificadas.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top