Domanda

Voglio convogliare l'output di un " template " file in MySQL, il file con variabili come $ {dbName} intervallate. Qual è l'utilità della riga di comando per sostituire queste istanze e scaricare l'output sullo standard output?

È stato utile?

Soluzione

Sed !

Dato template.txt:

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

non ci resta che dire:

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

Grazie a Jonathan Leffler per la segnalazione di passare più argomenti -e allo stesso invocazione sed .

Altri suggerimenti

Aggiornamento

Ecco una soluzione da yottatsa su una domanda simile che sostituisce solo variabili come $ VAR o $ {VAR } ed è una breve riga di testo

i=32 word=foo envsubst < template.txt

Naturalmente se i e parola sono nel tuo ambiente, allora è solo

envsubst < template.txt

Sul mio Mac sembra che sia stato installato come parte di gettext e da MacGPG2

Vecchia risposta

Ecco un miglioramento della soluzione da mogsie su una domanda simile, la mia soluzione non richiede che tu esegua l'escalation del doppio virgolette, fa mogsie, ma la sua è una fodera!

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

La potenza di queste due soluzioni è che si ottengono solo alcuni tipi di espansioni della shell che non si verificano normalmente $ ((...)), `...` e $ (...), sebbene backslash è un carattere di escape qui, ma non devi preoccuparti che l'analisi abbia un bug e fa più righe bene.

Usa / bin / sh . Creare uno script di shell piccolo che imposta le variabili, quindi analizzare il modello utilizzando la shell stessa. In questo modo (modifica per gestire correttamente le nuove righe):

File template.txt:

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

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

Output:

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

Ci stavo ripensando, visto il recente interesse, e penso che lo strumento a cui stavo inizialmente pensando fosse m4 , il processore macro per autotools. Quindi, invece della variabile che ho specificato in origine, useresti:

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

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

ecco la mia soluzione con perl basata sull'ex risposta, sostituisce le variabili d'ambiente:

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

Ecco una robusta funzione Bash che - nonostante l'uso di eval - dovrebbe essere sicura da usare.

Tutti i riferimenti alle variabili $ {varName} nel testo di input vengono espansi in base alle variabili della shell chiamante.

Nient'altro viene espanso: nessuno dei riferimenti di variabili i cui nomi sono non racchiuso in {...} (come $ varName ), né sostituzioni di comandi ( $ (...) e sintassi legacy `...` ), né sostituzioni aritmetiche ( $ ( (...)) e sintassi legacy $ [...] ).

Per trattare un $ come un letterale, \ -escape; per esempio:. \ $ {HOME}

Nota che l'input è accettato solo tramite stdin .

Esempio:

$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)

Codice sorgente funzione:

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 '`([

La funzione presuppone che nessun carattere di controllo 0x1 , 0x2 , 0x3 e 0x4 sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.

'\1\2\3\4') # ... then selectively reenable ${ references lineEscaped=${lineEscaped//

La funzione presuppone che nessun carattere di controllo 0x1 , 0x2 , 0x3 e 0x4 sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.

\4'{/\${} # Finally, escape embedded double quotes to preserve them. lineEscaped=${lineEscaped//\"/\\\"} eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([

La funzione presuppone che nessun carattere di controllo 0x1 , 0x2 , 0x3 e 0x4 sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.

done }

La funzione presuppone che nessun carattere di controllo 0x1 , 0x2 , 0x3 e 0x4 sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.

Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni sed e / o AWK esperti che probabilmente sanno come farlo molto più facilmente. Se hai una mappatura più complessa con più di un semplice dbName per i tuoi sostituti potresti estenderlo abbastanza facilmente, ma potresti anche metterlo in uno script Perl standard a quel punto.

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

Un breve script Perl per fare qualcosa di leggermente più complicato (gestire più chiavi):

#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{

Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni sed e / o AWK esperti che probabilmente sanno come farlo molto più facilmente. Se hai una mappatura più complessa con più di un semplice dbName per i tuoi sostituti potresti estenderlo abbastanza facilmente, ma potresti anche metterlo in uno script Perl standard a quel punto.

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

Un breve script Perl per fare qualcosa di leggermente più complicato (gestire più chiavi):

replace-script < yourfile | mysql

Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:

<*>\}/$replace{

Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni sed e / o AWK esperti che probabilmente sanno come farlo molto più facilmente. Se hai una mappatura più complessa con più di un semplice dbName per i tuoi sostituti potresti estenderlo abbastanza facilmente, ma potresti anche metterlo in uno script Perl standard a quel punto.

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

Un breve script Perl per fare qualcosa di leggermente più complicato (gestire più chiavi):

<*>

Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:

<*>}/g for keys %replace; print $buf;

Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:

<*>

Crea rendertemplate.sh :

#!/usr/bin/env bash

eval "echo \"$(cat $1)\""

E template.tmpl :

Hello, ${WORLD}
Goodbye, ${CHEESE}

Rendering del modello:

$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl 
Hello, Foo
Goodbye, Bar

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

Suggerirei di usare qualcosa come Sigil :      https://github.com/gliderlabs/sigil

È compilato su un singolo binario, quindi è estremamente facile da installare sui sistemi.

Quindi puoi fare un semplice one-liner come il seguente:

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

Questo è molto più sicuro di eval e più semplice dell'uso di regex o s<

Ho trovato questa discussione mentre mi chiedevo la stessa cosa. Mi ha ispirato a questo (attento con i backtick)

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

Molte scelte qui, ma ho pensato che avrei gettato il mio sul mucchio. È basato su perl, ha come target solo le variabili del modulo $ {...}, accetta il file da elaborare come argomento e genera il file convertito su stdout:

use Env;
Env::import();

while(<>) { 

Molte scelte qui, ma ho pensato che avrei gettato il mio sul mucchio. È basato su perl, ha come target solo le variabili del modulo $ {...}, accetta il file da elaborare come argomento e genera il file convertito su stdout:

<*>

Ovviamente non sono davvero una persona perl, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).

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

Molte scelte qui, ma ho pensato che avrei gettato il mio sul mucchio. È basato su perl, ha come target solo le variabili del modulo $ {...}, accetta il file da elaborare come argomento e genera il file convertito su stdout:

<*>

Ovviamente non sono davvero una persona perl, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).

; } print "$text";

Ovviamente non sono davvero una persona perl, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).

Può essere fatto in bash stesso se si ha il controllo del formato del file di configurazione. Devi solo procurarti (". & Quot;) il file di configurazione piuttosto che sotto-shell. Ciò garantisce che le variabili vengano create nel contesto della shell corrente (e continuino ad esistere) piuttosto che nella subshell (dove la variabile scompare quando la subshell esce).

$ 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 il tuo file di configurazione non può essere uno script di shell, puoi semplicemente 'compilarlo' prima di eseguirlo (la compilazione dipende dal tuo formato di input).

$ 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 '^

Nel tuo caso specifico, potresti usare qualcosa come:

$ 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

Quindi convoglia l'output di go.bash in MySQL e voilà, speriamo che non distruggerai il tuo database :-).

| 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

Nel tuo caso specifico, potresti usare qualcosa come:

<*>

Quindi convoglia l'output di go.bash in MySQL e voilà, speriamo che non distruggerai il tuo database :-).

Ecco un modo per fare in modo che la shell esegua la sostituzione, come se il contenuto del file fosse invece digitato tra virgolette doppie.

Utilizzando l'esempio di template.txt con i contenuti:

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

La riga seguente farà interpolare la shell del contenuto di template.txt e scriverà il risultato su standard out.

i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'

Spiegazione:

  • i e word vengono passati come variabili di ambiente ignorate nell'esecuzione di sh .
  • sh esegue il contenuto della stringa che viene passata.
  • Le stringhe scritte una accanto all'altra diventano una stringa, quella stringa è:
    • ' echo " ' + " $ (cat template.txt) " + ' " '
  • Poiché la sostituzione è compresa tra " , " $ (cat template.txt) " diventa l'output di cat template.txt .
  • Quindi il comando eseguito da sh -c diventa:
    • echo " Il numero è $ {i} \ nLa parola è $ {word} " ,
    • dove i e word sono le variabili di ambiente specificate.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top