Do pętli z alfabetem


12

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?


2
Działa to na Ubuntu.
— Pilot6

Nie mogę go uruchomić w 16.04 bash 4.3 jako skrypt. Ale działa, jeśli skopiuję go do terminala.
— denski

Odpowiedzi:


25

Prawdopodobnie uruchamiasz skrypt jako:

sh ForLoopAlphabetTest.sh

W Ubuntu shjest symbolicznie połączony dash; ponieważ dashnie 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 jako bashargument:

bash ForLoopAlphabetTest.sh

Teraz masz bashskrypt na skrypcie, abyś mógł wykonać skrypt ( chmod u+x ForLoopAlphabetTest.sh) i uruchomić go jako:

/path/to/ForLoopAlphabetTest.sh

lub z katalogu skryptu:

./ForLoopAlphabetTest.sh

Zauważ też, że twój skrypt zawiera rozwinięcie nawiasu {a..z}i forkonstrukcję w stylu C : for (( ... ))które również nie są obsługiwane przez dash; więc jeśli twoim celem jest przenośność, powinieneś spojrzeć tylko na shskładnie POSIX .


Dziękuję Ci. Czy istnieje sposób na obejście braku pojęcia tablic?
— denski

3
@denski Jeśli chcesz pisać przenośne skrypty, które mogą być uruchamiane /bin/shw 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ć?
— Eliah Kagan

Jeśli masz jakieś sugerowane lektury, byłoby to pomocne. Dzięki.
— denski

1
@denski Zamieściłem odpowiedź, która zawiera kilka linków i przykładów. W moim wcześniejszym komentarzu wspomniałem, że używałeś tablic i stylu C dla pętli, ale nie wspomniałem o używaniu rozszerzenia nawiasów klamrowych. Moja odpowiedź dotyczy tego, jak sobie radzić bez wszystkich trzech. Zauważ, że ta odpowiedź (tj. Heemayl, a nie moja) jest podstawowym rozwiązaniem twojego problemu; mine koncentruje się na tym, jak możesz przepisać skrypt, jeśli nie możesz polegać na funkcjach specyficznych dla bash.
— Eliah Kagan

@ heemayl Dla przypomnienia, chciałem dodać, że miałeś rację, zakładając, że uruchamiam skryptysh
— denski

10

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:

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

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 ]).


1
To jest fenomenalnie szczegółowa odpowiedź, niż w przypadku edukacji. Jestem bardzo początkującym, więc mój sposób pisania skryptów łączy ze sobą działające elementy znalezione w Internecie, dopóki nie zadziała. Nie mam wątpliwości, że są 1) lepsze i 2) prostsze sposoby robienia rzeczy, które tworzę za pomocą skryptów, a powyższe informacje znacznie pomagają w tym.
— denski

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.