bash with Como substituir espaços reservados ${} em um arquivo de texto?




remover espaços arquivo txt (12)

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

Todas as referências de variáveis ${varName} 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 legada `...` ), nem substituições aritméticas ( $((...)) e sintaxe legada $[...] ).

Para tratar um $ como um literal, \ -escape-lo; por exemplo: \${HOME}

Note 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 pressupõe que nenhum caractere de controle 0x1 , 0x2 , 0x3 e 0x4 esteja presente na entrada, porque esses caracteres. são usados ​​internamente - já que a função processa o texto , que deve ser uma suposição segura.

Eu quero canalizar a saída de um arquivo "template" para o MySQL, o arquivo com 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?


Isso pode ser feito no próprio bash se você tiver controle do formato do arquivo de configuração. Você só precisa fonte (".") O arquivo de configuração em vez de subshell. 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 a sub-camada 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 pode ser um shell script, você pode apenas '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 no MySQL e voila, esperançosamente você não destruirá seu banco de dados :-).


Eu estava pensando sobre isso novamente, dado o recente interesse, e acho que a ferramenta que eu estava pensando originalmente era m4 , o processador de macro para autotools. Então, em vez da variável especificada originalmente, você usaria:

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

Crie o 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

Eu sugiro usar algo como Sigil : https://github.com/gliderlabs/sigil

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

Então você pode fazer um simples one-liner como o seguinte:

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

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


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 do template.txt e grave o resultado como 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 scopped para a execução de sh .
  • sh executa o conteúdo da string que é passada.
  • Strings escritas próximas uma da outra se tornam 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 se torna:
    • echo "The number is ${i}\nThe word is ${word}" ,
    • onde i e word são as variáveis ​​de ambiente especificadas.

Eu encontrei este fio enquanto me perguntava a mesma coisa. Isso me inspirou nisso (cuidado com os backticks)

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

Muitas escolhas aqui, mas imaginei que eu lançaria o meu na pilha. Ele é baseado em perl, apenas direciona variáveis ​​do formulário $ {...}, pega o arquivo para processar como um argumento e exibe o arquivo convertido em 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 ser uma falha fatal (funciona para mim embora).


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

Atualizar

Aqui está uma solução do yottatsa sobre uma questão semelhante que apenas substitui variáveis ​​como $ VAR ou $ {VAR}, e é um breve one-liner

i=32 word=foo envsubst < template.txt

Claro que se eu e a palavra estão em seu ambiente, então é apenas

envsubst < template.txt

No meu Mac parece que foi instalado como parte do gettext e do MacGPG2

Resposta antiga

Aqui está uma melhoria para a solução de mogsie em uma questão semelhante, a minha solução não exige que você escale aspas duplas, mogsie faz, mas o seu é um forro!

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

O poder dessas duas soluções é que você só obtém alguns tipos de expansões de shell que não ocorrem normalmente $ ((...)), `...` e $ (...), embora a barra invertida seja uma escape de personagem aqui, mas você não precisa se preocupar que a análise tem um bug, e faz várias linhas muito bem.


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

Sed !

Dado template.txt:

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

nós apenas temos que dizer:

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

Obrigado a Jonathan Leffler pela dica para passar vários argumentos -e para a mesma invocação de sed .







templating