IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

[bash] Pièges à éviter.


Sujet :

Shell et commandes GNU

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    172
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 172
    Par défaut [bash] Pièges à éviter.
    Je créé ce sujet car je n'ai rien trouvé d'équivalent sur ce site.

    J'ai une toute jeune expérience du bash mais j'ai déjà rencontré un de ces pièges.

    Bash ne supporte pas les if (elif inclus) et fonctions vides, il y faut au moins une instruction.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    MaFonction {
    }
     
    if maCondition ; then
    fi
    Vous me direz, mais quel intérêt d'avoir une fonction ou un if vide ?
    Aucun, sauf si on veut simplement faire le prototype et avancer plus loin dans une autre partie du code, pour y revenir ultérieurement.
    Après on peut discuter du bien fondé d'une telle démarche, mais c'est un autre débat

    Bref, pour pallier à cela il suffit d'ajouter une instruction qui ne modifie pas le déroulement du code, true ou false semble tout indiqué dans la plupart des cas.

    Je précise que je n'ai trouvé aucune info à ce sujet dans le man de bash, et ça me semble tout sauf évident, j'ai jamais rencontré cela dans un autre langage script, personnellement j'ai perdu un temps fou à trouver cette cause, je pensais à tout sauf à cela.

    Si vous pouviez faire part d'éventuelles autres infos de ce genre ici (si il y en a)
    J'ai hésité à poster dans le sujet "truc et astuces", mais je trouvais que le sujet était différent, si le staff décide que ce n'est pas le cas, alors amen.

  2. #2
    Membre expérimenté Avatar de FRUiT
    Homme Profil pro
    Inscrit en
    Février 2011
    Messages
    83
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Février 2011
    Messages : 83

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par AnozerOne Voir le message
    Bref, pour pallier à cela il suffit d'ajouter une instruction qui ne modifie pas le déroulement du code, true ou false semble tout indiqué dans la plupart des cas.

    Je précise que je n'ai trouvé aucune info à ce sujet dans le man de bash, et ça me semble tout sauf évident, j'ai jamais rencontré cela dans un autre langage script, personnellement j'ai perdu un temps fou à trouver cette cause, je pensais à tout sauf à cela.

    Si vous pouviez faire part d'éventuelles autres infos de ce genre ici (si il y en a)
    Salut

    Le principal problème du shell, c'est qu'il est très lent. Normal puisqu'il analyse chaque instruction. Et donc pour qu'il soit malgré tout le plus rapide possible, son moteur d'analyse a été réduit au minimum. Corollaire, un moteur d'analyse réduit n'est pas cablé pour analyser toutes les situations possibles.
    Ce qui implique alors une syntaxe parfaite de la part du programmeur car sinon l'analyseur ne pigera rien.

    Donc après un if, il faut un then et une instruction. Après un while et un for, il faut un do et une instruction. D'où ta solution d'utiliser l'instruction "true" ne faisant rien de visible. Tu aurais pu aussi mettre un simple point-virgule symbolisant l'instruction "rien"
    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if ...
    then
        ;
    fi

    Autre danger du shell que j'ai déjà repéré mais qui peut causer souci à celui qui n'est pas habitué
    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    while ...
    do
        ...
        ...
        echo "Voulez-vous continuer ?"
        read rep
        if test $rep = non
        then
             break
        fi
    done

    Le danger de ce code vient si l'utilisateur tape "<return>" dans rien répondre. A ce moment là, la variable $rep est vide et le shell voit
    Cette syntaxe est bancale car l'opérateur "=" veut absolument deux opérandes et ici il n'en voit qu'un seul. D'où le message sybillin "test:= unary opérator expected" signifiant qu'il s'attend à avoir un opérateur unaire en lieu et place du "=" vu qu'il ne voit qu'un seul opérande.

    Pour pallier ce danger, toujours encapsuler les variables chaines par des guillemets
    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    while ...
    do
        ...
        ...
        echo "Voulez-vous continuer ?"
        read rep
        if test "$rep" = "non"
        then
             break
        fi
    done

    Si jamais la variable $rep" est vide, le shell verra
    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    if test "" = "non"
    Et là, il comprendra quand-même qu'il doit comparer 2 chaines...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    172
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 172
    Par défaut
    @FRUIT : Merci pour le lien, je m'étais limité à quelles sources francophones, mais je dois dire que celle là à l'air d'être d'une meilleure qualité.

    @Sve@r : : J'avais essayé une instruction vide ; mais bash me vomissait une erreur de syntaxe, as tu vraiment essayé cette solution ?

    Merci pour le warning, même si je le savais déjà (je ne peux pas non plus être complètement ignorant )

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par AnozerOne Voir le message
    @Sve@r : : J'avais essayé une instruction vide ; mais bash me vomissait une erreur de syntaxe, as tu vraiment essayé cette solution ?
    Non désolé, j'ai fait une erreur de souvenir. C'est l'instruction ":" et non ";" (j'ai tapé mon post de mémoire)...

    Mais puisqu'on est dans les petites subtilités du shell (je viens de lire http://mywiki.wooledge.org/BashPitfalls que j'ai trouvé pas mal du tout), j'ai remarqué un truc amusant sur les booléens

    En effet, dans les langages classiques, un booléen n'est jamais examiné plus que nécessaire. Par exemple dans un if (e1 or e2 or e3), si e1 est vrai, alors l'évaluation ne s'embête pas à examiner e2 et e3 puisque le test est de toute façon vrai. Pareil pour un "et" qui serait faux dès la première expression.
    Ce qui est pratique quand l'évaluation de e2 dépend de e1 comme les pointeurs en C
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    if (pt != NULL && *pt == ...)

    Mais pas en shell, comme le démontre le petit code suivant

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #!/bin/sh
    # Petit script démontrant le comportement d'une évaluation
     
    # Test sur le "ou"
    rm -f fichier1
    ls -l fichier2
    if test -n "$HOME" -o -z "$(>fichier1)"
    then
    	echo "gagné"
    	ls -l fichier1
    fi
     
    # Test sur le "et"
    rm -f fichier2
    ls -l fichier2
    if test -z "$HOME" -a -n "$(>fichier2)"
    then
    	:      # Juste pour te faire plaisir :mrgreen:
    else
    	echo "gagné"
    	ls -l fichier2
    fi
    Dans le cas du "ou", comme la variable $HOME n'est pas vide, le "if" est définitivement vrai. Mais au résultat, on se rend compte qu'il a quand-même inutilement évalué l'instruction suivante laquelle va créer le fichier n° 1 (ce qu'on voit lors du "ls")

    Et idem dans le cas du "et" où le fichier n° 2 est inutilement créé alors que le "if" est déjà définitivement faux...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    172
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 172
    Par défaut
    Ok, rien de bien important à priori mais cela met la valeur $? à 0, dans certains cas l'emploi de false à la place peut donc être utile.
    Mais dans la mesure où l'on parle d'un code qui n'est pas fini ...

    EDIT : Pour les booléens ca ne s'applique que pour test expression et [ expression ] (qui est la même chose avec une syntaxe différente mais pas pour [[ expression ]].
    Cela s'applique aussi pour les conditions liées par des && ou des ||

    J'avais mal lu

    Même si cette dernière forme [[ expression ]] n'est pas compatible avec le sh (mais l'est pour bash et ksh, et surement d'autres shell), elle est plus puissante.

    Plus d'explications dans cette page.
    (j'étais justement en train de la lire )

    Le problème vient quand on a plus de 2 tests ainsi en série, dès fois le comportement du shell n'a pas vraiment de sens

  7. #7
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par AnozerOne Voir le message
    Pour les booléens ca ne s'applique que pour test et [ expression ] (qui est la même chose avec une syntaxe différente mais pas pour [[ expression ]].
    Oui. Toutefois comme je développe dans du multi environnement, j'essaye d'avoir un shell 100% compatible Bourne Shell de base donc pour moi, [[...]] c'est interdit.

    Citation Envoyé par AnozerOne Voir le message
    Cela s'applique aussi pour les conditions liées par des && ou des ||
    Là ce n'est plus pareil. Le && et || sont des connecteurs d'instructions. e1 && e2 n'ira exécuter e2 que si e1 est vrai. Normal que si e1 soit faux, e2 ne soit pas exécuté
    Moi je parlais de l'évaluation interne faite par l'instruction "test" laquelle aurait-pu être un poil plus poussée (simplifiée peut-être ?)...

    Citation Envoyé par AnozerOne Voir le message
    Même si cette dernière forme n'est pas compatible avec le shell
    Arf non, le Bourne Shell de base connait parfaitement les "&&" et "||"
    Citation Envoyé par AnozerOne Voir le message
    elle est plus puissante.
    Je ne sais pas. Je ne suis pas certain qu'il soit judicieux de remplacer un
    qui n'appelle qu'une fois l'instruction; en
    qui l'appellera 2 fois...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    172
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 172
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Oui. Toutefois comme je développe dans du multi environnement, j'essaye d'avoir un shell 100% compatible Bourne Shell de base donc pour moi, [[...]] c'est interdit.
    Je n'ai pas cette contrainte, étant simplement amateur

    Là ce n'est plus pareil. Le && et || sont des connecteurs d'instructions. e1 && e2 n'ira exécuter e2 que si e1 est vrai. Normal que si e1 soit faux, e2 ne soit pas exécuté
    Et pourtant :

    But there's a problem. When we have a sequence of commands separated by Conditional Operators, Bash looks at every one of them, in order from left to right. The exit status is carried through from whichever command was most recently executed, and skipping a command doesn't change it.
    EDIT : Ok, erreur de logique de ma part, je pense que je vais arrêter les frais pour aujourd'hui ...


    Moi je parlais de l'évaluation interne faite par l'instruction "test" laquelle aurait-pu être un poil plus poussée (simplifiée peut-être ?)...
    En fait j'ai édité mon message cela s'applique aussi avec [[ ]]


    Arf non, le Bourne Shell de base connait parfaitement les "&&" et "||"
    L'intérêt n'est pas là (cette phrase était mal placé avant mon edit), mais je ne peux pas mieux expliquer que le lien que j'ai précédemment donné, et puis tu dois déjà le savoir de toute façon.

    Je ne sais pas. Je ne suis pas certain qu'il soit judicieux de remplacer un
    qui n'appelle qu'une fois l'instruction; en
    qui l'appellera 2 fois...
    Et pourtant il y en a un (le lien que j'ai donné explique cela très bien), ce qui semble d'ailleurs être le problème de ton exemple.

    Don't ever use the -a or -o tests of the [ command. Use multiple [ commands instead (or use [[ if you can). POSIX doesn't define the behavior of [ with complex sets of tests, so you never know what you'll get.

Discussions similaires

  1. Réponses: 0
    Dernier message: 16/06/2014, 18h28
  2. [Lazarus] [Linux] Lazarus - Zeoslib : compatibilité, pièges à éviter ?
    Par Jon Shannow dans le forum Lazarus
    Réponses: 10
    Dernier message: 31/10/2012, 12h01
  3. Implantation ERP - Conseils et pièges à éviter.
    Par martic dans le forum Forum général ERP
    Réponses: 11
    Dernier message: 31/08/2012, 15h37
  4. Réponses: 5
    Dernier message: 06/12/2011, 10h40
  5. Réponses: 1
    Dernier message: 12/05/2011, 17h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo