Napisałem bibliotekę rejestrowania bash, która ma być implementowana za pomocą złożonych skryptów, z których obecnie korzysta moja firma. Zaniepokoiłem się dostarczaniem nazwy skryptu ($ {BASH_SOURCE}) i numerem linii ($ {LINENO}) skryptu wywołującego podczas wykonywania wywołań dziennika. Jednak nie chciałem polegać na użytkowniku lub implementowaniu skryptu, aby przekazać te dwie zmienne jako parametry. Jeśli był to C/C++, po prostu utworzyłbym makro, które dodaje "__FILE__" i "__LINE__" do listy parametrów.Cytaty parametrów Bash i eval
W końcu udało mi się uruchomić tę część. Oto niektóre znacznie uproszczone streszczenia jako dowód pojęcia:
oto biblioteka rejestrowania:
# log.sh
LOG="eval _log \${BASH_SOURCE} \${LINENO}"
_log() {
_BASH_SOURCE=`basename "${1}"` && shift
_LINENO=${1} && shift
echo "(${_BASH_SOURCE}:${_LINENO}) [email protected]"
}
i wdrażaniu skrypt testowy:
# MyTest.sh
. ./log.sh
${LOG} "This is a log message"
# (test.sh:5) This is a log message
Działa to całkiem dobrze (i ja był zachwycony, że początkowo działał). Ma to jednak jeden rażący problem: interakcje między cytatami i ewaluacją. Jeśli zadzwonię:
${LOG} "I'm thrilled that I got this working"
# ./test.sh: eval: line 5: unexected EOF while looking for matching `''
# ./test.sh: eval: line 6: syntax error: unexpected end of file
Teraz wierzę, że rozumiem, dlaczego tak się dzieje. Przytoczone parametry pozostają nienaruszone, gdy są przekazywane do eval, ale w tym momencie zawartość jest umieszczana jako wynik w wynikowym łańcuchu poleceń. Wiem, że mogę to naprawić, robiąc trochę ucieczki; jednak nie chcę wymuszać na skryptach implementacji, aby to zrobić. Zanim zaimplementowałem tę funkcję "eval macro", kazałem użytkownikom nawiązywać połączenia bezpośrednio do "_log" i pozwalałem im opcjonalnie przekazywać "$ {LINENO}". Dzięki tej implementacji, niepowodzenie połączenia powyżej (tylko z cytowanym zdaniem) działało dobrze.
Na najbardziej podstawowym poziomie, wszystko, czego naprawdę chcę, to skrypt, aby móc wywołać [funkcja/makra dziennika] "Ciąg do logowania ze specjalnymi znakami" i wynikowy komunikat dziennika zawiera nazwę pliku i linię numer skryptu wywołującego, a następnie komunikat dziennika. Jeśli jest to możliwe, zakładam, że jestem bardzo blisko, ale jeśli jest coś, co przeoczyłem, wymagałoby innego podejścia, jestem także otwarty na to. Nie mogę zmusić użytkowników do ucieczki od wszystkich ich wiadomości, ponieważ prawdopodobnie spowoduje to, że nie będą używać tej biblioteki. Jest to taki problem, że jeśli nie znajdę rozwiązania tego problemu, prawdopodobnie powrócę do starej możliwości (która wymagała podania parametru $ {LINENO} jako parametru funkcji - było to znacznie mniej uciążliwe).
TLDR: Czy istnieje sposób na to, aby eval szanował znaki specjalne w cytowanym parametrze, bez konieczności ucieczki od nich?
Jeśli używasz konstruktów specyficznych dla basha, twój skrypt logowania nie powinien być nazywany "log.sh". Powinien to być "log.bash". –
@WilliamPursell - Zrobiłem trochę wyszukiwania i nie mogłem znaleźć żadnego standardu dotyczącego konwencji nazewniczych skryptów powłoki. W rzeczywistości większość osób wydaje się całkowicie pominąć sufiks. Szczerze mówiąc, nigdy nie widziałem czegoś innego niż "\ *. Sh" (lub skrypt bez rozszerzenia/sufiksu). I przy całej praktyczności nie powinno to mieć żadnego wpływu, prawda? Interpreter jest albo pobierany z pierwszej linii skryptu, albo używana jest twoja obecna powłoka. Wygląda na to, że po prostu sprowadza się to do osobistych preferencji. Ale być może "\ *. Sh" może sugerować użytkownikowi, że skrypt używa "sh". – LousyG
Jedyny praktyczny wpływ będzie, jeśli programista używa twoich funkcji z inną powłoką. Z nazwą "* .sh" programiści rozsądnie założyliby, że twoja biblioteka może pochodzić ze skryptu zsh. –