Jak podstawiać zmienne powłoki w złożonych plikach tekstowych


89

Mam kilka plików tekstowych, w których wprowadziłem zmienne powłoki (na przykład $ VAR1 lub $ VAR2).

Chciałbym wziąć te pliki (jeden po drugim) i zapisać je w nowych plikach, gdzie wszystkie zmienne zostałyby zastąpione.

Aby to zrobić, użyłem następującego skryptu powłoki (znalezionego w StackOverflow):

while read line
do
    eval echo "$line" >> destination.txt
done < "source.txt"

Działa to bardzo dobrze w przypadku bardzo podstawowych plików.

Jednak w przypadku bardziej złożonych plików polecenie „eval” robi za dużo:

  • Wiersze zaczynające się od „#” są pomijane

  • Analiza plików XML skutkuje mnóstwem błędów

Czy jest lepszy sposób, aby to zrobić? (w skrypcie powłoki ... wiem, że można to łatwo zrobić na przykład za pomocą Anta)

Z poważaniem

Odpowiedzi:


206

Patrząc, okazuje się, że w moim systemie jest envsubstpolecenie, które jest częścią pakietu gettext-base.

Więc to ułatwia:

envsubst < "source.txt" > "destination.txt"

Uwaga, jeśli chcesz używać tego samego pliku dla obu, będziesz musiał użyć czegoś podobnego moreutil użytkownika sponge, jak sugeruje Johnny Utahh: envsubst < "source.txt" | sponge "source.txt". (Ponieważ przekierowanie powłoki w przeciwnym razie opróżni plik przed jego odczytaniem).


4
Świetny! Ale działa tylko w przypadku zmiennych środowiskowych. Jak sprawić, by działało ze zmiennymi zadeklarowanymi w moim skrypcie .sh?
— Ben

12
@Ben: użyj 'export' (przed wywołaniem envsubst) dla każdej zmiennej, której chcesz użyć w envsubst
— tlo

9
envsubstjest częścią GNU gettext
— Andy

3
@user_mda, gdzie trafiają dane wyjściowe.
— derobert

4
Ostrzeżenie: jeśli source.txtzawiera jakiekolwiek $znaki spoza rozwijania zmiennych, envsubstrównież je zamieni i nie ma sposobu na uniknięcie $. Typowe (brzydkie) obejście to export DOLLAR="$".
— Kos

67

W odniesieniu do odpowiedzi 2, omawiając envsubst, zapytałeś:

Jak sprawić, by działało ze zmiennymi zadeklarowanymi w moim skrypcie .sh?

Odpowiedź brzmi: po prostu musisz wyeksportować swoje zmienne przed wywołaniem envsubst.

Możesz również ograniczyć ciągi zmiennych, które chcesz zamienić w danych wejściowych, używając envsubst SHELL_FORMATargumentu (unikając niezamierzonego zastąpienia łańcucha w wejściu wspólną wartością zmiennej powłoki - np $HOME.).

Na przykład:

export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'

envsubst "$MYVARS" <source.txt >destination.txt

Zastąpi wszystkie wystąpienia $VAR1i $VAR2(i tylko VAR1a VAR2) w source.txtz 'somevalue'i 'someothervalue'odpowiednio.


15
Pojedyncze cudzysłowy, 'które są używane do ustawiania, MYVARSsą kluczowe
— Mark Lakata

Zauważ, że export, envsubstlub jakiekolwiek polecenie przeplotu może się nie powieść, gdy tekst do zastąpienia jest duży. Odniesienie.
— biskup

@ram to dlatego, że pominięcie SHELL_FORMATargumentu (tj. "$MYVARS") powoduje zastąpienie wszystkich eksportowanych zmiennych. Jeśli tego potrzebujesz, nie martw się.
— Thiago Figueiro

14

Wiem, że ten temat jest stary, ale mam prostsze działające rozwiązanie bez eksportu zmiennych. Może być onelinerem, ale ja wolę rozdzielać \na końcu linii.

var1='myVar1'\
var2=2\
var3=${var1}\
envsubst '$var1,$var3' < "source.txt" > "destination.txt"

#         ^^^^^^^^^^^    ^^^^^^^^^^     ^^^^^^^^^^^^^^^
# define which to replace   input            output

Zmienne muszą być zdefiniowane w tej samej linii, w jakiej envsubstmają być traktowane jako zmienne środowiskowe.

'$var1,$var3'Jest opcjonalny tylko wymienić te wyszczególnione. Wyobraź sobie plik wejściowy zawierający, ${VARIABLE_USED_BY_JENKINS}którego nie należy zastępować.


8
  1. Zdefiniuj zmienną ENV
$ export MY_ENV_VAR=congratulation
  1. Utwórz plik szablonu ( in.txt ) z następującą zawartością
$MY_ENV_VAR

Możesz także użyć wszystkich innych zmiennych ENV zdefiniowanych przez Twój system, takich jak (w systemie Linux) $ TERM, $ SHELL, $ HOME ...

  1. Uruchom to polecenie, aby umieścić wszystkie zmienne env w pliku in.txt i zapisać wynik do out.txt
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
  1. Sprawdź zawartość pliku out.txt
$ cat out.txt

i powinieneś zobaczyć „gratulacje”.


0

Jeśli naprawdę chcesz używać tylko basha (i seda), przejdę przez każdą z twoich zmiennych środowiskowych (zwróconych setw trybie posix) i zbuduję z tego kilka -e 'regex'dla seda, zakończonych przez-e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g' , a następnie przekażę to wszystko do sed.

Perl wykonałby jednak lepszą robotę, masz dostęp do zmiennych środowiskowych jako tablicę i możesz wykonywać zastępcze pliki wykonywalne, więc każdą zmienną środowiskową dopasowujesz tylko raz.


dlaczego ktoś miałby zignorować taką odpowiedź ??? -perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' "$main_tf_file"
— Yordan Georgiev

0

Właściwie musisz zmienić readna, read -rktóry spowoduje, że będzie ignorował odwrotne ukośniki.

Powinieneś także unikać cytatów i odwrotnych ukośników. Więc

while read -r line; do
  line="${line//\\/\\\\}"
  line="${line//\"/\\\"}"
  line="${line//\`/\\\`}"
  eval echo "\"$line\""
done > destination.txt < source.txt

Wciąż jednak straszny sposób na ekspansję.


Rzeczywiście, jest to ryzyko podczas wykonywania „eval” na pliku, który może zawierać zły kod
— Ben,

0

Wyeksportuj wszystkie potrzebne zmienne, a następnie użyj onlinera perla

TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $& #ge;')

Spowoduje to zastąpienie wszystkich zmiennych ENV obecnych w TEKST wartościami rzeczywistymi. Cytaty również są zachowane :)


0

Jeśli chcesz, aby zmienne env zostały zastąpione w plikach źródłowych, zachowując wszystkie zmienne inne niż env bez zmian, możesz użyć następującego polecenia:

envsubst "$(printf '${%s} ' $(env | sed 's/=.*//'))" < source.txt > destination.txt

W tym miejscu wyjaśniono składnię zastępowania tylko określonych zmiennych . Powyższe polecenie używa podpowłoki do wyświetlenia wszystkich zdefiniowanych zmiennych, a następnie przekazuje je doenvsubst

Więc jeśli istnieje zdefiniowana zmienna env o nazwie $NAME, a Twój source.txtplik wygląda następująco:

Hello $NAME
Your balance is 123 ($USD)

destination.txtBędą:

Hello Arik
Your balance is 123 ($USD)

Zauważ, że $NAMEzostał zastąpiony i $USDpozostawiony nietknięty


0

Wywołaj plik binarny Perla, w trybie wyszukiwania i zamiany na wiersz (the -pi), uruchamiając kod Perla (the -e) w pojedynczych cudzysłowach, który iteruje po kluczach specjalnego %ENVskrótu zawierającego wyeksportowane nazwy zmiennych jako klucze i wyeksportowane wartości zmiennych jako wartości kluczy i dla każdej iteracji po prostu zamień ciąg zawierający a na $<<key>>jego <<value>>.

 perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file

Uwaga: dodatkowa obsługa logiki jest wymagana w przypadkach, w których dwie lub więcej zmiennych zaczyna się od tego samego ciągu ...


-1

envsubstwygląda na coś, czego chciałem użyć, ale -vopcja trochę mnie zaskoczyła.

Chociaż envsubst < template.txtdziałał dobrze, to samo z opcją -vnie działało:

$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.1 (Maipo)
$ envsubst -V
envsubst (GNU gettext-runtime) 0.18.2
Copyright (C) 2003-2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Bruno Haible.

Jak pisałem, to nie działało:

$ envsubst -v < template.txt
envsubst: missing arguments
$ cat template.txt | envsubst -v
envsubst: missing arguments

Musiałem to zrobić, aby działało:

TEXT=`cat template.txt`; envsubst -v "$TEXT"

Może to komuś pomoże.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.