2013-10-25 15 views
5

Próbuję użyć sed do zamiany ciągów szablonów w plikach o formacie% XXX% o wartości zmiennej zwanej XXX w moim skrypcie powłoki.Używanie sed do zamiany łańcucha w pliku na zawartość zmiennej o tej nazwie

np. Następujące prace doskonale

sed "s/%user_home%/$user_home/gi" 

Więc jeśli user_home=fred następujące pozycje,

NameVirtualHost *:80 

<VirtualHost *:80> 
    ServerName %server_name% 

    ErrorLog /var/log/apache2/%user_home%_webapp_error.log 
    CustomLog /var/log/apache2/%user_home%_webapp.log common 

    DocumentRoot /home/%user_home%/web_app/public 
</VirtualHost> 

staje

NameVirtualHost *:80 

<VirtualHost *:80> 
    ServerName %server_name% 

    ErrorLog /var/log/apache2/fred_webapp_error.log 
    CustomLog /var/log/apache2/fred_webapp.log common 

    DocumentRoot /home/fred/web_app/public 
</VirtualHost> 

Problemem jest to, że chcę, aby uruchomić polecenie sed bez wyraźnego poznania sznurki szablonów i ich zmienne z góry. Oznacza to, że szuka% XXX%, a następnie zamienia ją na zawartość $ XXX, nie dbając o rzeczywistą nazwę zmiennej.

Wiem, że ma to coś wspólnego z odsyłaczami wstecznymi, ale nie mogę wymyślić, jak użyć zawartości odwołania zwrotnego jako nazwy zmiennej.

Próbowałem,

sed "s/%\([a-z_]\)%/$(\1)/gi" 

ale to nie działa, ponieważ wydaje się być patrząc na zmienną o nazwie $ \ 1.

+1

backticks nie będzie tam pracować. Zmienne są rozszerzane przez powłokę _ przed wysłaniem argumentu do 'sed'. – Barmar

+1

Perl byłby lepszy do tego. Może użyć '$ ENV {$ variable}', aby uzyskać zmienną środowiskową po ustawieniu zmiennej '$ zmienna' na łańcuch znaków pomiędzy'% '. – Barmar

Odpowiedz

0

Korzystanie awk można to zrobić

awk '{gsub(/%user_home%/,"${user_home}")}1' file 
NameVirtualHost *:80 

<VirtualHost *:80> 
    ServerName %server_name% 

    ErrorLog /var/log/apache2/${user_home}_webapp_error.log 
    CustomLog /var/log/apache2/${user_home}_webapp.log common 

    DocumentRoot /home/${user_home}/web_app/public 
</VirtualHost> 

Ten wymienić %user_home% zmiennej ${user_home}

3

Problem polega na tym, że w momencie komenda sed faktycznie uruchomić (a więc przez czas pobiera nazwę zmiennej), komenda sed musi być w pełni zmontowana (włączając podstawienie wartości zmiennej Bash do ciągu zastępczego); więc wszystko dzieje się w złej kolejności.

Problem polega na tym, że sed nie zna zmiennych Bash, więc potrzebujesz Basha, aby podać szczegóły zmiennych, ale Bash nie wie o zamianie sed, więc nie ma żadnego sposobu na poznanie jakich zmiennych potrzebujesz.

Poprawkę, o ile chcesz używać zmiennych Bash, należy użyć więcej Bash: musisz zidentyfikować odpowiednie nazwy zmiennych przed pierwszym wywołaniem sed. Poniższy przykład pokazuje, jak możesz to zrobić.


Aby uzyskać listę wszystkich zmiennych nazw w pliku, można napisać coś takiego:.

grep -o '%[a-z_][a-z_]*%' FILE | grep -o '[a-z_][a-z_]*' | sort -u 

(pierwszy grep pobiera wszystkie wyrażenia postaci %...% Drugi grep filtrów poza znakami procentu lub możesz użyć do tego celu sed. sort -u eliminuje duplikaty, ponieważ potrzebujesz tylko listy różnych nazw zmiennych o .)

Uzbrojony że można zebrać sed polecenia, które wykonuje wszystkie niezbędne zamienników:

sed_args=() 
while read varname ; do 
    sed_args+=(-e "s/%$varname%/${!varname}/g") 
done < <(grep -o '%[a-z_][a-z_]*%' FILE | grep -o '[a-z_][a-z_]*' | sort -u) 
sed "${sed_args[@]}" FILE 

(Zwróć uwagę na użycie ${!varname} oznacza „wziąć wartość $varname jako zmiennej nazwy, a zwraca wartość tej zmiennej: „to jest to, co nazywa«§3.5.3 "Shell Parameter Expansion" of the Bash Reference Manual ekspansję pośredniego»)

można owinąć to w funkcji:..

function replace_bash_variables() { 
    local file="$1" 
    local sed_args=() 
    local varname 
    while read varname ; do 
     sed_args+=(-e "s/%$varname%/${!varname}/g") 
    done < <(grep -o '%[a-z_][a-z_]*%' "$file" | grep -o '[a-z_][a-z_]*' | sort -u) 
    if [[ "${#sed_args[@]}" = 0 ]] ; then 
     # if no variables to replace, just cat the file: 
     cat -- "$file" 
    else 
     sed "${sed_args[@]}" -- "$file" 
    fi 
} 

replace_bash_variables OLD_FILE > NEW_FILE 

Można również dostosować powyższe czynności, aby przetwarzać wiersz po wierszu, aby nie trzeba było dwukrotnie czytać tego pliku. (To daje większą elastyczność, gdyż czyta plik dwukrotnie oznacza, że ​​trzeba przejść w rzeczywistym pliku, a może nie (powiedzmy) zastosować to do wyjścia rurociągu.)

+0

To jest świetne rozwiązanie i rozwiązuje problem, ale nie mogę go uruchomić tak, jak jest. Mogę go uruchomić tylko poprzez przekierowanie wyjścia grepa do pliku tymczasowego, a następnie przekierowanie tego pliku do pętli while. Uruchomienie powyższego kodu daje mi -bash: błąd składni w pobliżu nieoczekiwanego tokena '<(grep -o '% [a-z _] [a-z _] *%'" $ plik "| grep -o '[a-z_ ] [a-z _] * '| sort -u) ". – richard

+0

@reagleton: Ups, przepraszam, głupi błąd. Dla mojego [podstawiania procesów] (http://www.gnu.org/software/bash/manual/bashref.html#Process-Substitution) użyłem '<(...)', kiedy potrzebowałem napisać '<< (...) ". (Często popełniam ten błąd zawstydzająco. "(...)" naprawdę * wygląda * tak, jakby przekierowywał standardowe wejście, ale tak nie jest, po prostu zostaje zastąpione nazwą FIFO lub innym.) – ruakh

0

użyj:

sed -E "s/%(\w+)%/\$\1/g" 

na przykład tak:

echo "abcdef %variable% blah" | sed -E "s/%(\w+)%/\$\1/g" 

drukuje:

abcdef $variable blah 
0

spróbuj 1 sed ale trzeba wcześniej t O złapać „set” zawartość znać nazwy zmiennych i wartości

#!/bin/ksh 
# YourFilename contain the file name of your file to treat (here passed as 1st parameter to a script) 
YourFileName=$1 

(set | sed 's/.*/#V0r:&:r0V#/'; cat ${YourFileName}) | sed -n " 
s/$/²/ 
H 

$ { 
    x 
    s/^\(\n *\)*// 
# also reset t flag 
    t varxs 

:varxs 
    s/^#V0r:\([a-zA-Z0-9_]\{1,\}\)=\([^²]*\):r0V#²\(\n.*\)%\1%/#V0r:\1=\2:r0V#²\3\2/ 
    t varxs 
: tmpb 

# clean the line when no more occurance in text 
# s/^#V0r:\([a-zA-Z0-9_]\{1,\}\)=\([^²]*\):r0V#²\n// 
    s/^[^²]*:r0V#²\n// 

# and next 
    t varxs 


# clean the marker 
    s/²\(\n\)/\1/g 
    s/²$// 

# display the result 
    p 
    } 
" 
  • ograniczenie tutaj ze względu na wykorzystanie char „²” nie uciekł więc jeśli ² pojawiają się w pliku, może to być irytujące (więc to zmienić char jako marker lub przełożyć je w pliku)
  • # V0R: i: r0V # znacznik są również i mogą być zmieniane bez problemu
Powiązane problemy