Ogólna zasada w skryptach powłoki polega na tym, że zmienne powinny być zawsze cytowane, chyba że istnieje ważny powód, aby tego nie robić. Aby uzyskać więcej informacji, niż prawdopodobnie chcesz wiedzieć, spójrz na to świetne pytanie i odpowiedzi: Implikacje bezpieczeństwa związane z zapominaniem o cytowaniu zmiennej w powłokach bash / POSIX .
Zastanów się jednak nad funkcją:
run_this(){
$@
}
Powinien $@
być tam cytowany czy nie? Bawiłem się nim przez chwilę i nie mogłem znaleźć żadnego przypadku, w którym brak cytatów spowodowałby problem. Z drugiej strony użycie cudzysłowów powoduje, że łamie się ono podczas przekazywania polecenia zawierającego spacje jako zmienną cytowaną:
#!/usr/bin/sh
set -x
run_this(){
$@
}
run_that(){
"$@"
}
comm="ls -l"
run_this "$comm"
run_that "$comm"
Uruchomienie powyższego skryptu zwraca:
$ a.sh
+ comm='ls -l'
+ run_this 'ls -l'
+ ls -l
total 8
-rw-r--r-- 1 terdon users 0 Dec 22 12:58 da
-rw-r--r-- 1 terdon users 45 Dec 22 13:33 file
-rw-r--r-- 1 terdon users 43 Dec 22 12:38 file~
+ run_that 'ls -l'
+ 'ls -l'
/home/terdon/scripts/a.sh: line 7: ls -l: command not found
Mogę to obejść, jeśli użyję run_that $comm
zamiast tego run_that "$comm"
, ale ponieważ funkcja run_this
(niecytowana) działa z oboma, wydaje się, że jest to bezpieczniejszy zakład.
Czy w konkretnym przypadku użycia $@
w funkcji, której zadaniem jest wykonanie $@
polecenia, należy $@
zacytować? Wyjaśnij, dlaczego nie należy go cytować, i podaj przykład danych, które mogą go złamać.
źródło
run_that
Zachowanie jest zdecydowanie tym, czego bym się spodziewał (a jeśli na ścieżce do polecenia będzie miejsce?). Jeśli chciałbyś innego zachowania, z pewnością wycofałbyś je z serwisu połączeń, na którym wiesz, jakie są dane? Spodziewałbym się wywołać tę funkcję jakorun_that ls -l
, która działa tak samo w obu wersjach. Czy jest przypadek, który sprawił, że spodziewałeś się inaczej?${mycmd[@]}
.Odpowiedzi:
Problem polega na tym, jak polecenie jest przekazywane do funkcji:
"$@"
powinien być używany w ogólnym przypadku, gdy twojarun_this
funkcja jest poprzedzona normalnie napisanym poleceniem.run_this
prowadzi do cytowania piekła:Nie jestem pewien, jak mam przekazać nazwę pliku ze spacjami
run_this
.źródło
run_this
obu przypadkach.$@
przypadkowo nie pozostawiłeś cytatu. Powinienem zostawić przykład. : Drun_this
. Jest to w zasadzie ten sam problem, na jaki napotykasz wypychanie złożonych poleceń w łańcuchy, jak omówiono w Bash FAQ 050 .To albo:
Lub:
lub:
Ale:
To nie ma większego sensu.
Jeśli chcesz wykonać
ls -l
polecenie (niels
polecenie z argumentamils
i-l
jako argumenty), wykonaj następujące czynności:Ale jeśli (bardziej prawdopodobne), jest to
ls
polecenie z argumentamils
i-l
jako argument, uruchomiłbyś:Teraz, jeśli jest to coś więcej niż proste polecenie, które chcesz wykonać, jeśli chcesz wykonać przypisania zmiennych, przekierowania, potoki ..., tylko
interpret_this_shell_code
:choć oczywiście zawsze możesz zrobić:
źródło
Patrząc z bash / ksh / zsh punktu widzenia
$*
i$@
są szczególnym przypadkiem ogólnego rozszerzalności tablicy. Rozszerzenia tablic nie są jak zwykłe rozszerzenia zmiennych:Dzięki rozszerzeniom
$*
/${a[*]}
macierz łączy tablicę z pierwszą wartościąIFS
- która jest domyślnie spacją - w jeden gigantyczny ciąg. Jeśli go nie zacytujesz, zostanie podzielony jak zwykły ciąg znaków.W przypadku rozszerzeń
$@
/${a[@]}
zachowanie zależy od tego, czy cytowane jest rozszerzenie$@
/${a[@]}
:"$@"
lub"${a[@]}"
), otrzymasz odpowiednik"$1" "$2" "$3" #...
lub"${a[1]}" "${a[2]}" "${a[3]}" # ...
$@
lub${a[@]}
), otrzymujesz odpowiednik$1 $2 $3 #...
lub${a[1]} ${a[2]} ${a[3]} # ...
W przypadku poleceń owijania zdecydowanie potrzebujesz cytowanych @ rozszerzeń (1.).
Więcej dobrych informacji na temat tablic bash (i bash-like): https://lukeshu.com/blog/bash-arrays.html
źródło
Od kiedy nie
$@
podwoiłeś cudzysłowu , pozostawiłeś wszystkie problemy z globowaniem w linku, który podałeś swojej funkcji.Jak możesz uruchomić polecenie o nazwie
*
? Nie możesz tego zrobić za pomocąrun_this
:I widzisz, nawet gdy wystąpił błąd,
run_that
dał ci bardziej znaczący komunikat.Jedynym sposobem na rozwinięcie
$@
pojedynczych słów jest podwójne cytowanie. Jeśli chcesz uruchomić je jako polecenie, musisz przekazać polecenie i jego parametry jako osobne słowa. To, co zrobiłeś po stronie dzwoniącego, a nie w ramach funkcji.jest lepszym wyborem. Lub jeśli twoje powłoki obsługują tablice:
Nawet jeśli powłoka w ogóle nie obsługuje tablicy, nadal możesz z nią grać, używając
"$@"
.źródło
Wykonywanie zmiennych w
bash
jest podatną na awarie techniką. Po prostu niemożliwe jest napisanierun_this
funkcji, która poprawnie obsługuje wszystkie przypadki brzegowe, takie jak:ls | grep filename
)ls > /dev/null
)if
while
itp.Jeśli wszystko, co chcesz zrobić, to uniknąć powtarzania kodu, lepiej skorzystaj z funkcji. Na przykład zamiast:
Powinieneś pisać
Jeśli polecenia są dostępne tylko w czasie wykonywania, powinieneś użyć
eval
, który jest specjalnie zaprojektowany do obsługi wszystkich dziwactw, które spowodująrun_this
niepowodzenie:Należy pamiętać, że
eval
jest znany na kwestie bezpieczeństwa, ale jeśli przechodzą zmienne z niezaufanych źródeł dorun_this
, będziesz musiał stawić czoła wykonanie dowolnego kodu tak dobrze.źródło
Wybór nalezy do ciebie. Jeśli nie podasz
$@
żadnej z jej wartości, poddaj ją dodatkowej rozbudowie i interpretacji. Jeśli ją zacytujesz, wszystkie przekazane argumenty są odtwarzane dosłownie w jej rozwinięciu. Nigdy nie będziesz w stanie w niezawodny sposób obsługiwać tokenów składni powłoki, takich jak&>|
i etc, bez samodzielnego analizowania argumentów - więc masz bardziej rozsądny wybór przekazania funkcji:"$@"
....lub...
$@
.Żaden sposób nie jest zły, jeśli jest celowy i jeśli skutki tego, co wybierzesz, są dobrze zrozumiane. Oba sposoby mają zalety jeden nad drugim, chociaż zalety drugiego rzadko są szczególnie przydatne. Nadal...
... to nie ma sensu , tylko rzadko mogą być znacznie użytku . A w
bash
powłoce, ponieważbash
domyślnie nie przykleja definicji środowiska do swojego środowiska, nawet jeśli wspomniana definicja jest dołączona do wiersza polecenia specjalnego wbudowanego lub funkcji, globalna wartość parametru$IFS
nie jest zmieniana, a deklaracja jest lokalna tylko narun_this()
połączenie.Podobnie:
... globbing jest również konfigurowalny. Cytaty służą celowi - nie są na darmo. Bez nich rozszerzenie powłoki podlega dodatkowej interpretacji - interpretacji konfigurowalnej . Kiedyś - z kilkoma bardzo starymi powłokami -
$IFS
był globalnie stosowany do wszystkich danych wejściowych, a nie tylko rozszerzeń. W rzeczywistości wspomniane powłoki zachowywały się bardzo podobnierun_this()
, ponieważ łamały wszystkie słowa wejściowe o wartości$IFS
. Tak więc, jeśli szukasz tego bardzo starego zachowania powłoki, powinieneś użyćrun_this()
.Nie szukam tego i jestem w tej chwili dość mocno zmuszony, aby wymyślić przydatny przykład. Generalnie wolę, aby polecenia, które uruchamia moja powłoka, były tymi, które wpisuję. I tak, biorąc pod uwagę wybór, prawie zawsze
run_that()
. Oprócz tego...Cokolwiek można cytować. Polecenia będą działać w cudzysłowie Działa, ponieważ do czasu uruchomienia polecenia wszystkie słowa wejściowe przeszły już usuwanie cudzysłowów - co jest ostatnim etapem procesu interpretacji danych wejściowych powłoki. Tak więc różnica między
'ls'
ils
może mieć znaczenie tylko podczas interpretacji powłoki - i dlatego cytowaniels
zapewnia, że żaden z nazwanych aliasówls
nie zostanie zastąpiony moim cytowanymls
słowem polecenia. Poza tym jedyne, na co wpływają cytaty, to delimitacja słów (czyli jak i dlaczego działa cytowanie zmiennych / białych znaków) oraz interpretacja metaznaków i słów zastrzeżonych.Więc:
Nigdy nie będziesz w stanie tego zrobić za pomocą ani,
run_this()
anirun_that()
.Ale nazwy funkcji,
$PATH
polecenia „d” lub wbudowane funkcje wykonają dokładnie cytowane lub niecytowane, i właśnie tak działarun_this()
irun_that()
przede wszystkim działa. Z$<>|&(){}
żadną z nich nie będziesz w stanie nic zrobić . Krótko mówiąceval
, jest.Ale bez niego jesteś ograniczony limitami prostego polecenia na podstawie używanych cytatów (nawet jeśli nie, ponieważ
$@
zachowuje się jak cytat na początku procesu, gdy polecenie jest analizowane dla metaznaków) . To samo ograniczenie dotyczy przypisań i przekierowań wiersza poleceń, które są ograniczone do wiersza polecenia funkcji. Ale to nie jest wielka sprawa:Mogłem mieć tak łatwo
<
przekierowane wejście lub>
wyjście, jak otworzyłem potok.W każdym bądź razie, nie ma tu dobrego ani złego sposobu - każdy sposób ma swoje zastosowanie. Po prostu powinieneś napisać to tak, jak zamierzasz go używać i powinieneś wiedzieć, co masz zamiar zrobić. Pomijając cytaty mogą mieć cel - w przeciwnym razie nie byłoby być cytaty w ogóle - ale jeśli pominąć ich istotnych powodów nie do celu, jesteś po prostu pisząc zły kod. Rób, co masz na myśli; W każdym razie staram się.
źródło