Pour boucle avec Alphabet

12

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?

denski
la source
2
Cela fonctionne sur Ubuntu.
Pilot6
Je ne peux pas le faire fonctionner sur 16.04 bash 4.3 en tant que script. Mais cela fonctionne si je le copie dans le terminal.
denski

Réponses:

25

Vraisemblablement, vous exécutez le script en tant que:

sh ForLoopAlphabetTest.sh

Dans Ubuntu, shest lié à dash; comme dashil n'y a pas de concept de tableaux, vous obtenez l'erreur de syntaxe pour (.

Le script fonctionne parfaitement bash, donc ce serait bien si vous l'exécutiez comme bashargument:

bash ForLoopAlphabetTest.sh

Maintenant, vous avez le bashshebang sur le script, vous pouvez donc rendre le script 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 l'expansion d'accolade {a..z}et la forconstruction de style C : for (( ... ))qui ne sont pas non plus prises en charge par dash; donc si votre objectif est la portabilité, vous ne devriez regarder que les shsyntaxes POSIX .

heemayl
la source
Je vous remercie. Existe-t-il un moyen de contourner le manque de concept de tableau de bord de tableau de bord?
denski
3
@denski Si vous voulez écrire des scripts portables qui peuvent être exécutés par /bin/shn'importe quel système d'exploitation de type Unix, vous ne pourrez pas utiliser de tableaux. Bash (et certains autres shells) les ont ajoutés car ils sont très pratiques et ne peuvent pas toujours être facilement remplacés par un code plus portable. Cependant, pour votre script en particulier, vous pouvez le faire sans problème et sans utiliser de fonctionnalités spécifiques à bash. Êtes-vous intéressé par la façon de procéder?
Eliah Kagan
Si vous avez une suggestion de lecture, ce serait utile. Merci.
denski
1
@denski J'ai publié une réponse qui comprend des liens et des exemples. Dans mon commentaire précédent ici, j'avais mentionné que vous utilisiez des tableaux et du style C pour les boucles, mais je n'avais pas mentionné votre utilisation de l'extension d'accolade. Ma réponse couvre comment faire sans les trois. Notez que cette réponse (c'est-à-dire celle de heemayl, pas la mienne) est la principale solution à votre problème; le mien se concentre sur la façon dont vous pourriez réécrire votre script si vous ne pouviez pas compter sur des fonctionnalités spécifiques à bash.
Eliah Kagan
@heemayl Pour mémoire, je voulais ajouter que vous aviez raison de supposer que sh
j'exécutais des
10

Votre script utilise trois fonctionnalités du shell Bash qui ne sont pas fournies par tous les shells de style Bourne. Comme le dit heemayl , vous pouvez simplement exécuter ce script avec bashau lieu de sh. Votre ligne de hachage en haut ( #!/bin/bash) spécifie bashmais n'est efficace que si vous exécutez le script, comme l' explique heemayl . Si vous passez le nom du script à sh, shn'appellera pas automatiquement bash, mais exécutera simplement le script. En effet, une fois votre script en cours d'exécution, la ligne de hachage n'a aucun effet .

Votre autre alternative, si vous avez besoin d'écrire des scripts entièrement portables qui ne dépendent pas des fonctionnalités de Bash, est de changer votre script pour qu'il fonctionne sans eux. Les fonctionnalités Bash que vous utilisez sont:

  • Un tableau . Cela est arrivé en premier, c'est donc ce qui a produit l'erreur lorsque vous avez essayé d'exécuter votre script avec le shell Dash . L'expression entre parenthèses ( {a..z} ), à laquelle vous attribuez chars, crée un tableau et ${chars[i]}, qui apparaît dans votre boucle, y indexe.
  • Expansion du corset. Dans Bash, et aussi dans de nombreux autres obus, {a..z}est étendu à 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. Cependant, ce n'est pas une fonctionnalité universelle (ou standardisée) des shells de style Bourne, et Dash ne la prend pas en charge.
  • Le style C autre forsyntaxe -loop . Bien que basée sur une expansion arithmétique , qui n'est pas elle-même spécifique à Bash (bien que certains shells très anciens et non conformes à POSIX ne l'aient pas non plus), la forboucle de style C est un Bash-ism et n'est pas largement portable pour d'autres coquilles.

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. Considérant combien vous utilisez des fonctionnalités spécifiques à Bash, vous pouvez simplement les utiliser, et assurez-vous 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 forboucle de style C sont faciles à remplacer; générer la gamme de lettres sans expansion d'accolade (et sans les coder en dur dans votre script) est la partie qui est un peu délicate.


Tout d'abord, voici un script qui imprime toutes les lettres latines minuscules:

#!/bin/sh

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

Il est portable sur la plupart des systèmes de type Unix et ne dépend pas du shell de style Bourne que vous utilisez. Cependant, quelques systèmes de type Unix ne sont pas seqinstallés par défaut (ils ont tendance à utiliser à la jotplace, qui n'est pas installé par défaut la plupart des systèmes GNU / Linux). Vous pouvez utiliser une boucle avec exprou une substitution arithmétique pour augmenter encore la portabilité, si vous devez:

#!/bin/sh

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

Cela utilise une whileboucle avec la [commande pour continuer à boucler uniquement lorsqu'il $iest à portée.


Plutôt que d'imprimer l'alphabet entier, votre script définit une variable net imprime les premières $nlettres minuscules. Voici une version de votre script qui ne repose sur aucune fonctionnalité spécifique à Bash et fonctionne sur Dash, mais nécessite 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 des nchangements combien de lettres sont 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à, il $stopy en a un de plus que le code de caractère de la dernière lettre qui doit être imprimé, donc j'utilise -lt(moins que) plutôt que -le(inférieur ou égal) avec la [commande. (Il aurait également fonctionné pour fabriquer stop=$((i + n - 1))et utiliser [ $i -le $stop ]).

Eliah Kagan
la source
1
Ceci est une réponse incroyablement détaillée, que vous pour l'éducation. Je suis très débutant, donc ma façon d'écrire des scripts consiste à rassembler des éléments de travail trouvés sur Internet jusqu'à ce que cela fonctionne. Je ne doute pas qu'il existe 1) de meilleures et 2) façons plus simples de faire les choses que je crée avec des scripts et ce qui précède contribue grandement à cela.
denski
Connexe: stackoverflow.com/questions/169511/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件