Twój skrypt wykorzystuje trzy funkcje powłoki Bash, które nie są dostępne we wszystkich powłokach typu Bourne'a. Jak mówi heemayl , możesz po prostu uruchomić ten skrypt bashzamiast sh. Twój hashbang linia u góry ( #!/bin/bash) stanowi, bashale jest skuteczna tylko jeśli wykonanie skryptu, jak heemayl wyjaśnione . Jeśli podasz nazwę skryptu sh, shnie zadzwoni automatycznie bash, ale po prostu uruchomi skrypt. Wynika to z faktu, że po uruchomieniu skryptu linia hashbang nie działa .
Inną alternatywą, jeśli musisz pisać w pełni przenośne skrypty, które nie zależą od funkcji Bash, jest zmiana skryptu, aby działał bez nich. Funkcje Bash, których używasz to:
- Tablicy . To było pierwsze, więc to spowodowało błąd podczas próby uruchomienia skryptu za pomocą powłoki Dash . Wyrażenie w nawiasie
( {a..z} ), do którego przypisujesz chars, tworzy tablicę i ${chars[i]}, które pojawia się w twojej pętli, indeksuje się do niej.
- Rozszerzenie klamry. W Bash, a także w wielu innych powłokach,
{a..z}jest rozszerzony do a b c d e f g h i j k l m n o p q r s t u v w x y z. Jednak nie jest to uniwersalna (ani znormalizowana) funkcja powłok w stylu Bourne'a, a Dash nie obsługuje tego.
- C stylu alternatywny
for-loop składni . Mimo że bazuje na rozszerzeniu arytmetycznym , które samo w sobie nie jest specyficzne dla Basha (chociaż niektóre bardzo stare, niezgodne z POSIX-em powłoki też go nie mają), forpętla w stylu C jest ismem Bash i nie jest powszechnie przenośna inne muszle.
Bash jest szeroko dostępny, szczególnie na systemach GNU / Linux, takich jak Ubuntu, i (jak widzieliście) jest również dostępny na macOS i wielu innych systemach. Biorąc pod uwagę, jak często korzystasz z funkcji specyficznych dla Bash, możesz po prostu z nich skorzystać i po prostu upewnij się, że używasz Bash (lub innej powłoki obsługującej funkcje, których używasz) podczas uruchamiania skryptów.
Możesz jednak zastąpić je przenośnymi konstrukcjami, jeśli chcesz. Tablica i forpętla w stylu C można łatwo wymienić; generowanie zakresu liter bez rozwijania nawiasów klamrowych (i bez kodowania ich na stałe w skrypcie) to część, która jest nieco trudna.
Po pierwsze, oto skrypt, który drukuje wszystkie małe litery łacińskie:
#!/bin/sh
for i in $(seq 97 122); do
printf "\\$(printf %o $i)\n"
done
seqKomenda sekwencje liczbowe. $( )wykonuje podstawienie polecenia , więc $(seq 97 122)jest zastępowane przez wynik działania seq 97 122. Są to kody znaków za apośrednictwem z.
- Potężne
printfpolecenie może zamieniać kody znaków na litery (np. printf '\141'Odbitki a, po których następuje nowa linia ), ale kody muszą być ósemkowe , a dane seqwyjściowe tylko dziesiętnie . Użyłem więc printfdwa razy: wewnętrzna printf %o $iprzekształca liczby dziesiętne (dostarczone przez seq) na liczbę ósemkową i jest zastępowana zewnętrznym printfpoleceniem. (Chociaż możliwe jest również użycie szesnastkowe , nie jest to prostsze i wydaje się mniej przenośne ).
printfinterpretuje \liczbę ósemkową jako znak z tym kodem i \njako nowy wiersz. Ale powłoka używa również \jako znaku ucieczki. \Przed $uniemożliwi $od powodując rozszerzanie się pojawić (w tym przypadku, podstawiania poleceń ), ale nie chcę, aby temu zapobiec, więc uciekł go innym \; to jest powód \\. Drugiego \przedtem nnie trzeba uciekać, ponieważ w przeciwieństwie do tego \$, \nnie ma specjalnego znaczenia dla powłoki w łańcuchu z podwójnym cudzysłowem.
- Aby uzyskać więcej informacji na temat używania podwójnych cudzysłowów i odwrotnego ukośnika w programowaniu powłoki, zobacz sekcję cytowania w standardzie międzynarodowym . Patrz także 3.1.2 Cytowanie w Podręczniku użytkownika Bash , w szczególności 3.1.2.1 Znak ucieczki i 3.1.2.3 Podwójne cudzysłowy . (Oto cała sekcja , w kontekście.) Zauważ, że pojedyncze cudzysłowy (
') są również ważną częścią składni cytowania powłoki, po prostu nie używałem ich w tym skrypcie.
Jest to przenośne dla większości systemów uniksowych i nie zależy od używanej powłoki w stylu Bourne'a. Jednak kilka systemów uniksowych nie jest seqinstalowanych domyślnie (zwykle używają ich jot, co nie jest instalowane domyślnie w większości systemów GNU / Linux). Możesz użyć pętli z exprpodstawieniem arytmetycznym lub podstawienia arytmetycznego, aby jeszcze bardziej zwiększyć przenośność, jeśli chcesz:
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Który wykorzystuje while-loop z tym [poleceniem , aby kontynuować pętle tylko gdy $ijest w zasięgu.
Zamiast drukować cały alfabet, skrypt definiuje zmienną ni drukuje pierwsze $nmałe litery. Oto wersja skryptu, która nie korzysta z funkcji specyficznych dla Bash i działa w Dash, ale wymaga seq:
#!/bin/sh
n=3 start=97
for i in $(seq $start $((start + n - 1))); do
printf "\\$(printf %o $i)\n"
done
Dostosowywanie wartości nzmian o liczbę drukowanych liter, tak jak w skrypcie.
Oto wersja, która nie wymaga seq:
#!/bin/sh
n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Jest o $stopjeden wyższy niż kod znakowy ostatniej litery, który powinien zostać wydrukowany, więc używam -lt(mniej niż) niż -le(mniej niż lub równo) z [poleceniem. (Byłoby również działać, aby zrobić stop=$((i + n - 1))i używać [ $i -le $stop ]).