web-dev-qa-db-fra.com

Pour boucle avec alphabet

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?

12
denski

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

sh ForLoopAlphabetTest.sh

Dans Ubuntu, shest lié symboliquement à dashname__; comme dashn'a pas de concept de tableaux, vous obtenez l'erreur de syntaxe pour (.

Le script fonctionne parfaitement sur bashname__; il serait donc préférable de l'exécuter en tant qu'argument bashname __ ':

bash ForLoopAlphabetTest.sh

Vous avez maintenant le nom Shebang bashsur 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 forde style C: for (( ... )) qui ne sont pas non plus pris en charge par dashname__; Par conséquent, si votre objectif est la portabilité, vous ne devez rechercher que les syntaxes POSIX shname__.

25
heemayl

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:

  • Un tablea . C’est ce qui est arrivé en premier, c’est donc ce qui a provoqué l’erreur lorsque vous avez essayé d’exécuter votre script avec Dash Shell . L’expression entre parenthèses ( {a..z} ), que vous affectez à chars, crée un tableau, et ${chars[i]}, qui apparaît dans votre boucle, y est indexée.
  • Expansion des accolades. Dans Bash, ainsi que dans de nombreux autres shells, {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.
  • Le style C syntaxe alternative de la variante 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

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

10
Eliah Kagan