Chcę przesłać dane wyjściowe pliku „szablonu” do MySQL, plik zawierający zmienne, takie jak ${dbName}
przeplatane. Jakie narzędzie wiersza poleceń zastępuje te wystąpienia i zrzuca dane wyjściowe na standardowe wyjście?
Chcę przesłać dane wyjściowe pliku „szablonu” do MySQL, plik zawierający zmienne, takie jak ${dbName}
przeplatane. Jakie narzędzie wiersza poleceń zastępuje te wystąpienia i zrzuca dane wyjściowe na standardowe wyjście?
Odpowiedzi:
Sed !
Podany plik template.txt:
Numer to $ {i} Słowo to $ {word}
musimy tylko powiedzieć:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Podziękowania dla Jonathana Lefflera za wskazówkę dotyczącą przekazywania wielu -e
argumentów do tego samego sed
wywołania.
cat
. Wszystko czego potrzebujesz to sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
oczekuje tekstu ucieczki, co jest kłopotliwe.
Oto rozwiązanie od yottatsy na podobne pytanie, które zastępuje tylko zmienne, takie jak $ VAR lub $ {VAR}, i jest krótkim, jednowierszowym
i=32 word=foo envsubst < template.txt
Oczywiście, jeśli ja i słowo są w twoim środowisku, to jest po prostu
envsubst < template.txt
Na moim Macu wygląda na to, że został zainstalowany jako część gettext i z MacGPG2
Oto ulepszenie rozwiązania z mogsie na podobne pytanie, moje rozwiązanie nie wymaga eskalowania podwójnych cudzysłowów, tak jak mogsie, ale jego jest jeden liniowiec!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Moc na tych dwóch rozwiązań jest to, że można dostać tylko kilka rodzajów rozszerzeń powłoki, które nie występują normalnie $ ((...)), `...` i $ (...), choć backslash jest tutaj znak ucieczki, ale nie musisz się martwić, że parsowanie ma błąd, a wiele linii jest w porządku.
envsubst
nie działa, jeśli twoje envars nie są eksportowane.
envsubst
jak sama nazwa wskazuje, rozpoznaje tylko zmienne środowiskowe , a nie zmienne powłoki . Warto również zauważyć, że envsubst
jest to narzędzie GNU , a zatem nie jest preinstalowane ani dostępne na wszystkich platformach.
Posługiwać się /bin/sh
. Utwórz mały skrypt powłoki, który ustawia zmienne, a następnie przeanalizuj szablon przy użyciu samej powłoki. Tak (edytuj, aby poprawnie obsługiwać znaki nowej linii):
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
polecenia wprowadzone na wejściu zostaną wykonane. Jeśli szablon to: "the words is; rm -rf $ HOME" stracisz pliki.
read
polecenie, jak napisano, przycina początkowe i końcowe białe spacje z każdej linii i ` \
` zjada '' znaki., (C) użyj tego tylko wtedy, gdy w pełni ufają lub kontrolują dane wejściowe, ponieważ podstawienia komend ( `…`
lub $(…)
) osadzone w danych wejściowych pozwalają na wykonywanie dowolnych poleceń z powodu użycia eval
. Wreszcie, istnieje niewielka szansa, że echo
początek wiersza pomylisz z jedną z opcji wiersza poleceń.
Myślałem o tym ponownie, biorąc pod uwagę niedawne zainteresowanie i myślę, że narzędziem, o którym pierwotnie myślałem, był m4
procesor makr dla automatycznych narzędzi. Więc zamiast zmiennej, którą pierwotnie określiłem, użyjesz:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
do tego prostego użycia zastępowania zmiennych / szablonów, jak wspomniano w innych odpowiedziach. m4
to świetne narzędzie, ale jest to pełnowartościowy preprocesor z dużo większą liczbą funkcji, a tym samym złożonością, która może nie być potrzebna, jeśli chcesz po prostu zastąpić niektóre zmienne.
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
`…`
lub $(…)
) osadzone w danych wejściowych pozwalają na wykonywanie dowolnych poleceń w wyniku użycia eval
i bezpośrednie wykonanie kodu powłoki w wyniku użycia source
. Ponadto podwójne cudzysłowy w danych wejściowych są po cichu odrzucane i echo
mogą pomylić początek wiersza z jedną z opcji wiersza poleceń.
Oto solidna funkcja Bash, która - pomimo użycia eval
- powinna być bezpieczna w użyciu.
Wszystkie ${varName}
odwołania do zmiennych w tekście wejściowym są interpretowane na podstawie zmiennych wywołującej powłoki.
Nic innego nie jest rozwijane: ani odwołania do zmiennych, których nazwy nie są zawarte w {...}
(takie jak $varName
), ani podstawienia poleceń ( $(...)
i starsza składnia `...`
), ani podstawienia arytmetyczne ( $((...))
i starsza składnia$[...]
).
Aby traktować a $
jako dosłowne, \
-urzuć go; na przykład:\${HOME}
Należy pamiętać, że dane wejściowe są akceptowane tylko przez stdin .
Przykład:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
Kod źródłowy funkcji:
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
}
Funkcja zakłada, że żadna 0x1
, 0x2
, 0x3
, i 0x4
znaki kontrolne są obecne w danych wejściowych, ponieważ te znaki. są używane wewnętrznie - ponieważ funkcja przetwarza tekst , powinno to być bezpieczne założenie.
eval
jest całkiem bezpieczny w użyciu.
"
poprawnie!)
${FOO:-bar}
lub wyprowadzić coś tylko wtedy, gdy jest ustawione - ${HOME+Home is ${HOME}}
. Podejrzewam, że z małym rozszerzeniem może również zwracać kody wyjścia dla brakujących zmiennych, ${FOO?Foo is missing}
ale obecnie tldp.org/LDP/abs/html/parameter-substitution.html ma ich listę, jeśli to pomoże
Utwórz rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Oraz template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Renderuj szablon:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
, uruchamiasz to jako kod.
eval "echo \"$(cat $1)\""
Działa świetnie !
oto moje rozwiązanie z perlem na podstawie poprzedniej odpowiedzi, zastępuje zmienne środowiskowe:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Jeśli jesteś otwarty na używanie Perla , to byłaby moja sugestia. Chociaż prawdopodobnie są niektórzy eksperci sed i / lub AWK, którzy prawdopodobnie wiedzą, jak to zrobić znacznie łatwiej. Jeśli masz bardziej złożone mapowanie z więcej niż tylko dbName dla swoich zamienników, możesz to dość łatwo rozszerzyć, ale równie dobrze możesz równie dobrze umieścić je w standardowym skrypcie Perla w tym momencie.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Krótki skrypt Perla, który robi coś bardziej skomplikowanego (obsługuje wiele kluczy):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Jeśli nazwiesz powyższy skrypt jako skrypt zastępujący, można go następnie użyć w następujący sposób:
replace-script < yourfile | mysql
Oto sposób, aby powłoka wykonała podstawienie za Ciebie, tak jakby zawartość pliku była zamiast tego wpisywana w cudzysłowy.
Na przykładzie template.txt z zawartością:
The number is ${i}
The word is ${word}
Następujący wiersz spowoduje, że powłoka interpoluje zawartość pliku template.txt i zapisuje wynik do standardowego wyjścia.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Wyjaśnienie:
i
i word
są przekazywane jako zmienne środowiskowe ograniczone do wykonania sh
.sh
wykonuje zawartość przekazanego ciągu.echo "
' + " $(cat template.txt)
" + ' "
'"
, " $(cat template.txt)
" staje się wynikiemcat template.txt
.sh -c
staje się:
echo "The number is ${i}\nThe word is ${word}"
,i
i word
są określonymi zmiennymi środowiskowymi.'$(rm -rf ~)'$(rm -rf ~)
dosłowne cudzysłowy w pliku szablonu będą pasowały do tych, które dodałeś przed jego rozwinięciem.
'$(echo a)'$(echo a)
. Produkuje 'a'a
. Najważniejsze, co się dzieje, jest to, że pierwszy element echo a
wewnątrz elementu '
jest oceniany, co może nie być tym, czego się spodziewasz, ponieważ jest w nim '
, ale zachowuje się tak samo, jak '
w przypadku umieszczania w "
cudzysłowie.
"
Chodzi o rozwinięcie czegokolwiek w cudzysłowie (włącznie $(...)
).
${varname}
inne rozszerzenia o wyższym ryzyku bezpieczeństwa, a nie inne rozszerzenia.
echo "
, po którym następuje podwójny cudzysłów z ciągiem literału template.txt
, po którym następuje kolejny ciąg literału "
, a wszystko to jest połączone w pojedynczy argument przekazywany do sh -c
. Masz rację, że '
nie można dopasować (ponieważ został zużyty przez zewnętrzną powłokę, a nie przekazany do wewnętrznej), ale z "
pewnością może, więc szablon zawierający Gotcha"; rm -rf ~; echo "
może zostać wykonany.
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
jako read
polecenia, w przeciwnym razie usuniesz początkowe i końcowe spacje z każdego wiersza wprowadzania. Ponadto echo
może pomylić początek wiersza z jedną z opcji wiersza poleceń, więc lepiej jest użyć printf '%s\n'
. Wreszcie, bezpieczniej jest podwójne cytowanie ${1}
.
Sugerowałbym użycie czegoś takiego jak Sigil : https://github.com/gliderlabs/sigil
Jest skompilowany do pojedynczego pliku binarnego, więc jest niezwykle łatwy do zainstalowania w systemach.
Następnie możesz wykonać prostą jedną linijkę, taką jak następujące:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Jest to znacznie bezpieczniejsze eval
i łatwiejsze niż użycie wyrażenia regularnego lubsed
cat
i używać <my-file.conf.template
zamiast tego, aby dać sigil
prawdziwy uchwyt pliku zamiast FIFO.
Znalazłem ten wątek, zastanawiając się nad tym samym. Zainspirowało mnie to do tego (uważaj na lewe napisy)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
$(< file)
eval echo "\"$(cat FILE)\""
ale może to nadal być niewystarczające, ponieważ podwójne cudzysłowy w danych wejściowych są odrzucane.
`…`
lub $(…)
) wbudowane w dane wejściowe pozwalają na wykonywanie dowolnych poleceń z powodu użycia eval
.
Sporo możliwości wyboru, ale pomyślałem, że rzucę swój na stos. Jest oparty na perlu, celuje tylko w zmienne w postaci $ {...}, pobiera plik do przetworzenia jako argument i wyprowadza przekonwertowany plik na standardowe wyjście:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Oczywiście nie jestem osobą perlową, więc łatwo może być fatalna wada (chociaż dla mnie działa).
Env::import();
linię - import jest implikowany przez use
. Sugeruję również, aby najpierw nie gromadzić całego wyjścia w pamięci: po prostu użyj print;
zamiast $text .= $_;
wewnątrz pętli i porzuć print
polecenie pętli .
Można to zrobić w samym bashu, jeśli masz kontrolę nad formatem pliku konfiguracyjnego. Musisz tylko pozyskać („.”) Plik konfiguracyjny, a nie podpowłokę. Zapewnia to, że zmienne są tworzone w kontekście bieżącej powłoki (i nadal istnieją), a nie w podpowłoce (gdzie zmienna znika po zamknięciu podpowłoki).
$ 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
Jeśli twój plik konfiguracyjny nie może być skryptem powłoki, możesz go po prostu „skompilować” przed wykonaniem (kompilacja zależy od formatu wejściowego).
$ 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
W swoim konkretnym przypadku możesz użyć czegoś takiego:
$ 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
Następnie prześlij wyjście go.bash do MySQL i voila, miejmy nadzieję, że nie zniszczysz swojej bazy danych :-).
go.bash
), Masz niewłaściwy koniec patyka - nie są one częścią rozwiązania, są tylko sposobem pokazania, że zmienne są ustawione prawidłowo.
Będziesz chciał czegoś solidniejszego niż obecne sugestie, ponieważ chociaż działają one w twoim (na razie) ograniczonym przypadku użycia, nie wystarczą w bardziej złożonych sytuacjach.
Potrzebujesz lepszego renderera. Potrzebujesz najlepszego renderera. Potrzebujesz Renderest!
Podany plik template.txt:
Cześć osobo}}!
Biegać:
$ person = Bob ./render template.txt
Zobaczysz wynik
Cześć Bob!
Zapisz go do pliku, przekierowując standardowe wyjście do pliku:
$ person = Bob ./render template.txt> rendering.txt
A jeśli zdarzy ci się renderować skrypt zawierający zmienne $ {}, których nie chcesz interpolować, The Renderest zapewni Ci ochronę bez konieczności robienia czegokolwiek innego!
Kontynuuj i zdobądź swoją kopię na https://github.com/relaxdiego/renderest