Pracuję nad skryptem powłoki, który konstruuje złożone polecenie ze zmiennych, np. Takich jak ten (z techniką, której nauczyłem się z Bash FAQ ):
#!/bin/bash
SOME_ARG="abc"
ANOTHER_ARG="def"
some_complex_command \
${SOME_ARG:+--do-something "$SOME_ARG"} \
${ANOTHER_ARG:+--with "$ANOTHER_ARG"}
Ten skrypt dynamicznie dodaje parametry --do-something "$SOME_ARG"
i --with "$ANOTHER_ARG"
aby some_complex_command
, jeśli te zmienne są zdefiniowane. Jak dotąd działa to dobrze.
Ale teraz chcę też móc wydrukować lub zalogować polecenie, gdy je uruchamiam, na przykład gdy mój skrypt jest uruchomiony w trybie debugowania. Więc kiedy mój skrypt działa some_complex_command --do-something abc --with def
, chcę również mieć to polecenie w zmiennej, aby np. Móc zalogować się do syslog.
Bash FAQ pokazuje technikę wykorzystania DEBUG
pułapki i $BASH_COMMAND
zmiennej (na przykład do celów debugowania) w tym celu. Próbowałem tego z następującym kodem:
#!/bin/bash
ARG="test string"
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"
echo "Command was: ${COMMAND}"
To działa, ale nie rozwija zmiennych w poleceniu:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Myślę, że muszę użyć eval, by rozwinąć się echo "$ARG"
do echo test string
(przynajmniej nie znalazłem sposobu bez którego eval
). Działa to:
eval echo "Command was: ${COMMAND}"
Daje następujące dane wyjściowe:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string
Ale nie jestem pewien, czy mogę eval
bezpiecznie używać tego w ten sposób. Bezskutecznie próbowałem wykorzystać niektóre rzeczy:
#!/bin/bash
ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER
echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"
Wygląda na to, że dobrze sobie z tym radzi, ale jestem ciekawy, czy ktoś widzi problem, który mi umknął.
Odpowiedzi:
Jedną z możliwości jest utworzenie funkcji otoki, która jednocześnie wydrukuje polecenie i wykona je w następujący sposób:
Aby w skrypcie można wykonać:
Nie musisz majstrować przy pułapce. Wadą jest to, że dodaje kilka
debug
słów kluczowych w całym kodzie (ale to powinno być w porządku, często takie rzeczy, jak aserty itp.).Możesz nawet dodać zmienną globalną
DEBUG
i zmodyfikowaćdebug
funkcję w następujący sposób:Następnie możesz wywołać skrypt jako:
lub
Lub tylko
w zależności od tego, czy chcesz mieć informacje debugowania, czy nie.
Napisałem wielką
DEBUG
literę, ponieważ należy ją traktować jako zmienną środowiskową.DEBUG
to trywialna i popularna nazwa, więc może kolidować z innymi poleceniami. Może nazwaćGNIOURF_DEBUG
lubMARTIN_VON_WITTICH_DEBUG
lubUNICORN_DEBUG
jeśli lubisz jednorożce (i to prawdopodobnie jak kucyki zbyt).Uwaga. W
debug
funkcji ostrożnie sformatowałem każdy argument za pomocąprintf '%q'
, aby dane wyjściowe były poprawnie zastępowane i cytowane, tak aby były wielokrotnego użytku dosłownie z bezpośrednim kopiowaniem i wklejaniem. Pokaże także dokładnie to, co zobaczyła pocisk, ponieważ będziesz w stanie zrozumieć każdy argument (w przypadku spacji lub innych śmiesznych symboli). Ta funkcja używa również bezpośredniego przypisania z-v
przełącznikiemprintf
, aby uniknąć niepotrzebnych podpowłok.źródło
&
. Musiałem przenieść je do funkcji, aby działało - niezbyt fajnie, ale nie sądzę, że jest lepszy sposób. Ale nic więcejeval
, więc muszę to zrobić, co jest miłe :)eval "$BASH_COMMAND"
uruchamia polecenieprintf '%s\n' "$BASH_COMMAND"
wypisuje dokładnie określone polecenie plus nowy wiersz.Jeśli polecenie zawiera zmienne (tj. Jeśli jest coś podobnego
cat "$foo"
), wówczas wydrukowanie polecenia powoduje wydrukowanie tekstu zmiennej. Niemożliwe jest wydrukowanie wartości zmiennej bez wykonania polecenia - pomyśl o takich poleceniachvariable=$(some_function) other_variable=$variable
.Najprostszym sposobem na uzyskanie śladu po uruchomieniu skryptu powłoki jest ustawienie
xtrace
opcji powłoki przez uruchomienie skryptu jakobash -x /path/to/script
lub wywołanieset -x
wewnątrz powłoki. Śledzenie jest drukowane z błędem standardowym.źródło
xtrace
, ale nie daje mi to zbyt dużej kontroli. Próbowałem „set -x; ...; set + x”, ale: 1) wypisano też polecenie „set + x” w celu wyłączenia xtrace 2) Nie mogę poprzedzić wyjścia np. Znacznikiem czasu 3) Mogę zaloguj się do syslog.BASH_COMMAND
która zawiera rozwiniętą komendę, ponieważ w pewnym momencie musi wykonać ekspansję zmiennej na komendzie podczas jej wykonywania :)