Jaki jest lepszy sposób wdrożenia print_last_arg
?
#!/bin/sh
print_last_arg () {
eval "echo \${$#}" # this hurts
}
print_last_arg foo bar baz
# baz
(Gdyby tak było, powiedzmy, #!/usr/bin/zsh
zamiast #!/bin/sh
wiedzieć, co robić. Moim problemem jest znalezienie rozsądnego sposobu na wdrożenie tego #!/bin/sh
.)
EDYCJA: Powyższe to tylko głupi przykład. Moim celem nie jest wydrukowanie ostatniego argumentu, ale raczej sposób na odniesienie się do ostatniego argumentu w funkcji powłoki.
EDYCJA 2: Przepraszam za tak niejasno sformułowane pytanie. Mam nadzieję, że tym razem wszystko będzie dobrze.
Gdyby tak było /bin/zsh
, zamiast /bin/sh
, mogę napisać coś takiego
#!/bin/zsh
print_last_arg () {
local last_arg=$argv[$#]
echo $last_arg
}
Wyrażenie $argv[$#]
jest przykładem tego, co opisałem w moim pierwszym EDYCJI jako sposobu na odniesienie do ostatniego argumentu w funkcji powłoki .
Dlatego naprawdę powinienem był napisać mój oryginalny przykład w ten sposób:
print_last_arg () {
local last_arg=$(eval "echo \${$#}") # but this hurts even more
echo $last_arg
}
... aby wyjaśnić, że to, czego szukam, jest mniej okropnym posunięciem na prawo od zadania.
Należy jednak pamiętać, że we wszystkich przykładach do ostatniego argumentu można uzyskać dostęp nieniszczący . IOW, dostęp do ostatniego argumentu pozostawia argumenty pozycyjne jako całość bez zmian.
źródło
var=$( eval echo \${$#})
sięeval var=\${$#}
- obaj są niczym podobni.shift
iset -- ...
rozwiązań opartych może być destrukcyjna, chyba że wykorzystywane w funkcjach, gdzie są one nieszkodliwe też.eval "var=\${$#}"
porównaniuvar=${arr[evaled index]}
z tym, że$#
jest to gwarantowana bezpieczna wartość. po co kopiować cały zestaw, a potem go niszczyć, skoro można go po prostu bezpośrednio zindeksować?Odpowiedzi:
... wypisze ciąg znaków,
the last arg is
po którym następuje <spacja> , wartość ostatniego argumentu i końcowy <nowyline>, jeśli jest co najmniej 1 argument, lub w przypadku argumentów zerowych nic nie wypisze.Jeśli zrobiłeś:
... wtedy albo przypisujesz wartość ostatniego argumentu do zmiennej powłoki,
$lastarg
jeśli jest co najmniej 1 argument, albo nic nie robisz. Tak czy inaczej, zrobiłbyś to bezpiecznie i powinien być przenośny nawet dla skorupy Olde Bourne, tak myślę.Oto jeszcze jeden, który działa podobnie, choć wymaga kopiując cały szereg arg dwukrotnie (i wymaga
printf
w$PATH
dla powłoki Bourne) :źródło
${1+":"} return
razu jak coś takiego - bo kto chciałby, aby coś robił lub ryzykowałby jakiekolwiek skutki uboczne, gdyby nie był potrzebny? Matematyka jest podobna - jeśli możesz być pewien, że możesz rozwinąć dodatnią liczbę całkowitą, możesz to zrobić przezeval
cały dzień.$OPTIND
jest do tego świetny.Oto uproszczony sposób:
(zaktualizowano w oparciu o punkt @ cuonglm, że oryginał zawiódł, gdy nie przekazał żadnych argumentów; teraz wyświetla się pusty wiersz - w
else
razie potrzeby zmień to zachowanie w klauzuli)źródło
echo "$# - 1" | bc
Biorąc pod uwagę przykład postu otwierającego (argumenty pozycyjne bez spacji):
Domyślnie
IFS=' \t\n'
, co powiesz na:Aby zwiększyć bezpieczeństwo
"$*"
, ustaw IFS (per @ StéphaneChazelas):Ale powyższe nie powiedzie się, jeśli argumenty pozycyjne mogą zawierać spacje. W takim przypadku użyj tego zamiast:
Należy pamiętać, że techniki te pozwalają uniknąć stosowania
eval
efektów ubocznych i nie powodują ich.Testowany w shellcheck.net
źródło
$IFS
jest spacja.IFS=' '
tutaj, aby wyjaśnić, że jest on używany do rozszerzenia"$*"
.Chociaż to pytanie ma nieco ponad 2 lata, pomyślałem, że podzielę się nieco bardziej złożoną opcją.
Uruchommy to
Rozszerzenie parametru powłoki Bash .
Edytować
Jeszcze bardziej zwięzłe:
echo "${@: -1}"
(Uważaj na przestrzeń)
Źródło
Przetestowano na macOS 10.12.6, ale powinien również zwrócić ostatni argument na większości dostępnych smaków * nix ...
Boli znacznie mniej
¯\_(ツ)_/¯
źródło
echo "${*: -1}"
coshellcheck
nie będzie narzekać.${array:n:m:}
jest rozszerzeniem. (w pytaniu wyraźnie wspomniano/bin/sh
)POSIXly:
(To podejście działa również w starej powłoce Bourne'a)
Z innymi standardowymi narzędziami:
(To nie będzie działać ze starymi
awk
, które nie miałyARGV
)źródło
awk
może być też stary awk, którego nie miałARGV
.awk
podejścia lub użyj innej zwykłejfor
pętli, jak inne, lub przekazując je do funkcji.Powinno to działać z dowolną powłoką zgodną z POSIX i działać również z wcześniejszą powłoką Solaris Bourne starszą od POSIX:
a oto funkcja oparta na tym samym podejściu:
PS: nie mów mi, że zapomniałem nawiasów klamrowych wokół korpusu funkcji ;-)
źródło
in ...
wfor
pętli i bez nawiasów klamrowych wokół zif
oświadczeniem używane jako ciała funkcji. Zobaczfor_clause
icompound_command
w pubs.opengroup.org/onlinepubs/9699919799 .Z „Unix - często zadawane pytania”
(1)
Jeśli liczba argumentów może wynosić zero, wówczas
$0
przypisany zostanie argument zero (zwykle nazwa skryptu)$last
. To jest powód, dla którego.(2)
(3)
Aby uniknąć drukowania pustej linii, gdy nie ma argumentów, zamień
echo "$last"
na:Unika się zerowej liczby argumentów
if [ $# -gt 0 ]
.To nie jest dokładna kopia tego, co znajduje się w linku na stronie, dodano pewne ulepszenia.
źródło
Powinno to działać na wszystkich systemach z
perl
zainstalowanym (więc większość UNICES):Sztuką jest, aby użyć
printf
, aby dodać\0
po każdym argumencie powłoki, a następnieperl
„s-0
przełącznik, który ustawia swój rekord separator NULL. Następnie iterujemy dane wejściowe, usuwamy\0
i zapisujemy każdą linię zakończoną znakiem NULL jako$l
.END
Blok (to właśnie}{
jest) będą realizowane po wszystkim wejście została odczytana tak drukuje ostatnią „linia”: ostatni argument powłoki.źródło
sed
,awk
czyperl
to może być i tak cennych informacji. wciąż możesz zrobić to samo zsed -z
aktualnymi wersjami GNU.Oto wersja z rekurencją. Nie mam pojęcia, jak to jest zgodne z POSIX ...
źródło
Prosta koncepcja, bez arytmetyki, bez pętli, bez ewaluacji , tylko funkcje.
Pamiętaj, że powłoka Bourne'a nie miała arytmetyki (potrzebna zewnętrzna
expr
). Jeśli chcesz uzyskać arytmetykę wolny,eval
wolny wybór, jest to opcja. Potrzebowanie funkcji oznacza SVR3 lub wyższy (bez nadpisywania parametrów).Poniżej znajdziesz bardziej solidną wersję z printf.
Ta struktura na wezwanie
printlast
jest stałe , argumenty muszą być ustawione w liście argumentów powłoki$1
,$2
itd (stos argument) i rozmowa odbywa się, jak podano.Jeśli lista argumentów wymaga zmiany, wystarczy je ustawić:
Lub stwórz łatwiejszą w użyciu funkcję (
getlast
), która może dopuszczać ogólne argumenty (ale nie tak szybko, argumenty są przekazywane dwa razy).Zauważ, że argumenty (
getlast
lub wszystkie zawarte w$@
printlast) mogą mieć spacje, znaki nowej linii itp. Ale nie NUL.Lepszy
Ta wersja nie jest drukowana,
0
gdy lista argumentów jest pusta, i użyj bardziej niezawodnego printf (cofnij się,echo
jeśli opcja zewnętrznaprintf
nie jest dostępna dla starych powłok).Korzystanie z EVAL.
Jeśli powłoka Bourne'a jest jeszcze starsza i nie ma żadnych funkcji, lub jeśli z jakiegoś powodu użycie eval jest jedyną opcją:
Aby wydrukować wartość:
Aby ustawić wartość w zmiennej:
Jeśli trzeba to zrobić w funkcji, argumenty należy skopiować do funkcji:
źródło
eval
?for loop
lubwhile loop
?echo "Hello"
pętle, ponieważ procesor musi czytać lokalizacje pamięci w pętli. Znowu: mój kod nie ma dodanych pętli.for
,while
lubuntil
pętle. Czy widzisz jakieśfor
while
lubuntil
pętle zapisane w kodzie ?. Nie ?, po prostu zaakceptuj fakt, zaakceptuj go i naucz się.Ten komentarz sugeruje użycie bardzo eleganckiego:
źródło