Działa to doskonale w OSX
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
Ale kiedy uruchamiam go na Ubuntu, pojawia się następujący błąd.
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
Nie mogę rozwiązać problemu. Jakieś sugestie?
Odpowiedzi:
Prawdopodobnie uruchamiasz skrypt jako:
W Ubuntu
sh
jest symbolicznie połączonydash
; ponieważdash
nie ma pojęcia o tablicach, pojawia się błąd składniowy dla(
.Skrypt działa idealnie
bash
, więc byłoby dobrze, gdybyś uruchomił go jakobash
argument:Teraz masz
bash
skrypt na skrypcie, abyś mógł wykonać skrypt (chmod u+x ForLoopAlphabetTest.sh
) i uruchomić go jako:lub z katalogu skryptu:
Zauważ też, że twój skrypt zawiera rozwinięcie nawiasu
{a..z}
ifor
konstrukcję w stylu C :for (( ... ))
które również nie są obsługiwane przezdash
; więc jeśli twoim celem jest przenośność, powinieneś spojrzeć tylko nash
składnie POSIX .źródło
/bin/sh
w dowolnym systemie operacyjnym podobnym do Uniksa, nie będziesz mógł używać tablic. Bash (i niektóre inne powłoki) dodały je, ponieważ są bardzo wygodne i nie zawsze można je łatwo zastąpić bardziej przenośnym kodem. Jednak w szczególności w przypadku skryptu możesz to zrobić bez problemu i bez korzystania z funkcji specyficznych dla bash. Czy jesteś zainteresowany tym, jak to zrobić?sh
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
zamiastsh
. 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ę skryptush
,sh
nie zadzwoni automatyczniebash
, 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:
( {a..z} )
, do którego przypisujeszchars
, tworzy tablicę i${chars[i]}
, które pojawia się w twojej pętli, indeksuje się do niej.{a..z}
jest rozszerzony doa 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.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:
seq
Komenda sekwencje liczbowe.$(
)
wykonuje podstawienie polecenia , więc$(seq 97 122)
jest zastępowane przez wynik działaniaseq 97 122
. Są to kody znaków zaa
pośrednictwemz
.printf
polecenie może zamieniać kody znaków na litery (np.printf '\141'
Odbitkia
, po których następuje nowa linia ), ale kody muszą być ósemkowe , a daneseq
wyjściowe tylko dziesiętnie . Użyłem więcprintf
dwa razy: wewnętrznaprintf %o $i
przekształca liczby dziesiętne (dostarczone przezseq
) na liczbę ósemkową i jest zastępowana zewnętrznymprintf
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\
przedtemn
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.'
) 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ą ichjot
, co nie jest instalowane domyślnie w większości systemów GNU / Linux). Możesz użyć pętli zexpr
podstawieniem arytmetycznym lub podstawienia arytmetycznego, aby jeszcze bardziej zwiększyć przenośność, jeśli chcesz: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 wymagaseq
:Dostosowywanie wartości
n
zmian o liczbę drukowanych liter, tak jak w skrypcie.Oto wersja, która nie wymaga
seq
: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 ]
).źródło