Für Schleife mit Alphabet

12

Dies funktioniert perfekt unter OSX

#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
  echo "${chars[i]}"
done

Aber wenn ich es auf Ubuntu laufen lasse, erhalte ich die folgende Fehlermeldung.

ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected

Ich kann das Problem scheinbar nicht lösen. Irgendwelche Vorschläge?

denski
quelle
2
Dies funktioniert unter Ubuntu.
Pilot6
Ich kann es nicht auf 16.04 Bash 4.3 als Skript arbeiten. Aber es funktioniert, wenn ich es in das Terminal kopiere.
denski

Antworten:

25

Vermutlich führen Sie das Skript aus als:

sh ForLoopAlphabetTest.sh

In Ubuntu shist symlinked zu dash; Da dashes kein Konzept für Arrays gibt, wird der Syntaxfehler für angezeigt (.

Das Skript funktioniert perfekt bash, also wäre es in Ordnung, wenn Sie es als bashArgument ausführen würden:

bash ForLoopAlphabetTest.sh

Jetzt haben Sie den bashShebang für das Skript, sodass Sie das Skript ausführbar machen ( chmod u+x ForLoopAlphabetTest.sh) und es wie folgt ausführen können:

/path/to/ForLoopAlphabetTest.sh

oder aus dem Skriptverzeichnis:

./ForLoopAlphabetTest.sh

Beachten Sie auch, dass Ihr Skript Klammererweiterungen {a..z}und forKonstrukte im C-Stil enthält: Diese for (( ... ))werden auch nicht von unterstützt dash. Wenn Ihr Ziel Portabilität ist, sollten Sie sich nur die POSIX- shSyntax ansehen .

heemayl
quelle
Vielen Dank. Gibt es eine Möglichkeit, das fehlende Konzept des Arrays von Dash zu umgehen?
denski
3
@denski Wenn Sie tragbare Skripte schreiben möchten, die von /bin/shjedem Unix-ähnlichen Betriebssystem ausgeführt werden können, können Sie keine Arrays verwenden. Bash (und einige andere Shells) haben sie hinzugefügt, weil sie sehr praktisch sind und nicht immer einfach durch tragbaren Code ersetzt werden können. Insbesondere für Ihr Skript können Sie dies jedoch problemlos und ohne Verwendung bashspezifischer Funktionen tun. Interessieren Sie sich dafür?
Eliah Kagan
Wenn Sie Anregungen zum Lesen haben, ist dies hilfreich. Vielen Dank.
denski
1
@denski Ich habe eine Antwort gepostet, die einige Links und Beispiele enthält. In meinem vorherigen Kommentar hier hatte ich erwähnt, dass Sie Arrays und C-Stil für Schleifen verwendet haben, aber Ihre Verwendung der Klammererweiterung nicht erwähnt. Meine Antwort deckt ab, wie man ohne alle drei auskommt. Beachten Sie, dass diese Antwort (Heemayls, nicht meine) die primäre Lösung für Ihr Problem ist. meins konzentriert sich darauf, wie Sie Ihr Skript möglicherweise umschreiben, wenn Sie sich nicht auf bash-spezifische Funktionen verlassen können.
Eliah Kagan
@heemayl Für die Aufzeichnung wollte ich hinzufügen, dass Sie in Ihrer Annahme richtig waren, dass ich Skripte mitsh
denski am
10

Ihr Skript verwendet drei Funktionen der Bash-Shell, die nicht von allen Bourne-Shells bereitgestellt werden. Wie Heemayl sagt , können Sie dieses Skript einfach mit bashanstelle von ausführen sh. Ihre Hashbang- Zeile oben ( #!/bin/bash) gibt an, bashaber sie ist nur wirksam, wenn Sie das Skript ausführen , wie er es erklärt hat . Wenn Sie den Namen des Skripts übergeben sh, shwird das Skript nicht automatisch aufgerufen bash, sondern nur ausgeführt. Dies liegt daran, dass, sobald Ihr Skript ausgeführt wird, die Hashbang-Zeile keine Auswirkung mehr hat .

Wenn Sie vollständig portable Skripte schreiben müssen , die nicht von den Bash-Funktionen abhängen, können Sie Ihr Skript auch so ändern, dass es ohne sie funktioniert. Die von Ihnen verwendeten Bash-Funktionen sind:

Bash ist weit verbreitet, insbesondere auf GNU / Linux-Systemen wie Ubuntu, und (wie Sie gesehen haben) auch auf macOS und vielen anderen Systemen verfügbar. In Anbetracht dessen, wie oft Sie Bash-spezifische Funktionen verwenden, möchten Sie diese möglicherweise nur verwenden und stellen Sie einfach sicher, dass Sie Bash (oder eine andere Shell, die die von Ihnen verwendeten Funktionen unterstützt) verwenden, wenn Sie Ihre Skripts ausführen.

Sie können sie jedoch durch portable Konstrukte ersetzen, wenn Sie möchten. Das Array und die C-förmige forSchleife können leicht ausgetauscht werden. Das Generieren des Buchstabenbereichs ohne geschweifte Klammern (und ohne festes Codieren in Ihrem Skript) ist der eine Teil, der ein wenig knifflig ist.


Hier ist ein Skript, das alle lateinischen Kleinbuchstaben ausgibt:

#!/bin/sh

for i in $(seq 97 122); do
    printf "\\$(printf %o $i)\n"
done

Dies ist für die meisten Unix-ähnlichen Systeme portierbar und hängt nicht davon ab, welche Bourne-Shell Sie verwenden. Einige Unix-ähnliche Systeme sind jedoch nicht seqstandardmäßig installiert (sie verwenden in der Regel jotstattdessen die meisten GNU / Linux-Systeme, die standardmäßig nicht installiert sind). Sie können eine Schleife mit exproder eine arithmetische Substitution verwenden, um die Portabilität weiter zu erhöhen, wenn Sie Folgendes tun müssen:

#!/bin/sh

i=97
while [ $i -le 122 ]; do
    printf "\\$(printf %o $i)\n"
    i=$((i + 1))
done

Das verwendet eine while-loop mit dem [Befehl , um die Schleife nur fortzusetzen, wenn sie $isich in Reichweite befindet.


Anstatt das gesamte Alphabet zu drucken, definiert Ihr Skript eine Variable nund druckt die ersten $nKleinbuchstaben. Hier ist eine Version Ihres Skripts, die sich auf keine Bash-spezifischen Funktionen stützt und auf Dash funktioniert, jedoch Folgendes erfordert seq:

#!/bin/sh

n=3 start=97
for i in $(seq $start $((start + n - 1))); do
    printf "\\$(printf %o $i)\n"
done

Das Anpassen des Werts nändert, wie viele Buchstaben gedruckt werden, wie in Ihrem Skript.

Hier ist eine Version, die nicht benötigt wird seq:

#!/bin/sh

n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
    printf "\\$(printf %o $i)\n"
    i=$((i + 1))
done

Es $stopgibt einen höheren Code als den Zeichencode des letzten Buchstabens, der gedruckt werden soll, daher verwende ich für den Befehl -lt(weniger als) und nicht -le(weniger als oder gleich) [. (Es hätte auch funktioniert, um zu machen stop=$((i + n - 1))und zu verwenden [ $i -le $stop ]).

Eliah Kagan
quelle
1
Dies ist eine phänomenal detaillierte Antwort, als Sie für die Ausbildung. Ich bin ein Anfänger und meine Art, Skripte zu schreiben, besteht darin, funktionierende Elemente aus dem Internet zusammenzuführen, bis sie funktionieren. Ich habe keine Zweifel, dass es 1) bessere und 2) einfachere Möglichkeiten gibt, die Dinge zu tun, die ich mit Skripten erstelle.
denski