Mam złożone polecenie, z którego chciałbym utworzyć skrypt powłoki / bash. Mogę to napisać pod względem $1
łatwości:
foo $1 args -o $1.ext
Chcę mieć możliwość przekazania wielu nazw wejściowych do skryptu. Jak to zrobić?
I oczywiście chcę obsługiwać nazwy plików ze spacjami w nich.
bash
command-line
Thelema
źródło
źródło
Odpowiedzi:
Służy
"$@"
do reprezentowania wszystkich argumentów:Spowoduje to iterację każdego argumentu i wydrukowanie go w osobnej linii. $ @ zachowuje się jak $ *, z wyjątkiem tego, że podczas cytowania argumenty są poprawnie dzielone, jeśli są w nich spacje:
źródło
"$@"
jest to, że rozwija się do zera, gdy nie ma parametrów pozycyjnych, podczas gdy"$*"
rozwija się do pustego ciągu - i tak, istnieje różnica między brakiem argumentów a jednym pustym argumentem. Zobacz${1:+"$@"} in
/ bin / sh` .$var
byłem w stanie wykonać argumenty.shift
wyrzuca$1
i przesuwa wszystkie kolejne elementy w dół.Przepisz teraz usuniętą odpowiedź przez VonC .
Zwięzła odpowiedź Roberta Gamble'a dotyczy bezpośrednio pytania. Ten wzmacnia niektóre problemy z nazwami plików zawierającymi spacje.
Zobacz także: $ {1: + "$ @"} w / bin / sh
Podstawowa teza:
"$@"
jest poprawna, a$*
(nie cytowana) prawie zawsze jest błędna. Jest tak, ponieważ"$@"
działa dobrze, gdy argumenty zawierają spacje, i działa tak samo, jak$*
gdy nie. W niektórych okolicznościach"$*"
jest również OK, ale"$@"
zwykle (ale nie zawsze) działa w tych samych miejscach. Nienotowane$@
i$*
są równoważne (i prawie zawsze błędne).Więc jaka jest różnica między
$*
,$@
,"$*"
, i"$@"
? Wszystkie są powiązane z „wszystkimi argumentami powłoki”, ale robią różne rzeczy. Gdy nie jest cytowany$*
i$@
rób to samo. Traktują każde „słowo” (sekwencję spacji) jako osobny argument. Cytowane formy są jednak zupełnie różne:"$*"
traktuje listę argumentów jako pojedynczy ciąg oddzielony spacjami, podczas"$@"
gdy argumenty traktuje prawie dokładnie tak, jak były podane w wierszu poleceń."$@"
rozwija się do zera, gdy nie ma argumentów pozycyjnych;"$*"
rozwija się do pustego ciągu - i tak, jest różnica, chociaż może być trudno ją dostrzec. Zobacz więcej informacji poniżej, po wprowadzeniu polecenia (niestandardowego)al
.Teza dodatkowa: jeśli potrzebujesz przetworzyć argumenty ze spacjami, a następnie przekazać je innym poleceniom, czasem potrzebujesz niestandardowych narzędzi do pomocy. (Lub powinieneś używać tablic ostrożnie:
"${array[@]}"
zachowuje się analogicznie do"$@"
.)Przykład:
Dlaczego to nie działa? Nie działa, ponieważ powłoka przetwarza cudzysłowy przed rozwinięciem zmiennych. Aby powłoka zwracała uwagę na osadzone w niej cytaty
$var
, musisz użyćeval
:To staje się bardzo trudne, gdy masz nazwy plików, takie jak „
He said, "Don't do this!"
” (z cudzysłowami i podwójnymi cudzysłowami i spacjami).Powłoki (wszystkie) nie sprawiają, że szczególnie łatwo jest obsługiwać takie rzeczy, więc (co zabawne) wiele programów uniksowych nie radzi sobie z nimi dobrze. W systemie Unix nazwa pliku (pojedynczy składnik) może zawierać dowolne znaki oprócz ukośnika i wartości NUL
'\0'
. Jednak powłoki zdecydowanie nie zachęcają do spacji, znaków nowej linii ani tabulatorów w nazwach ścieżek. Z tego powodu standardowe nazwy plików uniksowych nie zawierają spacji itp.Kiedy masz do czynienia z nazwami plików, które mogą zawierać spacje i inne kłopotliwe znaki, musisz być bardzo ostrożny, a już dawno odkryłem, że potrzebuję programu, który nie jest standardem w Uniksie. Nazywam to
escape
(wersja 1.1 została opatrzona datą 1989-08-23T16: 01: 45Z).Oto przykład
escape
zastosowania - z systemem sterowania SCCS. Jest to skrypt tytułowy, który wykonuje zarównodelta
(myśl check-in ), jak iget
(myśl check-out ). Różne argumenty, zwłaszcza-y
(powód, dla którego dokonałeś zmiany) zawierałyby spacje i znaki nowej linii. Zauważ, że skrypt pochodzi z 1992 roku, więc używa$(cmd ...)
notacji zamiast notacji i nie używa#!/bin/sh
w pierwszym wierszu.(Prawdopodobnie w dzisiejszych czasach nie używałbym ucieczki aż tak dokładnie - nie jest to potrzebne
-e
na przykład z argumentem - ale ogólnie jest to jeden z moich prostszych skryptówescape
.)escape
Program po prostu wyprowadza swoje argumenty, a jakecho
nie, ale zapewnia, że argumenty są chronione do stosowaniaeval
(jeden poziomeval
; mam program, który zrobił na zdalne wykonanie powłoki, i że potrzebne do ucieczki wyjścieescape
).Mam inny program o nazwie,
al
który wyświetla jego argumenty po jednym w wierszu (i jest jeszcze bardziej stary: wersja 1.1 z 1987-01-27T14: 35: 49). Jest to najbardziej przydatne podczas debugowania skryptów, ponieważ można je podłączyć do wiersza poleceń, aby zobaczyć, jakie argumenty są faktycznie przekazywane do polecenia.[ Dodano: A teraz, aby pokazać różnicę między różnymi
"$@"
notacjami, oto jeszcze jeden przykład:Zauważ, że nic nie zachowuje oryginalnych spacji między
*
i*/*
w linii poleceń. Zauważ też, że możesz zmienić „argumenty wiersza poleceń” w powłoce, używając:Ustawia 4 opcje „
-new
”, „-opt
”, „and
” i „arg with space
”.]
Hmm, to dość długa odpowiedź - być może egzegeza jest lepszym terminem. Kod źródłowy
escape
dostępny na żądanie (e-mail do imienia i nazwiska kropka w gmail dot com). Kod źródłowyal
jest niezwykle prosty:To wszystko. Jest to odpowiednik
test.sh
skryptu, który pokazał Robert Gamble, i może być napisane jako funkcja powłoki (ale funkcje powłoki nie istniały w lokalnej wersji powłoki Bourne'a, kiedy pisałem po raz pierwszyal
).Pamiętaj też, że możesz napisać
al
jako prosty skrypt powłoki:Warunek jest potrzebny, aby nie generował danych wyjściowych po przekazaniu żadnych argumentów.
printf
Komenda będzie produkować pusty wiersz tylko argumentu format string, ale program C produkuje niczego.źródło
Zauważ, że odpowiedź Roberta jest poprawna i działa
sh
również. Możesz (przenośnie) uprościć to jeszcze bardziej:jest równa:
Tj. Nie potrzebujesz niczego!
Testowanie (
$
jest wierszem poleceń):Po raz pierwszy przeczytałem o tym w Unix Programming Environment autorstwa Kernighana i Pike'a.
W
bash
,help for
dokumenty to:źródło
"$@"
znaczy, a kiedy już wiesz, cofor i
oznacza, jest nie mniej czytelne niżfor i in "$@"
.for i
jest to lepsze, ponieważ oszczędza naciśnięcia klawiszy. Porównałem czytelnośćfor i
ifor i in "$@"
.W prostych przypadkach można również użyć
shift
. Traktuje listę argumentów jak kolejkę. Każdyshift
wyrzuca pierwszy argument, a indeks każdego z pozostałych argumentów jest zmniejszany.źródło
shift
w pętli for, ten element jest nadal częścią tablicy, podczas gdy z tymshift
działa zgodnie z oczekiwaniami.echo "$1"
aby zachować odstępy w wartości ciągu argumentu. Ponadto (niewielki problem) jest to destrukcyjne: nie można ponownie użyć argumentów, jeśli wszystkie zostały przeniesione. Często nie stanowi to problemu - i tak lista argumentów jest przetwarzana tylko raz.--file myfile.txt
Więc 1 $ jest parametrem, 2 $ jest wartością i wywołujesz shift dwukrotnie, gdy musisz pominąć inny argumentMożesz również uzyskać do nich dostęp jako elementy tablicy, na przykład, jeśli nie chcesz wykonywać iteracji przez wszystkie z nich
źródło
./my-script.sh param1 "param2 is a quoted param"
: - używając argv = ($ @) otrzymasz [param1, param2], - używając argv = ("$ @"), otrzymasz [param1, param2 to cytowany param]źródło
[ $# != 0 ]
jest lepiej. Powyżej#$
powinno być$#
jak w inwhile [[ $# > 0 ]] ...
Wzmacniając odpowiedź baz, jeśli musisz wyliczyć listę argumentów indeksem (np. W celu wyszukania określonego słowa), możesz to zrobić bez kopiowania listy lub mutowania jej.
Załóżmy, że chcesz podzielić listę argumentów podwójnym myślnikiem („-”) i przekazać argumenty przed myślnikami do jednego polecenia, a argumenty po myślnikach do drugiego:
Wyniki powinny wyglądać następująco:
źródło
getopt Użyj polecenia w skryptach, aby sformatować dowolne opcje lub parametry wiersza poleceń.
źródło