Journal Args parser pour shell

Posté par  . Licence CC By‑SA.
Étiquettes :
24
15
fév.
2024

Bonjour à tous,

Vous écrivez des scripts shell mais vous trouvez que la gestion des options et arguments ainsi que de l'aide n'est pas leur point fort.
Pour pallier a ça j'ai fais une petite lib à sourcer dans vos scipts shell (dash, bash, zsh, ksh testés) pour gérer ceci.

Elle fonctionne un peu à la manière de argparse sous python et intègre une option pouir générer des script de complétion pour bash et zsh.
Elle est disponible ici : https://salsa.debian.org/openstack-team/third-party/auxilium
Elle est sous licence Apache 2 et packagée pour Debian et Archlinux.

  • # Un débat loin d’être clos la gestion des arguments

    Posté par  . Évalué à 4 (+1/-0). Dernière modification le 15 février 2024 à 15:58.

    Sympa! Il va falloir que je jette un œil. Bon, je n’aime vraiment pas argparse de Python, et comme j’en écris peu et des trucs simples je finis toujours par faire une gestion à l’arrache ad-hoc mais ça mérite que je regarde.

    Ça me rappelle qu’il y a de ça seulement quelques jours je me suis rendu compte que la construction que j’utilisais pour avoir des options longues avec getopts (ou plutôt, des versions longues des options courtes, ce qui n’est pas la même chose) souffrait d’un léger problème dont je ne m’étais pas rendu compte :

    for argument in "${@}"; do
      shift
      case "${argument}" in
         ('--verbose')             set -- "${@}" '-v' ;;
         ('--help')                set -- "${@}" '-h' ;;
         … … …
         … … …
         (*)                       set -- "${@}" "${argument}"
      esac
    done

    à mettre avant le bloc de getopts. On peut aussi le grouper avec le getopts dans une fonction mais ça ne change rien au problème.

    Ça fait le job, sauf… et bien quand une valeur passée à une option est identique à l’une des options longues, là ça explose en vol, vu que c’est la valeur qui se retrouve « raccourcie ». Faudrait que je vois si je peux trouver une solution pour ce cas précis. Je pourrais ajouter un ('--') break ;;, ce qui permettrait au moins de pouvoir passer une telle valeur en tant qu’argument simple (ie: un argument pris en tant que tel, qui n’est pas la valeur passé à une option), mais c’est pas vraiment une solution.

    Même sans parler de l’implémentation la manière de gérer les options est pas toujours évidente. Par exemple j’aime bien les programmes pour lesquels les options/arguments possibles sont en fonction du premier. Comme pour git par exemple. Il me semble qu’argparse le permet d’ailleurs.

  • # getopt(1)

    Posté par  (site web personnel) . Évalué à 7 (+4/-0).

    J'utilise depuis longtemps getopt(1), le programme de util-linux, pas la commande interne getopt du shell. Qu'apport de plus auxilium ?

    • [^] # Re: getopt(1)

      Posté par  . Évalué à 10 (+7/-0). Dernière modification le 15 février 2024 à 19:10.

      C’est getopts, avec un s le builtin du shell. Non ?

      Accessoirement c’est un composant de la norme POSIX. J’étais au courant de son existence et ce n’est certainement pas le fait que getopt ne soit pas POSIX qui m’avait fait l’abandonner, en faveur de getopts, parce que POSIX ça a son utilité mais c’est vraiment rébarbatif par rapport à un shell moderne, mais il y avait je ne sais plus quel point qui m’avait chagriné à l’époque où j’avais fait mon choix. Il faudra que je me penche à nouveau sur la question.

      En fait, ça fait bien longtemps que j’écris des scripts shell, en bash, et je me suis rendu compte que c’était le langage que j’utilisais le plus. Comme par ailleurs « tout le monde » lui prête une réputation de langage préhistorique au fonctionnement absolument horrible, ça me donne encore plus envie de le connaître bien ^^. Depuis au moins dix ans j’écrivais des scripts avec ce que j’avais appris jusqu’alors, découvrant encore parfois de temps en temps, de manière fortuite, une nouvelle fonctionnalité, mais sans chercher à en apprendre plus.

      Et bien, que la suffisance est une vilaine maladie en effet ! Je me souviens que de nombreuses années auparavant j’avais eu vent du « strict mode ». Je ma rappelle alors qu’à l’époque j’avais jugé cela comme une complication loin d’être indispensable et avais soigneusement oublié l’existence de cette « convention » (qui est plus que ça au final). Aujourd’hui, avec les années d’expériences acquises, quand j’ai relu de quoi il s’agissait, j’ai eu envie de mettre des baffes au moi du passé. Rien que l’argument de la nécessité d’activer l’option errexit (un des quatre points constituant ce qu’on nomme le « strict mode », qu’on pourrait d’ailleurs aussi appeler le « script mode » tout simplement) me semble aujourd’hui tomber sous le sens. Comment ai-je pu faire du Perl, du Python et trouver tout naturel qu’une erreur de syntaxe (par exemple), arrête l’exécution, tout en trouvant tout aussi normal que Bash puisse se comporter comme un canard à qui on a coupé la tête mais continue de marcher ? À l’évidence on supporterait difficilement de devoir se reloger à chaque fois qu’on fait une faute de frappe, ou qu’on essaye de supprimer un fichier inexistant, ou créer un répertoire qui existe déjà, pour prendre quelques exemples, quand on est en mode interactif, mais quelle idiotie totale de conserver ce comportement quand on écrit un script… Ce ne sont pas les quelques adaptations à apporter à sa façon de coder que cela implique qui justifient de ne pas activer ces option. Enfin, pour celui que j’étais à 20 ans il faut croire que si _o_.

      Si tu trouves que je parle chinois cher lecteur ayant à écrire des scripts bash (et je suppose que zsh et ksh sont concernés aussi, peu ou prou à l’identique), rends-toi ce service, rends le à toute la profession, prends le temps de considérer http://redsymbol.net/articles/unofficial-bash-strict-mode/ C’est un peu chiant au début car ça force à modifier quelques automatismes qu’on a pu acquérir, mais sans le moindre doute, même après des années de déformation professionnelle j’ai réussi à m’y habituer rapidement et c’est effectivement bien plus confortable.

      Je me demande combien de gens qui écrivent des scripts utilisent systématiquement ces options. Dans ma vie professionnelle je n’en ai jamais rencontrer. Ce serait même plutôt le contraire, comme des gens qui mettent systématiquement export devant une déclaration de variable, dont un qui l’a fait devant moi et, que j’ai pu interroger sur le moment et qui m’a répondu : « parce que des fois sans ça marche pas », et c’était une personne plus « expérimentée » que moi. Ou les éternels grep truc | wc -l, les echo lignes par lignes, et autres joyeusetés…

      Et à contrario quand je lis certaines personnes, comme ici ou sur stackoverfow par exemple, ou même que je lis pour la première fois une partie du manuel et que je réalise que je faisais de la merde, je suis loin de penser que je maîtrise la chose.

      Par exemple je viens de découvrir les builtins : enable, caller (et declare pas très longtemps auparavant bien que je connaisse local depuis un moment), et help bordel ! Qu’est-ce que j’ai pu man bash | grep -C5 truc comme un con ! /o\

      Faites ce que vous voulez mais RTFM! On le dira jamais assez ! ^^

      • [^] # Re: getopt(1)

        Posté par  . Évalué à 4 (+1/-0). Dernière modification le 15 février 2024 à 19:22.

        toto=$(cat fichier | grep truc | wc -l) suivi d’un if [ $toto -eq 1 ] … bien sûr tant qu’à faire :)

        • [^] # Re: getopt(1)

          Posté par  (site web personnel) . Évalué à 2 (+0/-0).

          UUOC, non ?

          toto=$(grep truc fichier | wc -l)

          un p'tit coup de shellcheck sur tout ça…

          Proverbe Alien : Sauvez la terre ? Mangez des humains !

          • [^] # Re: getopt(1)

            Posté par  . Évalué à 2 (+0/-0).

            Un peu plus

            grep -q truc fichier
            if [ $? -eq 1 ]; fi
            # …

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: getopt(1)

            Posté par  (site web personnel, Mastodon) . Évalué à 3 (+1/-0). Dernière modification le 16 février 2024 à 20:14.

            Encore mieux : toto=$(grep -qc truc fichier)
            et si le résultat doit être testé : if [ $(grep -qc truc fichier) -ne 0 ]

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

            • [^] # Re: getopt(1)

              Posté par  . Évalué à 4 (+1/-0). Dernière modification le 17 février 2024 à 00:10.

              Si je pouvais bosser qu’avec des gens doués comme vous ! ^^

              L’exemple que j’ai donné c’est ce qu j’ai pu voir, assez souvent même, dans un cadre professionnel. C’est des trucs que j’ai très sûrement fait moi-même en maternelle de shell. On dirait les exemples qu’on enseigne. Typiquement tu expliques cat, puis | et introduit grep et pour illustrer tu montres cat fichier | grep truc, après tu expliques wc, et pour montrer toute la puissance de la notion de pipe, qu’on peut chaîner, tu montres cat fichier | grep truc | wc -l. Etc… etc…

              Donc j’ai l’impression que bien des gens se sont arrêtés là… Mais loin de moi l’idée de les blâmer, enfin pas trop et pas tous ^^. Tout le monde n’a pas le goût de la programmation et on peut sûrement concevoir les métiers de l’informatique autrement, avec une vision plus clickodrome que ligne de code du bousin. Ça n’implique pas d’être débile pour autant, chacun ses forces et ses faiblesses, et surtout, sa vision des choses…

              • [^] # Re: getopt(1)

                Posté par  . Évalué à 4 (+2/-0).

                Les UUOC ne sont que des problèmes de puristes, ils ne posent de véritables problèmes que d'un point de vue esthétique. En interactif ils peuvent être très pratiques, par exemple si je construit un programme awk en plusieurs essais, mettre le fichier en début de ligne de commande est très pratique.

                cat ficher | awk '/pattern/'
                cat ficher | awk '/pattern/{a+=$3}END{print a}'
                #...

                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                • [^] # Re: getopt(1)

                  Posté par  . Évalué à 4 (+1/-0). Dernière modification le 17 février 2024 à 01:49.

                  En interactif ils peuvent être très pratiques

                  Oui, en interactif je ne réécris pas toute ma commande si je veux la relancer avec un grep dessus… Donc j’UUOC sans honte comme tout le monde, comme tu dis c’est très pratique.

                  Les UUOC ne sont que des problèmes de puristes, ils ne posent de véritables problèmes que d'un point de vue esthétique.

                  Là par-contre je m’inscris en faux. C’est d’abord un problème si ton script, ou ta fonction dans ton script, est amenée a être exécutée « plein de fois ». Une instruction qui prend 15ms au lieu de 2ms ça change rien si tu l’exécutes une seule fois. Si c’est quelques millions, ou même quelques milliers de fois, ça commence à avoir son importance.

                  Donc clairement, ce n’est pas une question purement esthétique.

                • [^] # Re: getopt(1)

                  Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0).

                  par exemple si je construit un programme awk en plusieurs essais, mettre le fichier en début de ligne de commande est très pratique.

                  < /etc/passwd awk -F : '/tth/ { print $3 }'

                  • [^] # Re: getopt(1)

                    Posté par  . Évalué à 5 (+3/-0).

                    cat n'est pas sale, hein ? Il n'y a pas besoin de faire des pieds et des mains pour l'annihiler. D'autant que généralement j'utilise head lors du prototypage.

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: getopt(1)

                      Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0).

                      La réponse de Tonton ne fait pas des pieds et des mains (au contraire c’est kiss) et ne cherche pas à annihiler cat.

                      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

                    • [^] # Re: getopt(1)

                      Posté par  (Mastodon) . Évalué à 2 (+2/-1).

                      Personne n'a dit que cat était sale, mais c'est juste que sa fonction initiale n'est pas de rediriger le contenu d'un fichier sur la sortie standard

                      …comme son nom l'indique, cat c'est le raccourci de concatenate qui sert donc à concaténer plusieurs fichiers (accessoirement sur stdout)

                      et donc, quitte à éviter un sous-shell via le pipe, autant faire simple

            • [^] # Re: getopt(1)

              Posté par  . Évalué à 3 (+0/-0). Dernière modification le 17 février 2024 à 00:28.

              Pour ma part, en tout cas dans le cas où un test est nécessaire. Généralement c’est pour conditionner l’exécution d’une autre commande ou d’une fonction. Alors à moins d’être dans le cas d’une « conditionnalité complexe » (ie: un if … elif … else … fi), je fais :

              grep -q truc fichier || action

              Le || pour coller à ton exemple avec -ne 0, mais si c’est au contraire -eq 0 (ie: on fait l’action si la condition est "success"), alors la même chose avec &&.

              Je fais parfois même des condition || { action1; && action2; } (ou l’inverse…) mais j’avoue que là ça devient assez illisible et il vaut mieux recourir à un if même si on peut faire sans.

              Flemme de vérifier (et pas sûr que ça change quoi que ce soit à l’exécution…) mais si c’est la condition “ya des lignes ou pas ?” qui t’intéresse, le -c de grep n’est même pas nécessaire :) Et l’intérêt à ne pas le mettre dans ce cas ce serait qu’à la simple lecture du début de la ligne, juste les options du grep, tu sais qu’on ne s’intéresse pas aux nombres de lignes qui matchent mais juste s’il y en a ou pas.

              • [^] # Re: getopt(1)

                Posté par  . Évalué à 2 (+0/-0).

                M’enfin, si tu en es à fuir cat pour des raisons de performance parce-qu’il te coûte une très précieuse milliseconde à se lancer, tu peux passer -l comme argument à grep pour éviter de lire tout le fichier s’il trouve ta correspondance à la deuxième ligne. Ça te fera faire plus d’économies que quelques UUOC qui ne coûtent pas grand chose en conditions réelles.

                • [^] # Re: getopt(1)

                  Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

                  Dans le cas d’usage, le simple -q le fait bien (i.e. -l implicite)

                  “It is seldom that liberty of any kind is lost all at once.” ― David Hume

                • [^] # Re: getopt(1)

                  Posté par  . Évalué à 5 (+2/-0). Dernière modification le 25 février 2024 à 00:52.

                  C’est plus le fork lié au pipe lié à l’utilisation de cat dans ce cas.

                  une très précieuse milliseconde à se lancer

                  « Se lancer » regroupe plusieurs étapes mais je ne vais pas détaillr. Un fork est un fork et c’est ça qu’il est important de limiter, car il a un coût, c’est peut-être même inférieur à la milliseconde mais ça ne change strictement rien à ce que je dis : 500µs * 10000000 = 50s

                  tu peux passer -l comme argument à grep pour éviter de lire tout le fichier s’il trouve ta correspondance à la deuxième ligne.

                  C’est une très bonne remarque, mais d’une : le « plus d’économie » il dépend de la taille du fichier, vu qu’on ne l’affiche pas, s’il fait 4k et qu’il est en RAM la différence avec le fork nécessaire est pas si évidente que ça. Par ailleurs comme très justement signalé plus bas, le fait d’utiliser -q (sans le -c) fait que le fichier ne sera lu que jusqu’à la première occurrence, le -l est superflu. Utiliser le -q sans le -c n’est donc pas seulement avantageux en terme de lisibilité du code comme je l’affirmait à tort.

                  en conditions réelles.

                  Une fois de plus, si les conditions réelles sont de traiter 10 fichiers de 1M lignes et de faire ça 48 fois par jour, ça fait une différence significative.

                  Je finis sur deux points car je sais qu’on peut/va me les opposer :

                  1) Si le traitement doit être lancé aussi souvent sur de tels volumes alors on gagnera davantage à écrire le traitement dans un langage compilé. Sur cette remarque qui paraît à beaucoup relever du bon sens même, j’ai deux choses à faire remarquer :

                  • Le développement, le déploiement et la maintenance d’un langage compilé a une complexité supérieure dans bien des cas et demande une charge de travail plus importante et des compétences différentes, compétences qui sont à ma connaissance plus rares en terme de ressource humaine, plus coûteuses, et potentiellement utilisables (et utilisées) sur des travaux qui ont souvent une plus forte valeur ajoutée pour l’entreprise.
                  • Si c’est pour pondre un code C aussi mal optimisé que le script Bash qu’il remplace, le gain peut même être inférieur à celui qu’une bonne optimisation du Bash existant (si c’est par exemple l’algorithme qu’on aurait pu optimiser, où l’ajout d’un cache, etc…)

                  Tout ça pour dire que passer d’un langage de script très connu à un langage compilé pour uniquement résoudre un problème de performance qu’on peut résoudre simplement en corrigeant l’écriture imparfaite d’une script shell existant (ie: en l’optimisant) je doute que ce soit pertinent très souvent, et clairement pas une évidence.

                  2) Tout ce que je dis sur les UUOC et autres constructions non optimales dans un script Bash c’est évidemment préférable de les éviter dès le départ. Mais, premièrement on ne les évitera pas toute, donc une phase d’optimisation final une fois l’aspect fonctionnel du script terminé est toujours la bienvenue. De ce fait, une trop grande attention portée à l’optimisation et temps passé sur le sujet dans les phases initiales du développement est contre-productive. Et ça ça ne concerne pas que les langages interprétés.

                  Tout en évitant de partir sur une architecture logiciel qui arrivera immanquablement à un programme trop lent car inadaptée. Il faut mieux un programme qui répond au cahier des charges sur les fonctionnalités malgré qu’il soit trop lent, et ainsi pouvoir se concentrer sur son optimisation en ayant une vue d’ensemble. Que d’étudier dans le détail la meilleure manière d’écrire telle fonction bas-niveau alors qu’on a fait genre à peine 20% des fonctions de plus haut niveau… C’est un travers assez courant chez tout développeur (sauf ce pour qui l’optimisation n’est même pas une pré-occupation à un seul instant, évidemment ^^), moi le premier, mais dont il s’agit de chercher à réduire l’importance.

                  • [^] # Re: getopt(1)

                    Posté par  . Évalué à 3 (+1/-0).

                    Je finis sur deux points car je sais qu’on peut/va me les opposer

                    Tu prend perl il aura le même coût de maintenance, mais n’exécutera aucun fork. Si le coût d'un fork est aussi primordial il ne faut pas utiliser de script shell. Le plus beau pansement que tu aura ne servira à rien sur une jambe de bois. Si comme tu le dis quelque soit le coût du fork tu va fait un million de fois, tu as déjà ton grep qui fork un million de fois. Pire en shell posix tu n'es pas sûr que [ soit un builtin, ça peut très bien utiliser /usr/bin/[.

                    Vraiment si le coût du fork t'es insupportable, rend-toi service, apprend perl. Tu ne sera pas dépaysé et si le coût des fork est effectivement sensible ça va être le jour et la nuit.

                    Pas d'« architecture logiciel » différente de ce que tu fais déjà, pas de compilation à gérer, obtenir le mode strict se fait avec une simple ligne en début de fichier use strict;, tout en gardant une grande facilité de fork pour utiliser un programme particulier,… et évidement perl et LA rolce des expressions régulières.

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: getopt(1)

                      Posté par  . Évalué à 3 (+0/-0). Dernière modification le 29 février 2024 à 20:46.

                      J’ai écrit un seul script « pour de vrai » en Perl et ce n’était pas pour une autre raison (ie: ce choix de Perl plutôt que Bash ou Python) que m’initier à ce langage dont je sais à sa réputation que c’est un langage qui compte. Je ne savais pas que Perl ne faisait « aucun fork » (sauf j’imagine dans le cas où tu veux avoir du parallélisme, et comme tu l’indiques, pour appeler un autre programme évidemment), je note cet aspect.

                      tu n'es pas sûr que [ soit un builtin, ça peut très bien utiliser /usr/bin/[.

                      D’où la recommandation d’utiliser systématiquement [[. Ou c’est pour une autre raison ? je ne sais plus, peut-être pour une autre raison mais sauf erreur c’est recommandé d’utiliser [[ systématiquement.
                      Par ailleurs en Bash (je ne sais pas pour ksh et zsh mais il y des chance qu’il en soit de même) tu peux t’assurer que [ et test appellent bien les built-in avec la commande enable.

                      Vraiment si le coût du fork t'es insupportable, rend-toi service, apprend perl.

                      Comme je te disais je m’y suis initié, je connais les grands principes et si je dois faire du Perl ça ne me pose aucun problème, c’est un beau langage. C’est pas comme si je me retrouvais obligé de faire du PowerShell par exemple (et ça m’est arrivé, je ne dis pas ça gratuitement selon un bête préjugé). Maintenant, je préfère le paradigme "tout fichier" de Bash à la manipulation de pointeurs et je ne peux de toute manière pas explorer à fond les deux langages (surtout qu’il me faut du temps pour explorer le Chuck, qui est encore d’un type bien différent, et que j’ai très peu de chance de mettre en œuvre dans un contexte devops ^^ (mais qui sait…)).

                      Sans être insupportable, si on peut faire en Bash un traitement en 1 seconde (et que c’est un temps acceptable dans le contexte) au lieu de 10 (et qui serait un temps tout aussi acceptable dans le même contexte) en évitant des forks inutiles, le fait qu’on puisse faire ce traitement en 0,1 s en Perl, et que même à 10 secondes tout le monde est content, ce n’est pas une raison ni pour passer à Perl ni pour se foutre que son script Bash en prenne 10. Et j’ai bien conscience du caractère légèrement maniaque de cette philosophie. Tout comme des problèmes que peut poser un souci d’optimisation précoce.

                      Pour tout te dire, je suis en train de développer un programme, en Bash, sans cahier des charges très précis (c’est pas pour le boulot, c’est expérimental et a clairement le caractère d’un loisir) dont je sais, sans pratiquement aucun doute, que si j’arrive à un truc qui ressemble à l’idée flou que j’ai en tête, je devrais me heurter aux limitations de la vitesse d’exécution de Bash (pas réputé comme le plus rapide parmi les shells par ailleurs). Je verrai bien ce que ça donne mais je me dis que si à ce moment là je veux dépasser cette limitation, ce sera une excellente occasion de refaire du C, ou m’initier à un autre langage compilé (Rust par exemple, mais je pense que je partirai plus sur du C). Parce que si j’ai un programme en Bash qui fonctionne, j’aurais tout de même un prototype pour savoir où je veux aller avec mon programme en C. Et quitte à devoir manipuler des pointeurs… :)

                      • [^] # [ [[

                        Posté par  (site web personnel, Mastodon) . Évalué à 3 (+1/-0).

                        Ou c’est pour une autre raison ? je ne sais plus, peut-être pour une autre raison

                        Quand tu es en BASH, utiliser directement [[ permet d’éviter certains écueils de test à moins d’être tatillonne avec les variables ;- (c’est une commande interne introduite par KSH88 et est connu de ZSH aussi.)

                        tu n'es pas sûr que [ soit un builtin, ça peut très bien utiliser /usr/bin/[.

                        À noter que test est bien une commande interne depuis System III en 1981 (en même temps qu’à été introduit le synonyme [ …retenu par POSIX qui indique « may be implemented as a single linked utility ») La/le spécification/standard POSIX permet que toutes les commandes internes puissent exister comme fonction ou comme binaire séparé : ce dernier cas est utile surtout pour les autres interpréteurs (moyennant quelques arrangements/réécritures, je me souviens d’avoir pu avoir des scripts qui Bourne qui pouvaient être traités aussi par CSH qui par contre va faire plus d’appels) ; et à moins d’indiquer le chemin absolu, éventuellement via un alias, c’est toujours la commande interne qui est prioritaire.

                        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

                      • [^] # shell prototype 4 C

                        Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0).

                        Parce que si j’ai un programme en Bash qui fonctionne, j’aurais tout de même un prototype pour savoir où je veux aller avec mon programme en C.

                        Si tu prototypes, avec l’idée de réécrire en C, repose-toi plutôt sur AWK (mais c’est un avis qui n’engage que moi.)
                        Tu peux directement utiliser PERL aussi, mais l’écrire de façon à pouvoir faire la traduction rapide en C ne produit pas un script très lisible (je trouve.) C’est la force de ce langage (le fameux « mille façons de faire » est sa capacité à s’adapter aux gens venant d’autres langages et paradigmes) et la source des reproches que lui font les gens qui ne connaissent pas.

                        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

      • [^] # Re: getopt(1)

        Posté par  . Évalué à 1 (+1/-2).

        Si tu trouves que je parle chinois cher lecteur ayant à écrire des scripts bash (et je suppose que zsh et ksh sont concernés aussi, peu ou prou à l’identique), rends-toi ce service, rends le à toute la profession, prends le temps de considérer http://redsymbol.net/articles/unofficial-bash-strict-mode/

        C'est un peu comme dire qu'il faut utiliser les extensions gcc/llvm defert quand on code en C.

        Si tu préfère que ton langage de script se comporte comme python ou perl, surtout ne pas hésiter à les utiliser ils sont là pour ça.

        J'utilise le strict mode dans certains cas, mais je n'en fait pas une règle. Et si le script grossi et doit durer dans le temps, je vais plutôt utiliser perl/python/ruby.

        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

        • [^] # Re: getopt(1)

          Posté par  . Évalué à 6 (+3/-0).

          utiliser perl/python/ruby.

          Il y a clairement des avantages, mais qui viennent bien sûr avec quelques inconvénient. Dans les avantages je pense d’abord au fait de la cohérence du langage, surtout pour Python, mais Perl est aussi exemplaire sur ce point (bien que les deux soient absolument différent, et à condition de ne pas recourir excessivement à la puissance d’abstraction de Perl ^^). Par rapport au shell(s) Unix c’est le jour et la nuit.

          Par contre pour « qui doit durer », je pense que ça dépend de si on parle de « qui doit être étendu souvent, par de nombreuses personnes » ou « qui doit être laissé dans un coin et voir passer les upgrades systèmes sans demander la moindre attention ». Dans le second cas Python est pas ouf, d’expérience, à partir du moment où on utilise quelques lib externes ça explose fréquemment à la version d’OS N+1. Pas le shell.

          Je trouve que la taille du code joue beaucoup aussi, la programmation objet de Python, dès que le programme devient relativement complexe, ça aide énormément, en plus de la syntaxe et du paradigme beaucoup plus cohérent auquel je faisais allusion précédemment.

          • [^] # Re: getopt(1)

            Posté par  . Évalué à 2 (+0/-0).

            Perl est vraiment fou pour ce travail, il ne surprend pas celui qui vient du shell, peut faire des unilignes au moins aussi fou qu'un shell, a une stabilité à toute épreuves, il n'est pas limitant (va dessiner des Qrcode en shell),…

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

            • [^] # Re: getopt(1)

              Posté par  . Évalué à 5 (+2/-0).

              il n'est pas limitant (va dessiner des Qrcode en shell)

              Je pense qu’on touche là à un point central, qu’est-ce que "le shell" désigne si on parle programmation ?

              Est-ce uniquement Bash (ou zsh, etc…) ou bien est-ce qu’on n’y inclus l’ensemble des binaires disponibles ? Bash et les autre shells sont des langages que je qualifierais de « glue », sans binaires comme curl ou grep ou find etc… on ferait pas grand chose.

              Les « bibliothèques » du shell ce sont les « standalone » que tout bonne bibliothèque fournie systématiquement.

              • [^] # Re: getopt(1)

                Posté par  . Évalué à 2 (+0/-0).

                Tu n'aura jamais la souplesse de bibliothèques même avec tous les exécutables de ta distribution (si tu n'utilise pas un autre langage que tu donne à manger à un compilateur ou un interpréteur) et ce que tu arrivera à faire sera bien plus douloureux.

                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                • [^] # Re: getopt(1)

                  Posté par  . Évalué à 3 (+0/-0).

                  si tu n'utilise pas un autre langage que tu donne à manger à un compilateur ou un interpréteur

                  Alors je mets de côté les langages compilés qui ont indéniablement des avantages sur les interprétés. Mais je ne vois pas ce que tu veux dire si tu parles d’interpréteur. Bash est au même titre que Perl et Python un langage interprété. Le fait que Python expose l’étape intermédiaire du bytecode ne change pas grand chose.

                  En d’autres termes, en quoi Perl et Python seraient de véritables langages interprétés et pas Bash ?

                  Les « exécutables de ma distribution » ils ont cet avantage sur les bibliothèques Python d’être nettement plus stabilisés en terme d’API… Sans parler de la quasi trivialité de la mise en place de « l’environnement d’exécution », qui s’il n’est pas très complexe dans le cas de Python, encore moins de Perl, n’est pas aussi trivial que les binaires qui constituent l’environnement d’exécution d’un script Bash.

                  Ça fait combien de temps que pip/pip3 ne permet même plus de faire un "search" ?

                  • [^] # Re: getopt(1)

                    Posté par  . Évalué à 3 (+1/-0).

                    Mais je ne vois pas ce que tu veux dire si tu parles d’interpréteur.

                    Ce que je veux dire c'est que si tu veux dire que bash fais aussi bien que python avec un script bash qui consiste à donner un code à manger à cpython, la comparaison ne tiens pas (même si c'est une solution qui peut être viable).

                    En d’autres termes, en quoi Perl et Python seraient de véritables langages interprétés et pas Bash ?

                    Ce n'est pas ce que je dis et je ne comprends pas d'où vient l'incompréhension. Tu demande ce qu'est la définition d'un script shell et je dis que la comparaison peut tenir tant que tu ne te sert pas d'un autre langage pour implémenter la dite fonctionnalité.

                    Les « exécutables de ma distribution » ils ont cet avantage sur les bibliothèques Python d’être nettement plus stabilisés en terme d’API…

                    Tu parle de POSIX (plus quelques trucs), moi je te parle de tout ce qu'il y a dans les dépôts de ta distribution y compris exa par exemple qui est abandonné après une courte vie.

                    Sans parler de la quasi trivialité de la mise en place de « l’environnement d’exécution », qui s’il n’est pas très complexe dans le cas de Python, encore moins de Perl, n’est pas aussi trivial que les binaires qui constituent l’environnement d’exécution d’un script Bash.

                    Bof ils ont tous leurs avantages et leurs inconvénients et tu as pour chacun pleins de façons de t'en sortir ou de te tirer une balle.

                    Pour mes besoins avec chacun des 3, j'utilise apt et le dépôt de ma distribution pour gérer les dépendances donc c'est littéralement identique (je ne remercierai jamais à leur juste valeur les gens qui font vivre ma distribution).

                    Si je sais que je vais partir dans un contexte ou je ne veux pas m'embêter avec les dépendances au déploiement j'utilise des binaires statistiques et je n'ai plus qu'à vérifier la libc.

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: getopt(1)

                      Posté par  . Évalué à 4 (+1/-0). Dernière modification le 21 février 2024 à 22:38.

                      bash fais aussi bien que python avec un script bash qui consiste à donner un code à manger à cpython

                      Je pense d’abord aux utilitaires tels que bc ou find, pour ne citer que ceux-là, qui sont définis dans POSIX et pas implémentés en Python. Je considère qu’il font parti du « langage » au sens large. Comme par exemple la bibliothèque "sys" fait partie de Python.

                      Le shell a été pensé comme un outil destiné à articuler ces autres programmes (les articuler autour du noyau), programmes implémentés en C pour des raisons évidente de performance. Je ne t’apprends rien.

                      Si tu utilise un binaire écrit en C ou autre langage, ou un script dans un autre langage, serait-ce du python, tu ne cesses pas pour autant de « faire du shell ».

                      Dirais-tu que tu cesses de faire du Python si tu utilises numpy ?

                      La différence que je vois c’est qu’en Bash tu appelles rarement d’autres outils « pure bash », au contraire de Python où tu vas trouver des bibliothèques « pure Python » (paramiko par exemple). Mais ça finira toujours par exécuter du C à un moment (cpython et bash étant eux-mêmes implémentés en C), même de l’assembleur si on continue le raisonnement… je ne trouve pas que ce genre de considération a le moindre de sens pour distinguer les deux langages. Ah si, peut-être celle-ci : pour reprendre l’exemple de paramiko, vu que c’est implémenté en Python tu peux modifier ce composant tout en continuant à à « parler Python », tu peux surcharger par exemple. Et voilà comment on en arrive à ce que je disais : des scripts Pythons qui pètent à un moment donné parce que celles et ceux qui l’ont écrit ont surchargé telle ou telle bibliothèque et que les chances que cette surcharge résiste à l’évolution de la bibliothèque s’amenuisent avec le temps, d’où on se retrouve avec des requirements.txt longs comme le bras.

                      Évidemment on peut faire le « sagouin » tout pareil en shell en faisant reposer son script sur un binaire à telle version ou compilé avec telles options, on peut… mais :

                      • Bash n’incite pas à cela puisque ça implique soit de compiler du C, soit « faire du Python » ou autre

                      • Je pense qu’on peut comparer la stabilité de l’interface du binaire ssh, mise en œuvre de référence de libssh (ie: OpenSSL en pratique bien souvent…) et livré avec celle-ci, et la stabilité de l’interface de paramiko, je ne suis pas sûr que paramiko soit aussi figé (quoi qu’aujourd’hui j’ose espérer que c’est plutôt le cas) que le premier.

                      Donc pour conclure, l’un dans l’autre, Perl, Python et Bash (leurs écosystèmes) ont des différences d’architecture mais fondamentalement aucune : ce sont des langages interprétés, point barre. C’est juste que le « système d’import » de Bash c’est… le systèmes de fichiers ! ;)

                      tu as pour chacun pleins de façons de t'en sortir ou de te tirer une balle.

                      D’accord suivant ce que je viens de dire, mais le danger de balle dans le pied n’est pas de même nature je dirais.

                      Si je sais que je vais partir dans un contexte ou je ne veux pas m'embêter avec les dépendances au déploiement j'utilise des binaires statistiques et je n'ai plus qu'à vérifier la libc.

                      Et voilà comment un Pythonien se retrouve à faire du shell ! ^^

                      Avec assez d’ouverture d’esprit je pense d’ailleurs qu’on pourrait utiliser Python ou Perl comme on utilise Bash, comme shell interactif pour les opérations quotidiennes, manuelles, comme regarder le contenu d’un répertoire, supprimer un fichier, etc… Ce qui montre bien que les trois ne sont fondamentalement pas différents.

                      • [^] # Re: getopt(1)

                        Posté par  . Évalué à 2 (+0/-0).

                        On ne se comprends définitivement pas. Un peu de code sera plus clair :

                        #!/bin/sh
                        
                        python3 - <<EOF
                        import re
                        
                        string = 'This is a string'
                        pattern = '^[A-Z]'
                        
                        found = re.match(pattern, string)
                        
                        if found:
                            print("The input value is started with the capital letter")
                        else:
                            print("You have to type string start with the capital letter")
                        EOF

                        Je décrivais plus haut le shell come :

                        1. tu as le language qui est pareil queue pour tout autre langage
                        2. tu as les binutils globalement qui sont la bibliothèque standard du langage
                        3. tu as les autres exécutables qui sont des bibliothèques autres comme tu as des bibliothèque dans tous les langages
                        4. tu as dans tous les langages ou presque la possibilité d'écrire un programme dans un autre langage et de l'executer comme dans mon exemple le shell écris du python et l'execute

                        Ce dernier point, bien que très utile, n'est pas intéressant dans le cadre d'une comparaison de langages tel qu'on le discutait à la base.

                        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                        • [^] # Re: getopt(1)

                          Posté par  . Évalué à 3 (+0/-0).

                          OK, merci d’avoir pris le temps de m’expliquer.

                          Je ne comprenais pas que tu parlais du point 4 puisque je ne conçois même pas ça comme une option qui puisse être pertinente dans un seul cas…

                          J’en déduis que j’ai eu la chance d’être épargné par une elle absurdité jusqu’ici mais grace à toi je sais que ça existe alors si je tombe un jour sur ce genre de truc je serais moins déstabilisé !

                          Ce dernier point, bien que très utile,

                          Quand ÇA s’avère « utile » (pour livrer un truc qui marche) c’est que tout le projet s’apparente à un château de cartes en papier toilette humide à mon avis ^^

                          • [^] # Re: getopt(1)

                            Posté par  . Évalué à 3 (+0/-0).

                            Un bout d’assembleur dans un code C, à la rigueur. exceptionnellement. Mais mélanger ainsi du Python et du Bash, non, ça mérite l’empalement.

    • [^] # Re: getopt(1)

      Posté par  . Évalué à 1 (+0/-0).

      Il apporte en plus la génération automatique de l'aide en ligne en rajoutant l'option -h|--help et la génération d'un fichier d'auto-complete pour bash et zsh.

      Dans la TODO list, il y a aussi la gestion des subcommands.

  • # vs argbash

    Posté par  (site web personnel, Mastodon) . Évalué à 5 (+3/-0).

    Il existe un projet plus ancien et assez éprouvé, argbash. Faut voir comment ça se compare avec.

    “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: vs argbash

      Posté par  . Évalué à 5 (+2/-0). Dernière modification le 16 février 2024 à 01:25.

      Over 84.6% of Bash scripts you can find on the Internet don't accept command-line arguments, which greatly limits their usefulness. This has a reason - Bash doesn't make command-line argument parsing easy.

      Le dernier script que j’ai écrit, un truc perso. J’ai pas voulu « me faire chier » avec les options, je me suis dit que des paramètres positionnels ferait l’affaire, au moins dans un premier temps. Après tout, il est toujours possible de spécifier "" pour un paramètre si on veut laisser la valeur par défaut. La seule contrainte c’est de se souvenir de l’ordre de ceux-ci.

      J’en suis à onze… amendoné va falloir que je fasse quelque chose. Ceci dit, quand on utilise le script de manière intensive c’est pas insurmontable… Mais une autre personne, ce qui inclus son moi futur qui voudra utiliser à nouveau le script après avoir cessé de le faire pendant une certain temps, elle risque une atrophie capillaire potentiellement conséquente. :/

      Pour en revenir à la phrase que j’ai citée et qui est issue du site pointé par Gil Cot : je ne trouve pas que ce soit plus facile en Python (et en Perl j’en pas fait assez pour avoir un avis). Le peu d’expérience que j’ai avec d’autres langages me permet pas d’avoir un avis sur ceux-ci non plus. Mais ce que je veux dire : c’est une problème intrinsèquement complexe… et si un des paramètre est de nature à être une liste ? Et si je veux que si telle option est présente, telle autre soit ignorée ou interdite (et est-il plus judicieux de l’ignorer ou de l’interdire ?), et surtout : si je veux une option qui puisse optionnellement prendre une valeur ? (ie: option absente → cas 1, option présente sans valeur → cas 2, option présente avec une valeur → cas 3). Mixer tout ça, tout en souhaitant laisser aux utilisateurs la possibilité de les spécifier dans l’ordre qu’ils souhaitent ? ça peut devenir vite un facteur criminogène !

      Je pense à le gestion des options de ffmpeg pour ceux qui connaissent. L’implémentation ne doit pas être triviale à mon avis. Sachant qu’en plus le comportement, les possibilités d’option peuvent varier selon le paramétrage de la compilation…

      • [^] # Re: vs argbash

        Posté par  (site web personnel, Mastodon) . Évalué à 5 (+3/-0).

        Les paramètres positionnels font très bien l’affaire …dans un premier temps :) Bon, sous réserve de se souvenir de l’ordre et qu’il y en ait pas trop… Et je pense que onze c’est beaucoup. Mes trucs quand j’utilise les paramètres positionnels dans mes scripts :

        • Faut pas qu’il y en ait trop (j’essaye de ne pas dépasser trois) ; c’est plus facile pour la mémoire et pour l’aide.)
        • Je les renseigne dans une cartouche de commentaire en début (je dois avoir quelques exemples sur LinuxFr) et je prévois un message d’aide (du basique : juste échouer dès le début avec le rappel de la syntaxe quand on n’a pas tous les arguments ou que ceux-ci ne correspondent pas aux motifs attendus…)
        • À partir d’un certain nombre (strictement plus de deux), j’essaie de prévoir un mode interactif : si le Nième paramètre est absent, faire un prompt et lire la réponse plus prévoir de pouvoir s’arrêter.
        • Je classe les paramètres par ordre d’importance avec les obligatoires au début, puis les optionnels (avec une valeur par défaut précisée dans l’aide)

        Dès que l’ordre doit être libre ou quand il y a beaucoup de paramètres (qui implique le premier point pour la tranquillité d’esprit), go getops parce-que je fais essentiellement du POSIX. Si on n’est pas concerné par ce dernier point, autant utiliser le truc plus évolué de son shell (soit getop pour bash —mais la commande est disponible aussi via util-linux pour les autres interpréteurs—, zparseopts pour zsh, le getops de ksh93, etc.) avec le support des options longues et autres détails.

        Perl a un module Getopt::Long inclus de base un peu partout.

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: vs argbash

          Posté par  . Évalué à 3 (+0/-0).

          Je les renseigne dans une cartouche de commentaire en début

          Je l’ai fait aussi sinon ce serait même pas la peine… Et c’est le fait de devoir faire des head -n20 avant de lancer le script qui me rappelle que ça devient critique. :) (je sais que le "n" pour la commande head n’est obligatoire, mais comme il l’est quand il y a d’autres options je le mets systématiquement…)

          j’essaie de prévoir un mode interactif

          Alors ça c’est le truc qui me vient jamais à l’esprit et que j’ai du mal à envisager, mais sans aucune bonne raison.

          getop pour bash

          Faut vraiment que j’aille voir pourquoi j’avais décidé de ne plus utiliser ce truc. Vu que c’est grosso modo la même époque où j’ai jugé que le strict mode c’était bof, il y a des chances que ma décision ait été tout aussi conne concernant getopt. :)

          • [^] # Re: vs argbash

            Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0).

            Faut vraiment que j’aille voir pourquoi j’avais décidé de ne plus utiliser ce truc.

            Peut-être comme moi, pour être POSIX/portable ?
            Peut-être juste que tu n’as pas eu besoin des plus ?

            c’est le truc qui me vient jamais à l’esprit et que j’ai du mal à envisager, mais sans aucune bonne raison.

            Je pense qu’il y a une question d’habitudes/historiques. Je m’explique.

            J’ai utilisé beaucoup de programmes interactifs aux interfaces frustres et que l’on pouvait pourtant mettre dans des scripts : ed, fdisk, …
            J’ai utilisé pas mal de programmes qui peuvent s’utiliser avec des paramètres bien sentis ou interactivement : sh ou ssh, mailx, …
            Du coup je pense aux deux usages. Mon moi du futur n’a pas toujours envie de chercher l’ordre des arguments avant d’utiliser la commande une fois en passant. Et un truc que j’ai initialement prévu pour s’exécuter de façon conversationnelle, j’ai envie à un moment de pouvoir l’utiliser dans un script ou un cron (donc il faut pouvoir lui passer les paramètres et non attendre devant de pouvoir lui donner la cuillerée.)

            Il y a aussi beaucoup de commandes classiques qui proposent une option ou un mode assisté : cp -i, rm -i, …

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

            • [^] # Re: vs argbash

              Posté par  . Évalué à 2 (+0/-0).

              J'aime pas personnellement les modes interactifs car ils nuisent à la répétabilité, immédiate ou bien plus tard.

              Je code surtout des scripts pour moi et dans ces cas là, je fais une aide interactive, mais elle ne conclus pas sur l’exécution du script, mais sur l'affichage de la commande complète. Avec zsh et print -z (auto-citation) on directement écrire dans le buffer de la prochaine commande :

              $ ma_commande
              # faire des choix avec des questions, curse, fzf ou autre
              $ ma_commande option1 option2| # le curseur se trouve au niveau du |

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: vs argbash

                Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

                C’est une bonne approche aussi, un assistant pour avoir les bons paramètres dans le bon ordre et tout :)
                C’est que pour ma part j’ai souvent besoin des deux modes (ça ne s’exclue pas) et si possible indépendant de tout shell (d’ailleurs je n’ai pas encore basculé sur zsh)

                “It is seldom that liberty of any kind is lost all at once.” ― David Hume

            • [^] # Re: vs argbash

              Posté par  . Évalué à 3 (+0/-0).

              Peut-être comme moi, pour être POSIX/portable ?
              Peut-être juste que tu n’as pas eu besoin des plus ?

              J’ai relu plus ou moins intégralement le manuel de getopt (enfin, du getopt qui est sur ma machine pour être précis), puis exécuté un help getopts.

              Sûr que getopt est plus « puissant », mais quelle complexité ! Je crois que ce n’était même pas une histoire d’être POSIX. Juste un désire de m’épargner un tel « merdier » :)

              Sinon il y a aussi cette méthode, basique, qu’il faudrait que je teste un jour :

              while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
                -V | --version )
                  echo "$version"
                  exit
                  ;;
                -s | --string )
                  shift; string=$1
                  ;;
                -f | --flag )
                  flag=1
                  ;;
              esac; shift; done
              if [[ "$1" == '--' ]]; then shift; fi

              (indiquée sur https://devhints.io/bash)

              Mais je crois que ça souffre du même souci que la méthode de transformer toutes les options longues en leur équivalent court avant de passer la main à getopts : si une valeur a la forme '-x' ou '--xxx' ça coince. (oui j’ai eu le cas)

              Je vais garder mes, maintenant 12 paramètres positionnels et attendre l’éclair de génie qui éclairera mon choix d’une gestion des options satisfaisante ! ^^

              Si c’était un script pour le boulot susceptible d’être utilisé par autrui je pense qu’actuellement ce serait getopts avec options courtes uniquement ! À 12 paramètres j’aurais encore de la marge avant d’être bloqué. E j’ai l’impression qu’arrivé à un tel nombre de paramètre faut aller vers des sous-commandes si on veut faire quelque chose un minimum ergonomique.

    • [^] # Re: vs argbash

      Posté par  . Évalué à 2 (+1/-0).

      Argbash est un utilitaire qui va prendre un template en entrée et générer un script bash en sortie à ce que je comprend.
      Il faut lui indiquer en commentaire au début du fichier de scripts principal toutes les options et arguments qui vont être utilisés.

      Auxilium, quand à lui, génère l'aide en ligne (il rajoute l'option -h|--help), peut générer des fichier d'autocomplete pour bash et zsh (il rajoute l'option --sh-complete).

      Il permet de rajouter des options/arguments dans des fichiers "fils", du moment que le fichier fils soit sourcé avant l'appel à auxilium_parse, ce qui permet de faire des fichiers de plugin rajoutant ses propres options par exemple.

      • [^] # Re: vs argbash

        Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

        Ça fait longtemps que je ne l’ai pas utilisé mais il me semble qu’on pouvait l’intégrer dans le script. Ceci dit, tu as tout à fait raison que le principe est d’utiliser un système descriptif via commentaires, et cette description est transformée en code sans avoir à se préoccuper des subtilités et limitations du shell. Cela implique aussi (de mémoire) l’aide ; par contre il n’y a pas de complétion.

        Merci pour le boulot.

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # Quoting

    Posté par  . Évalué à 7 (+4/-0). Dernière modification le 16 février 2024 à 01:59.

    Pas vraiment pris de le temps de regarder de près mais je pense que je le ferai. Juste lu le README, il y a une chose qui me fait tiquer :

    auxilium_parse $@

    Ne pas mettre de double quotes ici n’est-ce pas à coup sûr le meilleur moyen qu’une valeur passée à une option telle que 'j’ai un espace yo!' ou "moi aussi !" foute le bordel ?

    Si j’ai bien compris :

     "$@"
    

    a la particularité de se « développer » (? pas sûr du terme…) en "element1" "element2" "element3" … etc… alors que sans les doubles quotes, il est strictement équivalent à $*, et se développera en element1 element2 element3 …, donc sans quote entourant chaque élément.

    Jamais eu autant de prise tête avec les échappements en Python, j’ai l’impression qu’en shell c’est une des difficulté majeure de bien comprendre en quoi '' "" $'' sont différents, et que IFS est un élément clé (et strict mode pas une chose de seconde importance…).

    J’ai des souvenirs d’en arriver parfois à des triples échappement jadis… or il n’y a que le double échappement qui puisse être justifié dans certains cas, assez rares, mais quand tu triples échappe faut aller prendre l’air et faire une sieste ! ^^

    Au sujet de IFS, j’ai jamais compris l’utilisation d’un OLDIFS pour rétablir le truc après eu besoin de le modifier. Quand j’ai besoin de le modifier c’est généralement pour un un read, et en faisant IFS='caractère ad-hoc' read … à la suite du read IFS a toujours sa valeur. _o_ (\t\n bien sûr ! pour ceux qui suivent)

    Comme on peut faire par exemple, dans un shell interactif : LANG=C man bash si on a habituellement sa locale en fr mais qu’on veut lire la sainte bible en anglais. Ça ne va pas changer la valeur de LANG pour les commandes suivante, juste pour la commande qui suit l’affectation (un truc que j’ai eu du mal à capter à mes débuts clairement)…

    • [^] # Re: Quoting

      Posté par  (site web personnel, Mastodon) . Évalué à 3 (+1/-0).

      Quand tu fais affectation pour une exécution, pas de souci : LANG=fr IFS=' ' read hoo baa

      Tu remarqueras que c’est (habituellement/normalement) toute une série d’instructions/lignes entre le changement de IFS et son rétablissement. L’affectation n’étant pas faite pour une seule exécution mais pour toutes les exécutions suivantes, il faut l’annuler quand on a fini nos affaires sinon bonjour la crise de nerfs : le shell utilise IFS quasiment partout (et il y a souvent des cas que l’on ne soupçonne pas.)

      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

      • [^] # Re: Quoting

        Posté par  . Évalué à 3 (+0/-0).

        toute une série d’instructions/lignes entre le changement de IFS et son rétablissement.

        Oui en effet. Je ne vois pas de cas d’usage mais ça ne veut clairement pas dire qu’il n’y en a pas.

        le shell utilise IFS quasiment partout

        Tu veux dire des commandes du shell comme ls, xargs ou autre qui pourraient avoir leur comportement modifié à cause de la modification de IFS ? Ça m’étonnerait (ou plutôt j’espère que non ^^). Je pense que tu parles du reste du code du script, voire de script externes appelés par celui-ci ?

        Et heureusement la modification ne se propage bien-sûr pas au « sur-shell » qui a lancé le script.

        $ printf "%q\n" "$IFS"; echo -e 'IFS=4\nprintf "IFS vaut : %q\\n" "$IFS"; exit 4\n' > script; bash -f script; printf "%q" "$IFS"
        
        $' \t\n'
        IFS vaut : 4
        $' \t\n'

        (je sais c’est moche, mais c’est représentatif)

        Et c’est un bon exemple de cas « àlakon » où un double échappement se justifie, encore que, c’est juste pour que le fichier "script" généré ait pas la gueule de travers :)

        • [^] # Re: Quoting

          Posté par  (site web personnel, Mastodon) . Évalué à 3 (+1/-0). Dernière modification le 22 février 2024 à 00:10.

          Tu veux dire des commandes du shell comme ls, xargs ou autre qui pourraient avoir leur comportement modifié à cause de la modification de IFS ? Ça m’étonnerait (ou plutôt j’espère que non ^^)

          Notre exemple initial montre que l’une des passes d’interprétation de la ligne de commandes n’est pas impactée… Le shell va utiliser les blancs horizontaux pour faire sa liste de tokens.

          Une autre des passes (je présume que ça se fait en plusieurs fois mais ce n’est pas obligatoire, juste qu’il y a les divers aspects à distinguer) va aussi faire une liste de tokens mais en faisant intervenir IFS : comparé au cas précédent, la liste de caractères de séparation n’est plus en dur. Je crois bien (à vérifier) que c’est toujours le cas pour les « expansions » (donc ça va impacter la compréhension de ton $@ par exemple, la liste passée au for…in, etc.)

          Exemples marrants (ou pas) qui, j’espère, montrent bien ces deux aspects

          # celui-ci est sioux
          echo $(IFS=:; echo $PATH)
          
          c=0
          for i in $(IFS=:; echo $PATH); do
            c=$((c+1))
            echo "$c: $i"
          done
          
          var="tic,tac,toe"
          # pour bash
          IFS="," read -r -a array <<< "$var"
          # pour ksh
          IFS="," read -r -A array <<< "$var"

          “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # Ça va il est même pas trois heure !

    Posté par  . Évalué à 6 (+3/-0).

    if [ "$(basename $0)" = "auxilium" ]; then
        echo "You can't use this script directly"
        echo "You can view an example by call /usr/bin/auxilium-test"
        exit
    fi

    Si j’ai pu critiquer les echo sur plusieurs lignes dans un autre post j’espère que tu ne te remettras pas en question sur ce point, car ça a le mérite d’être nettement plus lisible que la manière dont je m’y prends moi et finalement je ne vois pas ce qui me permet de critiquer cette façon de faire.

    J’allais dire autre chose mais je vais fermer ma gueule parce que j’avais juste oublié que ton script se devait d’être POSIX et je ne peux donc pas te donner de solution à l’éventuel et très hypothétique problème que je vois… Or « Pas de solution, pas de problème. »

    Par contre pour les deux fonctions suivantes :

    _to_lower(){
        echo $(echo $1 |tr -s '[:upper:]' '[:lower:]')
    }
    
    _to_upper(){
        echo $(echo $1 |tr -s '[:lower:]' '[:upper:]')
    }

    Tu ne pourrais pas faire simplement :

    _to_lower(){
        echo "$1" |tr -s '[:upper:]' '[:lower:]'
    }
    
    _to_upper(){
        echo "$1" |tr -s '[:lower:]' '[:upper:]'
    }

    ?

    L’absence de quote dans le cas de echo pose rarement des soucis mais s’il y a un * dans ton argument ça va quand même faire des trucs potentiellement ennuyeux et assurément indésirables.

    Mais surtout, je ne vois pas pourquoi tu fais un « echo d’un echo », ça ne fait pas la même chose effectivement, dans ton cas tu appelles un sous-shell, mais en l’occurrence je ne vois pas en quoi c’est utile. (Note que je n’ai bien sûr pas lu tout le script avant de commenter, c’est ma spécialité ! ;) Je pense toujours pouvoir le faire, mais là j’ai plus le temps ce « soir ».

    • [^] # Re: Ça va il est même pas trois heure !

      Posté par  . Évalué à 2 (+1/-0).

      Merci pour les remarques
      J'ai modifié pour la prochaine version. Je ne me rappelle plus pour quelle raison j'avais fait comme ceci.

    • [^] # Re: Ça va il est même pas trois heure !

      Posté par  . Évalué à 3 (+2/-0).

      pas regardé le script, mais à propos de casse, j'ajouterais :

      avec les commandes internes bash, pour modifier la casse du paramètre $1 :

      echo ${1,,}   # minuscules
      echo ${1^^}   # majuscules

      (et une seule ',' ou un seul '' pour ne traiter que le premier caractère de la chaîne)

      Et on peut aussi déclarer qu'une variable sera systématiquement en minuscules ou en majuscules :

      declare -l truc   # miuscules (l de lower)
      declare -u truc   # majuscules (u de upper)
      • [^] # Re: Ça va il est même pas trois heure !

        Posté par  . Évalué à 5 (+3/-0).

        Pour pallier a ça j'ai fais une petite lib à sourcer dans vos scipts shell (dash, bash, zsh, ksh testés) pour gérer ceci.

        😉

        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: Ça va il est même pas trois heure !

        Posté par  . Évalué à 4 (+3/-0).

        Les commandes suggérer sont spécifiques à bash, et ne sont, par exemple, pas disponibles dans dash, ce qui explique mon choix afin de rester compatible avec le maximum de shell.

        Pour la même raison, je passe par des fichiers temporaires plutôt que d'utiliser des tableaux.

      • [^] # Re: Ça va il est même pas trois heure !

        Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

        C’est assez connu (enfin je pense), mais juste pas POSIX (pas de souci si tu te limites à bash ou ksh ici, dans d’autres shells ça passera pas.)

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: Ça va il est même pas trois heure !

          Posté par  . Évalué à 3 (+0/-0). Dernière modification le 17 février 2024 à 21:26.

          Je ne connaissais pas ${X,,} ni ${X^^} (c’est dans la « cheat sheet » à laquelle je me réfère mais je n’ai pas pris le temps de tout lire (parce qu’il m’est illusoire de tout retenir de toute façon…)).

          J’ai vu que declare permettait effectivement de faire ça. Cependant je ne vois pas quels sont les cas d’usage. Vous auriez des exemples ?

          • [^] # Re: Ça va il est même pas trois heure !

            Posté par  . Évalué à 3 (+1/-0).

            C'est du bash 4 (attention il modifie certains comportement de -e) et en bash 5 tu peu écrire ${(U)var} (ce qui marche aussi en zsh et, je crois, en ksh).

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

            • [^] # Re: Ça va il est même pas trois heure !

              Posté par  . Évalué à 3 (+0/-0).

              Merci pour l’info, mais à quel moment c’est utile de « smallcaser » ou « uppercaser » une valeur ? C’était ça ma question.

              Dans le cas d’une saisie utilisateur ? Autre ? Note que je ne remets pas en doute l’utilité, juste que je l’ignore et ça m’agace ;)

              • [^] # Re: Ça va il est même pas trois heure !

                Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0). Dernière modification le 20 février 2024 à 12:49.

                La première fois que j'ai vu l'utilisation de uppercase dans un script, c'était pour pouvoir faire un test de comparaison de string facilement.

                Genre tester si valeur de l'argument passé par l'utilisateur est une variante de True/true/TRUE…

                • [^] # Re: Ça va il est même pas trois heure !

                  Posté par  . Évalué à 5 (+3/-0). Dernière modification le 20 février 2024 à 13:59.

                  Oui c'est généralement pour ça :

                  file='image.PNG'
                  
                  if [[ ${(L)file##*.} == 'png' ]]; then
                      #…
                  fi

                  Edit: pardon c'est du zsh, ça ne fonctionne pas tel quel en bash

                  https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # echo Plop

    Posté par  . Évalué à 4 (+1/-0).

        _AXM_ARGS_FILE=$(mktemp -t ${__AXM_FILE_PREFIX}-auxilium_args.XXXXXX)
        _AXM_ARGS_VALUE=$(mktemp -t ${__AXM_FILE_PREFIX}-auxilium_value.XXXXXX)
        _AXM_LIST_ARGS=$(mktemp -t ${__AXM_FILE_PREFIX}-auxilium_list_args.XXXXXX)
        _AXM_LIST_OPT=$(mktemp -t ${__AXM_FILE_PREFIX}-auxilium_list_opt.XXXXXX)

    Il servent à quoi les .XXXXXX finaux ?

    Si tu veux que le nom soit propre à l’exécution (au cas peu probable où on exécuterait ton script 2+ fois en parallèle (dans un framework plus large qui l’inclurait par exemple…)), tu devrais p-e utiliser $$ en lieu et place de .XXXXXX ?

    Après lecture rapide de ton script je dois dire que je n’ai pas le volonté de vraiment étudier le truc. Mais je le garde dans mes bookmark et le testerai p-e à l’occasion.

    Et j’apprends l’existence de /usr/bin/tabs, l’aide parle de COBOL, FORTRAN et S/370, entre autre, je t’avoue que je suis trop intimidé pour essayer de comprendre à quoi ça sert ! ^^

    • [^] # Re: echo Plop

      Posté par  . Évalué à 3 (+1/-0). Dernière modification le 17 février 2024 à 21:52.

      Si tu veux que le nom soit propre à l’exécution (au cas peu probable où on exécuterait ton script 2+ fois en parallèle (dans un framework plus large qui l’inclurait par exemple…)), tu devrais p-e utiliser $$ en lieu et place de .XXXXXX ?

      Ça indique à mktemp qu'il peut les remplacer par ce qu'il veut. Et mktemp t'assure de manière plus fiable que c'est lui qui a créer le fichier sans risque de concurrence surprenante.

      Le man dis

      Create a temporary file or directory, safely, and print its name. TEMPLATE must contain at least 3 consecutive 'X's in last component. If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

    • [^] # Re: echo Plop

      Posté par  . Évalué à 3 (+0/-0). Dernière modification le 17 février 2024 à 21:53.

      Ah si, une remarque quand même. C’est quelque chose que je faisais aussi, en préfixant pour éviter tout risque de collisions mais de ce que j’ai compris, par convention les identifiants (ie: non de variable) en majuscule sont à réserver aux variables d’environnement.

      Mais bon… je note que tu préfixes par _ par prudence, je crois pas que ce soit très grave comme façon de faire, perso je ne vais pas réécrire tous les scripts que j’ai fait avec des variables en majuscules pour les passer en minuscules ! :) Le plus souvent je me contentais de les préfixer toutes par une chaîne dériver du script. par exemple un script "UltimateTrucWrapper" je préfixais par UTW_. Avec ça je pense être assez tranquille. Mais maintenant j’ai changé j’en ai plus rien à branler j’utilise des minuscules avec un nom le plus descriptif possible, exemple : lobby_carpet_color_RVB :)

  • # catégorie du paquet Debian

    Posté par  . Évalué à 3 (+1/-0).

    Pourquoi avez-vous classé le paquet Debian dans réseau (catégorie net) ? De mon point de vue d'admin qui fouille dans les paquets, c'est un peu bizarre. Ce n'est pas une critique, mais une simple question.

    • [^] # Re: catégorie du paquet Debian

      Posté par  . Évalué à 2 (+1/-0).

      Simplement, parce qu'à la base, il est dans Openstack/…/third-party. Et que Openstack et dans 'net'.
      Mais je peux changer ça, à vous de me suggérer la meilleur catégorie à utiliser.

  • # Programmation défensive en bash

    Posté par  . Évalué à 1 (+0/-0). Dernière modification le 27 février 2024 à 23:47.

    Article du blogue de Seboss666 qui résume selon moi assez bien les diverses bonnes pratique de programmation en bash.

    https://blog.seboss666.info/2020/04/programmation-defensive-en-bash/

    J'essaie de m'en rapprocher le plus possible … ce qui m’empêche pas de faire du "quick and dirty … la majeur partie du temps !

    Notes en vrac:

    • il est vrais que j'oublie sans cesse comment positionner les : avec while getopts, j'essaierais la lib.

    • bien que je l'utilise en shell par défaut sur mon pc perso (bash pour le reste), je n'ai jamais créé de script en zsh; il n'est généralement pas présent par défaut. Il a de bon argument par rapport à bash ?

    • Ce qui est ci dessous est à mon sens assez lisible. Pas la peine d'utiliser tout le temps des fonctions.

    [[ ! -d "${mondossier}" ]] && mkdir "${mondossier}
    • Je n'ai pas encore réussi à comprendre le principe des tests unitaires donc pas ( encore ? ) de ca dans mes scripts.

    • le bash c'est bien mais quand ca devient vraiment trop long et compliqué -> python

    • [^] # Re: Programmation défensive en bash

      Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0).

      Pour ce test, on peut faire directement portable (i.e. sans bashisme)

      [ ! -d "${mondossier}" ] && mkdir "${mondossier}"

      …et dans le cas présent on peut inverser (le test et l’enchaînement)

      [ -d "${mondossier}" ] || mkdir "${mondossier}"

      On peut même faire simplement (mais avec réserve… vous avez trouvé ?)

      mkdir -p "${mondossier}"

      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

      • [^] # Re: Programmation défensive en bash

        Posté par  . Évalué à 1 (+0/-0).

        Par moment on ne se souvient plus de pourquoi on fait les chose comme cela …
        Donc d'après mes notes, j'utilise [[ ]] et non [] car la variable de [ -f $variablequinexistepas ] est considérée comme une chaîne de caractère vide, le test est donc vrais.

        $ [ -f $variablequinexistepas ] && echo ok || echo ko
        ok
        $ [[ -f $variablequinexistepas ]] && echo ok || echo ko
        ko

        En doublon avec le set -u

        [ -d "{mondossier}" ] || mkdir "{mondossier}"

        Effectivement ca fonctionne, mais je trouve plus logique : si quelque chose n'existe pas, je fais ..
        Au contraire de si quelque chose existe … je passe ? sinon je le créer.

        mkdir -p "${mondossier}"

        Si "${mondossier}" est vide ou n'existe pas ça va planter.

        • [^] # Re: Programmation défensive en bash

          Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0).

          Héhé :) Si tu regardes bien, ce n’est pas exactement le même test

          $ [ -f $variablequinexistepas ] && echo ok || echo ko
          ok
          $ [ -f "$variablequinexistepas" ] && echo ok || echo ko
          ko

          Par moment on ne se souvient plus de pourquoi on fait les chose comme cela …

          La forme [[…]] permet en effet de se prémunir des variables vides non “quoté”s …ou comportant des espaces, etc.

          Effectivement ca fonctionne, mais je trouve plus logique : si quelque chose n'existe pas, je fais ..
          Au contraire de si quelque chose existe … je passe ? sinon je le créer.

          Cette seconde forme se lit : vérifie si le répertoire existe, sinon crée le (et effectivement on ne fait rien s’il existe)
          La première forme est littéralement : vérifie le contraire de si le répertoire existe (oui donc au final si le répertoire n’existe pas), puis alors crée le (sinon je passe)

          “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: Programmation défensive en bash

          Posté par  (site web personnel, Mastodon) . Évalué à 3 (+1/-0).

          On peut même faire simplement (mais avec réserve… vous avez trouvé ?)

          Si "${mondossier}" est vide ou n'existe pas ça va planter.

          Exact. Hormis cela, la commande ne râle pas si le dossier existe déjà, raison pour laquelle on peut zapper le test d’existence… En vrai on peut tester si la variable n’est pas vide (-z) ou si le chemin n’est pas déjà utilisé (-e) pour ne pas tenter de remplacer un fichier par un répertoire (il y aura normalement une errreur mais bon.)

          En fait, pour rester dans l’esprit de l’exemple, j’aurais du simplement écrire : mkdir "${mondossier}"
          …parce-que l’option -p nécessite aussi une autre réserve : que le chemin ne soit pas sur un montage absent… La création va se faire mais on va s’arracher les cheveux par la suite…

          “It is seldom that liberty of any kind is lost all at once.” ― David Hume

          • [^] # Re: Programmation défensive en bash

            Posté par  . Évalué à 1 (+0/-0).

            En fait, pour rester dans l’esprit de l’exemple, j’aurais du simplement écrire : mkdir "${mondossier}"

            Hein ?

            $ ls /tmp/ | grep test
            $ mkdir /tmp/test
            $ ls /tmp/ | grep test
            test
            $ mkdir /tmp/test
            $ mkdir /tmp/test
            $ echo $?
            0
            $ mkdir  -p /tmp/test/test
            $ mkdir  -p /tmp/test/test
            $ mkdir  -p /tmp/test/test

            Ha bha pinaise, j'étais presque sûre que ca sortirait une erreur.

    • [^] # Re: Programmation défensive en bash

      Posté par  (site web personnel, Mastodon) . Évalué à 4 (+2/-0).

      il est vrai que j'oublie sans cesse comment positionner les : avec while getopts,

      C’est simple… il suffit de savoir (ou se souvenir) que c’est pour l’argument… « If a character is followed by a , the option shall be expected to have an argument, which should be supplied as a separate argument. »
      Donc, abc:d signifie que sont acceptés (dans n’importe quel ordre) : -a, -b, -d, -c $Bidule.
      Tandis que, ab:c:d signifie que sont acceptés (dans le désordre) : -a, -d, -b $Truc, -c $Chose.

      Il y a juste un cas particulier que l’on utilise très peu : en première position (donc concerne le script lui-même), ça va annuler/annihiler l’affichage normal des erreurs…

      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: Programmation défensive en bash

      Posté par  (site web personnel, Mastodon) . Évalué à 3 (+1/-0).

      Je n'ai pas encore réussi à comprendre le principe des tests unitaires donc pas ( encore ? ) de ça dans mes scripts.

      Quand sera venu le moment, tu pourras ajouter à ta trousse d’outils : bach ou bats ou ShellSpec ou shunit2 ou korn-spec/bash-spec ou zunit etc. (tiens, ça pourrait faire l’objet d’un ou deux journaux si j’ai un peu de bande passante.)

      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: Programmation défensive en bash

      Posté par  . Évalué à 2 (+0/-0). Dernière modification le 29 février 2024 à 12:05.

      Il a de bon argument par rapport à bash ?

      Le principal pour moi c'est quand tu commence à faire des manipulations de variables un peu sophistiquées.

      file='image.PNG'
      
      if [[ ${(L)file##*.} == 'png' ]]; then
          #…
      fi

      Ne fonctionne pas en bash parce que tu peux faire ${(L)file},
      tu peux faire ${file##*.}, mais tu ne peux pas combiner les 2 en une fois.

      Ensuite il y a évidement les modules de zsh qui sont un peu fou où tu a par exemple un client ftp disponible.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.