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?
Antworten:
Vermutlich führen Sie das Skript aus als:
In Ubuntu
sh
ist symlinked zudash
; Dadash
es 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 alsbash
Argument ausführen würden:Jetzt haben Sie den
bash
Shebang für das Skript, sodass Sie das Skript ausführbar machen (chmod u+x ForLoopAlphabetTest.sh
) und es wie folgt ausführen können:oder aus dem Skriptverzeichnis:
Beachten Sie auch, dass Ihr Skript Klammererweiterungen
{a..z}
undfor
Konstrukte im C-Stil enthält: Diesefor (( ... ))
werden auch nicht von unterstütztdash
. Wenn Ihr Ziel Portabilität ist, sollten Sie sich nur die POSIX-sh
Syntax ansehen .quelle
/bin/sh
jedem 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?sh
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
bash
anstelle von ausführensh
. Ihre Hashbang- Zeile oben (#!/bin/bash
) gibt an,bash
aber sie ist nur wirksam, wenn Sie das Skript ausführen , wie er es erklärt hat . Wenn Sie den Namen des Skripts übergebensh
,sh
wird das Skript nicht automatisch aufgerufenbash
, 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:
( {a..z} )
, den Sie zuweisenchars
, erstellt ein Array und${chars[i]}
indiziert es, das in Ihrer Schleife angezeigt wird.{a..z}
wird darauf erweiterta 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
. Dies ist jedoch keine universelle (oder standardisierte) Funktion von Bourne-Shells, und Dash unterstützt sie nicht.for
Schleifen-Syntax im C-Stil . Obwohl die C- Schleife auf einer arithmetischen Erweiterung basiert , die selbst nicht spezifisch für Bash ist (obwohl einige sehr alte, nicht POSIX-konforme Shells diese auch nicht haben), ist sie ein Bash-Ismus und nicht weit verbreitet andere Muscheln.for
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
for
Schleife 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:
seq
Befehl generiert numerische Sequenzen.$(
)
Führt eine Befehlssubstitution durch und$(seq 97 122)
wird durch die Ausgabe von ersetztseq 97 122
. Dies sind die Zeichencodes füra
throughz
.printf
Befehl können Zeichencodes in Buchstaben umgewandelt werden (z. B.printf '\141'
Ausdruckea
, gefolgt von einem Zeilenumbruch ). Die Codes müssen jedoch in Oktalform vorliegen , während sieseq
nur in Dezimalform ausgegeben werden . So ich verwendet habe ,printf
zweimal: die innereprintf %o $i
wandelt Dezimalzahlen (vomseq
) zu oktale und ist substituiert in den äußerenprintf
Befehl. (Obwohl es auch möglich ist , hexadezimal zu verwenden , ist es nicht einfacher und scheint weniger portabel zu sein .)printf
interpretiert\
gefolgt von einer Oktalzahl als das Zeichen mit diesem Code und\n
als eine neue Zeile. Aber die Schale verwendet auch\
als Escape - Zeichen. Eine\
vor$
wird verhindert , dass$
von verursacht eine Expansion auftreten (in diesem Fall Befehl Substitution ), aber ich möchte nicht , dass verhindern, so habe ich es mit einem anderen entkommen\
; das ist der grund dafür\\
. Die zweite ,\
bevorn
nicht entkommen werden müssen , weil, im Gegensatz zu\$
,\n
hat keine besondere Bedeutung für den Shell in einer Strings in doppelten Anführungszeichen.'
) ebenfalls ein wichtiger Bestandteil der Shell-Zitiersyntax sind. Ich habe sie nur zufällig nicht in diesem Skript verwendet.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
seq
standardmäßig installiert (sie verwenden in der Regeljot
stattdessen die meisten GNU / Linux-Systeme, die standardmäßig nicht installiert sind). Sie können eine Schleife mitexpr
oder eine arithmetische Substitution verwenden, um die Portabilität weiter zu erhöhen, wenn Sie Folgendes tun müssen:Das verwendet eine
while
-loop mit dem[
Befehl , um die Schleife nur fortzusetzen, wenn sie$i
sich in Reichweite befindet.Anstatt das gesamte Alphabet zu drucken, definiert Ihr Skript eine Variable
n
und druckt die ersten$n
Kleinbuchstaben. Hier ist eine Version Ihres Skripts, die sich auf keine Bash-spezifischen Funktionen stützt und auf Dash funktioniert, jedoch Folgendes erfordertseq
: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
:Es
$stop
gibt 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 machenstop=$((i + n - 1))
und zu verwenden[ $i -le $stop ]
).quelle