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 bash
zamiast sh
. Twój hashbang linia u góry ( #!/bin/bash
) stanowi, bash
ale jest skuteczna tylko jeśli wykonanie skryptu, jak heemayl wyjaśnione . Jeśli podasz nazwę skryptu sh
, sh
nie 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ą), for
pę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 for
pę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
seq
Komenda 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 a
pośrednictwem z
.
- Potężne
printf
polecenie 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 seq
wyjściowe tylko dziesiętnie . Użyłem więc printf
dwa razy: wewnętrzna printf %o $i
przekształca liczby dziesiętne (dostarczone przez seq
) na liczbę ósemkową i jest zastępowana zewnętrznym printf
poleceniem. (Chociaż możliwe jest również użycie szesnastkowe , nie jest to prostsze i wydaje się mniej przenośne ).
printf
interpretuje \
liczbę ósemkową jako znak z tym kodem i \n
jako 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 n
nie trzeba uciekać, ponieważ w przeciwieństwie do tego \$
, \n
nie 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 seq
instalowanych 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 expr
podstawieniem 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 $i
jest w zasięgu.
Zamiast drukować cały alfabet, skrypt definiuje zmienną n
i drukuje pierwsze $n
mał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 n
zmian 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 $stop
jeden 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 ]
).