Użyłem jednego z poniższych
echo $(stuff)
echo `stuff`
(gdzie stuff
jest np pwd
lub date
czy coś bardziej skomplikowane).
Potem powiedziano mi, że ta składnia jest nieprawidłowa, zła praktyka, nie elegancki, nadmierny, zbędny, zbyt skomplikowany, program kultu kultowego, noobish, naiwny itp.
Ale polecenie nie działa, więc co dokładnie jest nie tak?
shell-script
sh
echo
Kamil Maciorowski
źródło
źródło
echo $(echo $(echo $(echo$(echo $(stuff)))))
również ma pracę, a jeszcze prawdopodobnie nie będzie go używać, prawda? ;-)stuff
. : DOdpowiedzi:
tl; dr
Podeszwa
stuff
będzie najprawdopodobniej pracować dla Ciebie.Pełna odpowiedź
Co się dzieje
Kiedy biegniesz
foo $(stuff)
, dzieje się tak:stuff
biegnie;$(stuff)
wywołaniefoo
;foo
uruchamia się, argumenty wiersza poleceń oczywiście zależą od tego, co zostałostuff
zwrócone.Ten
$(…)
mechanizm nazywa się „zastępowaniem poleceń”. W twoim przypadku głównym poleceniem jestecho
zasadniczo wypisywanie argumentów wiersza poleceń na standardowe wyjście. Wszystko, costuff
próbuje wydrukować na standardowe wyjście, jest przechwytywane, przekazywaneecho
i drukowane na standardowe wyjścieecho
.Jeśli chcesz, aby wyjście
stuff
zostało wydrukowane na standardowe wyjście, po prostu uruchom podeszwęstuff
.`…`
Składnia służy temu samemu celowi co$(…)
(pod tym samym tytułem: „podstawienia polecenia”), istnieje kilka różnic chociaż, więc nie można ślepo je wymieniać. Zobacz to FAQ i to pytanie .Czy powinienem unikać
echo $(stuff)
bez względu na wszystko?Istnieje powód, z którego możesz skorzystać,
echo $(stuff)
jeśli wiesz, co robisz. Z tego samego powodu powinieneś unikać,echo $(stuff)
jeśli tak naprawdę nie wiesz, co robisz.Chodzi o to
stuff
iecho $(stuff)
nie jest dokładnie równoważne. Ten ostatni oznacza wywołanie operatora split + glob na wyjściustuff
z wartością domyślną$IFS
. Podwójne cytowanie podstawienia polecenia zapobiega temu. Cytując pojedynczą zamianę polecenia, nie jest to już podstawienie polecenia.Aby to zaobserwować, jeśli chodzi o dzielenie, uruchom następujące polecenia:
I do globowania:
Jak widać,
echo "$(stuff)"
jest to odpowiednik (-ish *)stuff
. Możesz go użyć, ale po co komplikować to w ten sposób?Z drugiej strony, jeśli chcesz wyjście
stuff
do poddania dzielenie + uogólniające wtedy mogą znaleźćecho $(stuff)
przydatne. To jednak musi być twoja świadoma decyzja.Istnieją polecenia generujące dane wyjściowe, które powinny zostać ocenione (w tym dzielenie, globowanie i inne) i uruchamiane przez powłokę, więc
eval "$(stuff)"
jest taka możliwość (patrz ta odpowiedź ). Nigdy nie widziałem polecenia, które wymagałoby wyjścia w celu dodatkowego podziału + globowania przed wydrukowaniem . Celowe używanieecho $(stuff)
wydaje się bardzo rzadkie.Co
var=$(stuff); echo "$var"
?Słuszna uwaga. Ten fragment kodu:
powinno być równoważne z
echo "$(stuff)"
(-ish *) dostuff
. Jeśli jest to cały kod, po prostu uruchomstuff
zamiast tego.Jeśli jednak musisz użyć wyników
stuff
więcej niż jeden raz, to podejściejest zwykle lepszy niż
Nawet jeśli
foo
tak,echo
i dostanieszecho "$var"
kod, może być lepiej zachować go w ten sposób. Rzeczy do rozważenia:var=$(stuff)
stuff
biegami raz; nawet jeśli polecenie jest szybkie, właściwym rozwiązaniem jest unikanie dwukrotnego obliczania tego samego wyniku. A możestuff
ma skutki inne niż pisanie na standardowe wyjście (np. Tworzenie pliku tymczasowego, uruchamianie usługi, uruchamianie maszyny wirtualnej, powiadamianie zdalnego serwera), więc nie chcesz uruchamiać go wiele razy.stuff
generuje czasu w zależności czy wyjście nieco przypadkowy, można uzyskać niespójne wynikifoo "$(stuff)"
ibar "$(stuff)"
. Povar=$(stuff)
ustaleniu wartości$var
i możesz być pewnyfoo "$var"
ibar "$var"
uzyskać identyczny argument wiersza poleceń.W niektórych przypadkach zamiast tego
foo "$var"
możesz chcieć (potrzebować) użyćfoo $var
, szczególnie jeślistuff
generuje wiele argumentów dlafoo
(zmienna tablicowa może być lepsza, jeśli twoja powłoka ją obsługuje). Znowu wiedz, co robisz. Jeśli chodzi oecho
różnicę międzyecho $var
iecho "$var"
jest taka sama jak międzyecho $(stuff)
iecho "$(stuff)"
.* Odpowiednik ( -ish )?
Powiedziałem, że
echo "$(stuff)"
jest równoważne (-ish) dostuff
. Istnieją co najmniej dwa problemy, które sprawiają, że nie jest to dokładnie równoważne:$(stuff)
działastuff
w podpowłoce, więc lepiej powiedzieć, żeecho "$(stuff)"
jest równoważne (-ish)(stuff)
. Polecenia, które wpływają na powłokę, w której działają, jeśli są w podpowłoce, nie wpływają na główną powłokę.W tym przykładzie
stuff
jesta=1; echo "$a"
:Porównaj to z
i z
Kolejny przykład, zacznij od
stuff
byciacd /; pwd
:oraz testy
stuff
i(stuff)
wersje.echo
nie jest dobrym narzędziem do wyświetlania niekontrolowanych danych . To, oecho "$var"
czym rozmawialiśmy, powinno byćprintf '%s\n' "$var"
. Ale ponieważ wspomniano w tym pytaniuecho
i ponieważ najbardziej prawdopodobnym rozwiązaniem nie jest zastosowanieecho
w pierwszej kolejności, postanowiłem nie przedstawiaćprintf
do tej pory.stuff
lub(stuff)
przeplata wyjście stdout i stderr, jednocześnieecho $(stuff)
wypisując wszystkie wyjście stderr zstuff
(które uruchamia się jako pierwsze), a dopiero potem przetworzone wyjście stdout przezecho
(które uruchamia się jako ostatnie).$(…)
usuwa wszelkie końcowe znaki nowej linii, a następnieecho
dodaje je z powrotem.echo "$(printf %s 'a')" | xxd
Daje więc inną wydajność niżprintf %s 'a' | xxd
.Niektóre polecenia (
ls
na przykład) działają inaczej w zależności od tego, czy standardowym wyjściem jest konsola, czy nie; tols | cat
samo niels
robi. Podobnieecho $(ls)
będzie działać inaczej niżls
.Odkładając na
ls
bok, w ogólnym przypadku, jeśli musisz zmusić to inne zachowanie, tostuff | cat
jest lepsze niżecho $(ls)
lubecho "$(ls)"
ponieważ nie powoduje to wszystkich innych problemów wymienionych tutaj.Prawdopodobnie inny status wyjścia (wspomniany dla kompletności tej odpowiedzi na wiki; szczegółowe informacje znajdują się w innej odpowiedzi, która zasługuje na uznanie).
źródło
stuff
sposób będzie przeplatałstdout
istderr
generował, jednocześnieecho `stuff`
drukując wszystkie danestderr
wyjściowe zstuff
, i tylko wtedy danestdout
wyjściowe.echo $(stuff)
możeecho "$(stuff)"
sprawić, że będziesz mieć o wiele więcej problemów , jakbyś nie chciał jawnie globowania i ekspansji.$(…)
usuwa wszelkie końcowe znaki nowej linii, a następnieecho
dodaje je z powrotem.echo "$(printf %s 'a')" | xxd
Daje więc inną wydajność niżprintf %s 'a' | xxd
.ls
na przykład) działają inaczej w zależności od tego, czy standardowym wyjściem jest konsola, czy nie; tols | cat
samo niels
robi. I oczywiścieecho $(ls)
będzie działać inaczej niżls
.Kolejna różnica: utracono kod wyjścia podpowłoki, dlatego
echo
zamiast tego pobierany jest kod wyjścia .źródło
$?
będzie kod powrotuecho
(forecho $(stuff)
) zamiast tegoreturn
edytowanego przezstuff
.stuff
Funkcjareturn 1
(kod wyjścia), aleecho
ustawia go w pozycji „0”, niezależnie od kodu powrotustuff
. Nadal powinien być w odpowiedzi.