Cela fonctionne parfaitement sur OSX
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
Mais lorsque je l'exécute sur Ubuntu, j'obtiens l'erreur suivante.
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
Je n'arrive pas à résoudre le problème. Aucune suggestion?
Vraisemblablement, vous exécutez le script en tant que:
sh ForLoopAlphabetTest.sh
Dans Ubuntu, sh
est lié symboliquement à dash
name__; comme dash
n'a pas de concept de tableaux, vous obtenez l'erreur de syntaxe pour (
.
Le script fonctionne parfaitement sur bash
name__; il serait donc préférable de l'exécuter en tant qu'argument bash
name __ ':
bash ForLoopAlphabetTest.sh
Vous avez maintenant le nom Shebang bash
sur le script. Vous pouvez donc le rendre exécutable (chmod u+x ForLoopAlphabetTest.sh
) et l'exécuter en tant que:
/path/to/ForLoopAlphabetTest.sh
ou depuis le répertoire du script:
./ForLoopAlphabetTest.sh
Notez également que votre script contient une extension d'accolade {a..z}
et une construction for
de style C: for (( ... ))
qui ne sont pas non plus pris en charge par dash
name__; Par conséquent, si votre objectif est la portabilité, vous ne devez rechercher que les syntaxes POSIX sh
name__.
Votre script utilise trois fonctionnalités du shell Bash qui ne sont pas fournies par tous les shells de type Bourne. Comme heemayl dit , vous pouvez simplement exécuter ce script avec bash
au lieu de sh
. Votre hashbang ligne en haut (#!/bin/bash
) spécifie bash
mais n’est efficace que si vous exécutez le script, en tant que heemayl a expliqué . Si vous transmettez le nom du script à sh
, sh
n'appelle pas automatiquement bash
, mais exécute simplement le script. C'est parce que ne fois que votre script est en cours d'exécution, la ligne de hachage n'a aucun effet .
Votre autre alternative, si vous avez besoin d'écrire entièrement portable des scripts qui ne dépendent pas des fonctionnalités de Bash, est de changer votre script pour qu'il fonctionne sans elles. Les fonctionnalités de Bash que vous utilisez sont:
( {a..z} )
, que vous affectez à chars
, crée un tableau, et ${chars[i]}
, qui apparaît dans votre boucle, y est indexée.{a..z}
est développé en 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
. Toutefois, il ne s’agit pas d’une fonctionnalité universelle (ni normalisée) des coques de style Bourne, et Dash ne la prend pas en charge.for
- . Bien que basé sur expansion arithmétique , qui est non spécifique à Bash (bien que certains très vieux shells non conformes à POSIX n'ont pas soit, la boucle for
de style C est un bash-isme et n’est pas facilement transférable à d’autres coques.Bash est largement disponible, en particulier sur les systèmes GNU/Linux comme Ubuntu, et (comme vous l'avez vu) est également disponible sur macOS et de nombreux autres systèmes. En fonction de votre utilisation des fonctionnalités spécifiques à Bash, vous pouvez simplement les utiliser et vous assurer simplement que vous utilisez Bash (ou un autre shell qui prend en charge les fonctionnalités que vous utilisez) lorsque vous exécutez vos scripts.
Cependant, vous pouvez les remplacer par des constructions portables si vous le souhaitez. Le tableau et la boucle for
de style C sont faciles à remplacer; Générer la plage de lettres sans expansion d'accolade (et sans les coder en dur dans votre script) est la partie la plus délicate.
Premièrement, voici un script qui affiche toutes les lettres latines minuscules:
#!/bin/sh
for i in $(seq 97 122); do
printf "\\$(printf %o $i)\n"
done
seq
génère des séquences numériques. $(
)
exécute substitution de commande , de sorte que $(seq 97 122)
est remplacé par la sortie de seq 97 122
. Ce sont les codes de caractères pour a
à z
.printf
peut transformer les codes de caractère en lettres (par exemple, printf '\141'
imprime a
, suivi d'un nouvelle ligne ), mais les codes doivent être en octal =, alors que seq
ne génère que en décimal . J'ai donc utilisé deux fois printf
: le printf %o $i
intérieur convertit les nombres décimaux (fournis par seq
) en octal, et est substitué en commande printf
extérieure. (Bien que il est également possible d'utiliser hexadécimal , ce n'est pas plus simple et semble être moins portable .)printf
interprète \
suivi d'un nombre octal comme caractère avec ce code et de \n
comme nouvelle ligne. Mais le Shell utilise également \
en tant que caractère d'échappement. Un \
devant $
empêchera $
de provoquant une expansion (dans ce cas, substitution de commande ), mais je ne veux pas empêcher cela, alors je l'ont échappé avec un autre \
; c'est la raison de \\
. Le second \
avant n
n'a pas besoin d'être échappé car, contrairement à \$
, \n
n'a pas de signification particulière pour le shell dans une chaîne entre guillemets.'
) sont également une partie importante de la syntaxe de citation de Shell. Je ne les ai simplement pas utilisés dans ce script.Ceci est portable sur la plupart des systèmes de type Unix et ne dépend pas du shell Bourne-style que vous utilisez. Cependant, quelques systèmes de type Unix n’ont pas installé seq
par défaut (ils ont tendance à utiliser jot
, qui n’est pas installé par défaut sur la plupart des systèmes GNU/Linux). Vous pouvez utiliser une boucle avec expr
ou une substitution arithmétique pour augmenter la portabilité, si vous avez besoin de:
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Cela utilise une boucle while
- avec la commande [
pour continuer à boucler uniquement lorsque $i
est dans la plage.
Plutôt que d'imprimer tout l'alphabet, votre script définit une variable n
et imprime les premières lettres minuscules $n
. Voici une version de votre script qui ne repose sur aucune fonctionnalité spécifique à Bash et fonctionne sur Dash, mais requiert seq
:
#!/bin/sh
n=3 start=97
for i in $(seq $start $((start + n - 1))); do
printf "\\$(printf %o $i)\n"
done
Ajuster la valeur de n
modifie le nombre de lettres imprimées, comme dans votre script.
Voici une version qui ne nécessite pas seq
:
#!/bin/sh
n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Là, $stop
est un plus élevé que le code de caractère de la dernière lettre devant être imprimée, aussi j’utilise -lt
(inférieur à) plutôt que -le
( inférieur ou égal) avec la commande [
. (Cela aurait également fonctionné pour créer stop=$((i + n - 1))
et utiliser [ $i -le $stop ]
).