Revue des techniques de programmation en shell

114
22
sept.
2014
Ligne de commande

L'été est fini, c'est la rentrée ! Pour se remettre en forme, rien de tel qu'une revue des techniques de programmation en shell — ces techniques sont pour le shell de Bourne /bin/sh mais peuvent être utilisées avec les shells compatibles qui permettent parfois des solutions plus faciles.

Sommaire

Note: comme ce texte est déjà très long, je propose que ceux qui ont aussi des choses à dire sur ce sujet le fassent dans des dépêches supplémentaires.

Le dernier qui a fait ça chez nous est en train de sécher dans un faux plafond

Si vous souhaitez faire don de votre corps à un taxidermiste, utilisez l'une des techniques suivantes dans votre programme: les backquotes, la commande echo ou bien analysez la sortie de la commande ls.

Les backquotes, backticks ou contr'apostrophes

N'écrivez jamais

candidates=`awk '{print($2)}' /etc/fstab`

mais plutôt

candidates=$(awk '{print($2)}' /etc/fstab)

parce que c'est plus facile à lire, plus facile de présenter correctement une longue fonction, plus facile à emboiter, bref les backquotes sont une technologie obsolète.

À propos, il est souvent plus facile et plus lisible de définir une fonction auxiliaire:

list_mount_points()
{
  awk '
    /^#/||/^$/{next}
    {print($2)}
  ' /etc/fstab
}

candidates=$(list_mount_points)

Quand on a plus de place, c'est plus facile de mieux présenter son code, d'écrire un code plus robuste et le nom de la fonction décrit son rôle.

La commande echo

La fonction echo n'est utile que pour afficher des messages de diagnostic, si la fonction printf est disponible, il faut toujours préférer cette dernière, qui est plus puissante et plus facile à utiliser!

On peut par exemple imprimer des codes de caractère arbitraires:

TAB=$(printf '\t')
CR=$(printf '\015')

et imprimer correctement toutes les variables

attrape_nigaud='Pan!\cPan!'
printf '>>> %s <<<\n' "$attrape_nigaud"
echo "$attrape_nigaud"

L'appel à printf est bien défini mais pas celui à echo qui produit Pan! sous Debian/Jessie mais Pan!\cPan! sous FreeBSD.

Rappelons la définition bien pratique de la fonction eprintf permettant d'écrire sur la sortie d'erreur standard :

eprintf()
{
   >&2 printf "$@"
}

Analyse de la sortie de la commande ls

En bref la règle d'or est que la commande ls sert uniquement pour l'utilisation interactive du shell, dans le cas d'un script shell, on utilise soit le globbing soit la commande find en conjonction avec xargs.

La raison est que la commande ls est inutilisable dès que les fichiers contiennent des caractères exotiques. Il est inutilement difficile d'analyser correctement la sortie de ls et il est tellement facile d'utiliser un globbing:

for file in $(ls); do ← Exemple à ne pas suivre!
  printf '%s\n' "$file"
done

est corrigé en

for file in *; do
  printf '%s\n' "$file"
done

ou bien, pour des recherches plus compliquées:

NUL=$(printf '8f872742767daab9354ff276b9d5504afb94060400')
find /path/to/dir -type d -name '* *' -print0 \
  | while IFS="$NUL" read file; do
  printf '%s\n' "$file"
done

L'information fournie par ls -l est facilement accessible avec stat.

Construction de la ligne de commande

Un problème analogue à celui de l'analyse la sortie de la commande ls est celui de la préparation de l'appel à une commande du type

utility -file file1 -file file2

La version vite-fait mal fait consiste à dire:

argv=''
for file in *; do
   argv="$argv -file $file" ← Exemple à ne pas suivre
done
utility $argv

Évidemment, comme vos noms de fichiers utilisent un codage binaire où le ' ' représente 0 et le retour à la ligne représente 1, tout va de travers! La façon indestructible d'appeler utility est d'utiliser xargs -0 comme dans:

job_strategy()
{
   local file
   for file in *; do
     printf '-file\000%s\000' "$file"
   done
}

job_strategy | xargs -0 utility

Programmation structurée

Les débutants commettent souvent l'erreur consistant à vouloir répliquer les structures d'enregistrement (les struct de C ou C++, les record de Pascal) dans des variables du shell.

Mais ce n'est pas comme ça que ça marche!

Un bonne règle pour l'organisation des données dans un programme shell est de de n'utiliser les variables que pour les objets du monde UNIX: chemins de dossiers, chemins de fichiers, pids, uids, etc. Tout le reste, en particulier les structures d'enregistrement, va dans des fichiers ou des pipes, sous forme de table séparée par des | ­ou par des caractères ASCII US=$(printf '\037) pour ceux qui vivent au pays des gens utilisant | dans leurs données — et les traitements complexes sont effectués par des filtres. Si ça ne convient pas à votre programme, changez de langage, utilisez OCaml, Perl ou Python. Bien-sûr, tout le monde n'est pas de cet avis, en particulier, les maîtres de la programmation shell. Les maîtres ont une caractéristique importante qui démultiplie leur faculté de jugement bien au delà de ce que peut imaginer un débutant : ce sont des maîtres. Si vous êtes un débutant, ne vous laissez pas impressionner par les maîtres qui ont déjà écrit un interpréteur Scheme en sed et utilisent la commande printf pour insérer du code shell auto-modifiant dans le noyau Linux. N'hésitez pas à utiliser un autre langage pour résoudre votre problème.

Exemple du renommage de fichier

Illustrons cette technique sur l'exemple classique du renommage de fichier en masse. La procédure finale se décompose en

job_select | job_strategy | job_perform

On a trois fonctions correspondant aux étapes suivantes:

  1. job_select prépare une liste des fichiers à renommer ;
  2. job_strategy prépare une donnée tabulaire avec deux colonnes, l'une contenant le nom initial, l'autre le nouveau nom ;
  3. job_perform qui fait la modification finale.

On fait l'hypothèse paranoïaque que les caractères /n et | n'apparaîssent pas dans les noms de fichiers. Si ce n'est pas le cas, utilisez un vrai langage de programmation — mais les maîtres, eux, savent comment faire!

La fonction job_select ressemble typiquement à

job_select()
{
   find /path/to/directory -name '*@!*' -print0
}

La commande job_strategy prépare un plan de travail:

job_strategy()
{
   sed -e'
     h
     s/@!/--censored--/g
     H
     x
     s/\n/|/'
}

Ceux qui n'ont pas encore écrit d'interpréteur Scheme en sed peuvent lire le walkthtough suivant, en anglais parceque je ne sais pas dire walkthrough ou pattern space en français et que personne n'en comprendrait les traductions:

h        Save pattern space to hold space
s/…/…/   Edit the name in the pattern space
H        Append the edited name to the hold space
x        Exchange hold and pattern space
s/\n/|/  Replace the newline by a field separator

Bien-sûr on peut utiliser n'importe quel autre langage que sed pourvu qu'on soit capable d'écrire un filtre transformant

Quel @! de patron

en

Quel @! de patron|Quel --censored-- de patron

Finalement la procédure de renommage ressemble à

job_rename()
{
  local oldname newname
  while IFS='|' read oldname newname; do
    if [ "$oldname" = "$newname" ]; then
      printf 'Skipping %s\n' "$oldname" >&2
    else
      mv "$oldname" "$newname"
    fi
  done
}

Oui je sais, on peut faire la même chose avec rename '/@!/--censored--/' * ou avec la commande @! de zsh, mais il s'agit d'un exemple un peu scolaire pour illustrer la méthode.

Un effet secondaire très appréciable de cette organisation est qu'on peut tester chaque morceau individuellement et faire du mocking facilement.

Ainsi, si au lieu d'exécuter job_select | job_strategy | job_perform à la fin de mon script j'utilise simplement job_select je peux m'assurer que la liste de fichiers est correcte, et avec job_select | job_strategy je peux vérifier que mon plan de travail est correct. Aussi, je peux créer des données artificielles pour tester job_strategy, par exemple

mock_issue_42_job_select()
{
   cat <<'EOF'
This is an example of pathological input causing issue 42
EOF
}

mock_issue_42_job_select | job_strategy

Exemple de l'analyse de contenu de fichier vidéo

Un exemple plus amusant est celui de cette fonction, issue d'un programme de vidéothèque que j'ai écrit en shell.

# cinema_identify FILE
#  Identify the given file.
#
#   Identifying the file causes a content index to be dumped on
#   stdout.  This content index describes the content of the file.
#   There is two types of records, for Tracks and for Attachments.
#
#     Track|INDEX|TYPE|CODEC
#     Attachment|INDEX|TYPE|SIZE|FILENAME
#
#   An output example is:
#
#     Track|0|video|V_MPEG4/ISO/AVC
#     Track|1|audio|A_VORBIS
#     Track|2|audio|A_VORBIS
#     Track|3|audio|A_VORBIS
#     Track|4|subtitles|S_VOBSUB
#     Track|5|subtitles|S_VOBSUB
#     Attachment|1|image/jpeg|93405|+POSTER
#     Attachment|2|text/plain|54|+INDEX

cinema_identify()
{
    env LANG=C mkvmerge --identify "$1" \
    | sed -e '
s/'//g
/^File/{
  d
}
/^Track/{
  s/ ID /|/
  s/: /|/
  s/ (\(.*\))/|/
}
/^Attachment/{
  s/ ID /|/
  s/: type /|/
  s/. size /|/
  s/ bytes, file name /|/
}'
}

Cette commande prend un fichier vidéo en argument et écrit sur la sortie un truc du genre

Track|0|video|V_MPEG4/ISO/AVC
Track|1|audio|A_VORBIS
Track|2|audio|A_VORBIS
Track|3|audio|A_VORBIS
Track|4|subtitles|S_VOBSUB
Track|5|subtitles|S_VOBSUB
Attachment|1|image/jpeg|93405|+POSTER
Attachment|2|text/plain|54|+INDEX

Bien-sûr dans le vrai script, le script sed est dans un fichier auxiliaire pour, pour le test et la lisibilité.

Voici comment traiter de façon polymorphe cette sortie:

cinema_polymorph_example()
{
    local field_type rest
    while IFS='|' read field_type rest; do
    IFS='|' set -- $rest
    case "$field_type" in
        Track)      process_track "$@";;
        Attachment) process_attachment "$@";;
        *)          >&2 printf 'Unknown field type %s\n' "$field_type";
                        exit;;
    esac
    done
}

Dans cet exemple chaque ligne commençant par Track est traitée par process_track et chaque ligne commençant par Attachment est traitée par process_attachment.

Liste des commandes travaillant avec des données tabulaires

Voici une liste pêle-mêle de fonctions travaillant sur des données tabulaires.

awk(1)                   - pattern-directed scanning and processing language
sed(1)                   - stream editor
paste(1)                 - merge corresponding or subsequent lines of files
join(1)                  - relational database operator
comm(1)                  - select or reject lines common to two files
cut(1)                   - cut out selected portions of each line of a file
lam(1)                   - laminate files
sort(1)                  - sort or merge records (lines) of text and binary files
uniq(1)                  - report or filter out repeated lines in a file

Il faut bien noter que toutes ces fonctions n'utilisent pas forcément les mêmes options pour définir le délimiteur.

Introspection

Oui, vous lisez bien, le shell permet de faire l'introspection, de façon limitée. Par exemple, voyons comment définir une mini base de données de repositories git à sauvegarder:

project1_repo='/var/git/project1.git'
project1_refs='master v1.0 v2.0'
project2_repo='/var/git/project2.git'
project2_refs='master v1.0 v2.0'

gitdb_list()
{
   set | sed -n -e 's/_repo$//p'
}

gitdb_tabular()
{
   local repo dir refs
   gitdb_list | while read repo; do
     eval printf '%s|%s|%s\n' "$repo" "\$${repo}_dir" "\$${repo}_refs"
   done
}

gitdb_backup()
{
   local repo dir refs
   gitdb_list | while read repo dir refs; do
     git bundle create "/backup/$repo" "$repo_dir" HEAD $refs
   done
}

Dans ce cas particulier, je préfèrerais utiliser directement une base de données tabulaires, mais si pour une raison ou une autre on ne devait utiliser que des variables shell pour transmettre l'information alors cette technique est utilisable.

  • # Muchas gracias!

    Posté par . Évalué à 10.

    Merci pour ce guide. Je me rends compte que j'ai du bol de n'être pas encore en train de sécher dans un faux-plafond. Mais, tout compte fait, il y a deux raisons à ça: 1) je suis seul à scripter sous Linux dans la boîte et 2) y a pas de faux plafond.

    Ceci dit, la théorie et les règles, c'est bien. Mais écrire des scripts SHELL qui tournent aussi bien sous *NIX que Mac OS, par exemple, ça rend les règles plus difficiles à suivre. Surtout en raison des différences entre les outils, genre sed et tar, pour ne citer qu'eux.

    Cette dépèche est à accrocher en post-it sur le bureau.

    (Hein? Comment ça, trop de post-it?)

    • [^] # Re: Muchas gracias!

      Posté par . Évalué à 4.

      Ceci dit, la théorie et les règles, c'est bien. Mais écrire des scripts SHELL qui tournent aussi bien sous *NIX que Mac OS, par exemple, ça rend les règles plus difficiles à suivre. Surtout en raison des différences entre les outils, genre sed et tar, pour ne citer qu'eux.

      Pas tant que ça, les extensions GNU sont surtout utiles pour des usages interactifs ou sont assez peu connu.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Muchas gracias!

        Posté par . Évalué à 3.

        […] les extensions GNU sont surtout utiles pour des usages interactifs ou sont assez peu connu.

        J'en ai aucune idée en fait, je ne suis pas encore assez avancé, j'imagine. Les expériences que j'ai eues concernent notamment les options. Par exemple, '-r' rend les expressions régulières bien plus lisibles avec sed sous Linux. Mais avec OSX, c'est '-E'. Je ne me souviens plus des détails avec tar mais je sais que j'ai séché pas mal de temps avant de trouver.

    • [^] # Re: Muchas gracias!

      Posté par (page perso) . Évalué à 3.

      Ceci dit, la théorie et les règles, c'est bien. Mais écrire des scripts SHELL qui tournent aussi bien sous *NIX que Mac OS, par exemple, ça rend les règles plus difficiles à suivre. Surtout en raison des différences entre les outils, genre sed et tar, pour ne citer qu'eux.

      Moi qui viens de passer l'après midi à devoir «convertir» un scrip SHELL Linux en VBScript car seules les machines de l'entreprises (donc sous Windows) sont autorisée sur le réseau … je me dit que j'aimerais bien n'avoir a faire qu'as MacOS X

      • [^] # Re: Muchas gracias!

        Posté par . Évalué à 7.

        PowerShell est probablement bien plus intéressant à utiliser.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Muchas gracias!

        Posté par (page perso) . Évalué à 2.

        GNUWin32 ?

        (enfin, si tu as l'autorisation d'installer des logiciels)

        Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

        • [^] # Re: Muchas gracias!

          Posté par (page perso) . Évalué à 1.

          En entreprise c'est un droit qui est assez rare, et que je n'ai pas. C'est aussi (mis à part le fait que ce soit un système libre) l'un des gros avantage que j'avais à utiliser ma machine : je peu y faire ce que je veux.

          Pour le choix entre PowerSchell et VBScript, j'ai juste pris celuis qui me semblais dans l'instant le plus correspondre à mes besoins.

          • [^] # Re: Muchas gracias!

            Posté par (page perso) . Évalué à 5.

            Si c'est pas trop tard (et bien que off-topic), reconsidère PowerShell, il a vraiment beaucoup plus d'atouts, pour peu que tu sois sur des windows plus récents que XP…

            • [^] # Re: Muchas gracias!

              Posté par (page perso) . Évalué à 1.

              Rien n'est jamais trop tard, quelqu'un aurais un bout de guide pour le grand débutant libriste linuxien a propos de cette technologie ?
              J'ai l'impression de m'y retrouver comme pour la première fois face à un terminal sur une mandake 8.

              Après le but est de faire du libre (QGis - PostgreSQL - SQLite) mais sous Windows, donc ca touche quand même le libre.

  • # Construction de la ligne de commande

    Posté par . Évalué à 10.

    Pour la construction de la ligne de commande, il faut aussi parler de l'utilisation de -- pour déterminer la fin des options, on ne devrait jamais voir :

    cp "$fichier1" "$fichier2"

    mais plutôt

    cp -- "$fichier1" "$fichier2"

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Construction de la ligne de commande

      Posté par (page perso) . Évalué à 10.

      C'est quand même mieux de dire pourquoi :

      Le double-tiret permet d'indiquer la fin des options, et le début des paramètres. Cela permet d'éviter d'interpréter des paramètres en tant qu'options.

      Exemple :

      $ echo -tea | grep -i '-tea'
      grep: invalid option -- 't'
      Usage: grep [OPTION]... PATTERN [FILE]...
      Try `grep --help` for more information.
      
      $ echo -tea | grep -i -- '-tea'
      -tea
  • # Liste des commandes travaillant avec des données tabulaires

    Posté par . Évalué à 10.

    Personnellement je ne les connais pas toutes mais il est intéressant souvent de travailler avec un langage qui ferra tout le boulot. Notamment awk est vraiment puissant et il est généralement inutile d'utiliser grep ou sed devant ou derrière un awk ce dernier faisant très bien le boulot.

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Liste des commandes travaillant avec des données tabulaires

      Posté par (page perso) . Évalué à 7.

      La puissance d'expressivité de awk est surprenante, même avec un niveau intermédiaire on arrive à écrire rapidement des programmes très intéressants et étonnamment courts.

      C'est indéniablement un des programmes outil qui, à mon sens, doivent faire partie de la trousse à outil de toute personne utilisant un système UNIX — au même titre que le shell et make.

      • [^] # Re: Liste des commandes travaillant avec des données tabulaires

        Posté par . Évalué à 6.

        Il m'arrive régulièrement de faire des scripts awk uniquement (avec le shbang #!/usr/bin/awk -f). J'avais notamment réécris en moins de 10 lignes un programme java d'un collègue qui analysait des logs ssh…

        Moi je trouve assez amusant la manière de programmer en awk qui fait penser à de la programmation évènementielle ou un parseur SAX.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Liste des commandes travaillant avec des données tabulaires

        Posté par (page perso) . Évalué à 3.

        La puissance d'expressivité de awk est surprenante, même avec un niveau intermédiaire on arrive à écrire rapidement des programmes très intéressants et étonnamment courts.

        Oui, awk est très intéressant mais à mon avis il vaut mieux le réserver à des programmes courts.
        Au delà d'une ou deux dizaines de lignes, les erreurs dans le programme deviennent difficiles à gérer.
        Alors, je préfère écrire un filtre en C. C'est plus facile à lire et à maintenir.

        • [^] # Re: Liste des commandes travaillant avec des données tabulaires

          Posté par . Évalué à 5.

          Alors, je préfère écrire un filtre en C. C'est plus facile à lire et à maintenir.

          Ouai, bof bof. Le C pour gérer des chaines de caractère c'est vraiment pas génial, là où tout langage de script te fais le café (perl en tête).

          Personnellement je trouve que sortir du C pour ça est la majorité du temps une très grosse perte de temps.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Liste des commandes travaillant avec des données tabulaires

            Posté par . Évalué à 4.

            Bon, je préfère Perl pour ajouter des filtres perso (quand un truc genre grep/sed/awk devient compliqué), mais il ne faut pas oublier qu'écrire un programme en C ne signifie pas forcément « écrire un programme en C en ne se servant que de string.h comme base » : tu peux parfaitement utiliser des bibliothèques (par exemple, la bibliothèque de regexps du projet GNU), et simplifier grandement ton code plutôt qu'utiliser strtok. De même, tu peux parfaitement avoir commencé à construire un ensemble de wrappers (genre smalloc qui va crasher si y'a un problème d'allocation, et qui te permet de ne pas te soucier de la gestion d'erreur, sread pour gérer les erreurs et le besoin de répéter l'opération en cas d'interruption, etc.) qui vont simplifier grandement ton code et le rendre suffisamment lisible pour te concentrer sur l'essentiel : l'algorithme.

            J'insiste, je pense qu'un langage de plus haut niveau (Perl/Python/Ruby/Lua/Bla) serait sans doute meilleur pour gérer ce genre de choses, mais je pense aussi que le meilleur outil pour faire un programme de taille raisonnable est celui qu'on maîtrise bien.

    • [^] # Re: Liste des commandes travaillant avec des données tabulaires

      Posté par . Évalué à 3.

      à condition de faire alias awk=perl avant :)

  • # echo

    Posté par (page perso) . Évalué à 10.

    La commande echo

    La fonction echo n'est utile que pour afficher des messages de diagnostic, si la fonction printf est disponible, il faut toujours préférer cette dernière, qui est plus puissante et plus facile à utiliser!

    Et qui est surtout portable, contrairement à echo, dont les mises en œuvre diffèrent de façon incompatible, entre celles intégrées aux shells et celles indépendantes ! Regardez notamment son option -e…

    • [^] # Re: echo

      Posté par . Évalué à 9.

      Il faut aussi rappeler que s'il s'agit de logging la commande logger est faite pour ça.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # Un petit mot sur les performances

    Posté par . Évalué à 10.

    Le shell (quelque soit le shell) n'est pas performant, c'est mou que ça en peu plus (même dash) par conception. Pourtant on peut faire des programmes très performants en shell !

    1. Les parties critiques peuvent être écris dans des langages différents (que ce soit du C ou un autre langage de script)
    2. on peut très très très facilement faire du parallélisme poussé.

    Il y a 2 grandes familles de parallélisme en shell qui sont très simples à faire.

    Les pipelines, tout le monde les connais on les as tous déjà vu. Il y a des exemples plus hauts de programmes l'utilisant. On découpe un traitement en plusieurs étapes et chaque processus s'occupe d'une partie de ce traitement. On peut voir ça comme une programmation fonctionnelle. Chaque bout de programme est un filtre et a une entrée bien définie et une sortie bien définie (par exemple son entrée c'est des noms de fichiers et ça sortie c'est une sommes de contrôle un espace puis le nom du fichier).

    Le parallélisme à la xargs (je connais pas le nom du paradigme), là il s'agit d'exécuter le traitement pleins de fois en parallèle. Il faut savoir qu'il n'y a pas que xargspour faire ça. find le fait aussi très bien avec le terminateur + à la place de ; en fin de commande. Il y a aussi gnu parallel, mais il est rarement installé.

    L'énorme avantage de ces modes c'est qu'on ne lance pas des milliers de processus (notamment avec les pipes) ce qui est contre productif.

    Si j'ai des conseils à donner sur les performances en shell, je conseil de regarder en priorité 3 choses :

    • le nombre de fork, lancer pleins de fois le même programme c'est très lourd, il faut généralement voir pour lui donner toutes les données d'un coup ou voir pour le lancer en arrière plan et pouvoir lui fournir les données au fur et à mesure
    • regarder les accès disque : utiliser des fichiers pour stocker des données interne au programme limite les performance (mais c'est des fois nécessaires vu que les pipes sont limités à 4Kio de données) et ajoute des la complexité (il ne faut pas de collision, il faut bien supprimer les fichiers à la fin du programme)
    • quand on utilise les pipe éviter comme la peste les traitement au milieu des pipelines qui nécessitent l'ensemble des données. Par exemple un sort va totalement détruire les performance des pipes parce qu'il va attendre d'avoir toutes les données avant de pouvoir commencer sont travail

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Un petit mot sur les performances

      Posté par (page perso) . Évalué à 3.

      Excellent complément(s)!

      Le shell (quelque soit le shell) n'est pas performant, c'est mou que ça en peu plus (même dash) par conception.

      Je confirme totalement, c'est d'ailleurs une raison supplémentaire pour déléguer tous les traitements complexes à des filtres.

      Je m'étais amusé à faire de la POO en shell, c'est à dire à implémenter un mécanisme d'héritage. En gros:

      • Chaque classe est matérialisée par un dossier
      • Chaque méthode est matérialisée par un programme dans ce dossier, typiquement un script ou un vrai binaire natif
      • L'héritage et les fonctions virtuelles sont matérialisées par des symlinks.

      En appelant copieusement la fonction eval et en regardant sur le dossier plusieurs fois avant chaque appel de méthode, on arrive à faire ramer copieusement n'importe quelle machine!

      Est-ce que par hasard tu connais des outils qui vont un pas plus loin et font du parallélisme distribué? Par exemple qui récupère une liste de comptes SSH et lance des programmes sur l'hôte distant?

      • [^] # Re: Un petit mot sur les performances

        Posté par . Évalué à 3.

        Pas surprenant, je l'ai pas mis plus haut mais celui qui met un eval dans un script shell, doit mettre 15 lignes pour expliquer pourquoi il est obligé de faire ça.

        Est-ce que par hasard tu connais des outils qui vont un pas plus loin et font du parallélisme distribué? Par exemple qui récupère une liste de comptes SSH et lance des programmes sur l'hôte distant?

        Comme ça, non. Tu peux jouer un peu avec fabric, mais on quitte le monde du shell. Perso je pencherais plus pour faire quelque chose à coup de netcat/socat, mais ça doit demander pas mal de boilerplate. Pour arriver à quelque chose.

        Bien sûr tu peut aussi simplement faire des ssh alias cmd1 qui marche bien et qui te permet de récupérer le résultat.


        1. j'utilise des alias défini dans mon ~/.ssh/config généralement, ça me permet d'avoir du coté script shell de la logique uniquement de la logique et du coté fichier de configuration ssh tout ce qui est spécifique à l'environnement 

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Un petit mot sur les performances

        Posté par (page perso) . Évalué à 3.

        Est-ce que par hasard tu connais des outils qui vont un pas plus loin et font du parallélisme distribué? Par exemple qui récupère une liste de comptes SSH et lance des programmes sur l'hôte distant?

        Peut être GNU parallel

      • [^] # Re: Un petit mot sur les performances

        Posté par (page perso) . Évalué à 2.

        Est-ce que par hasard tu connais des outils qui vont un pas plus loin et font du parallélisme distribué? Par exemple qui récupère une liste de comptes SSH et lance des programmes sur l'hôte distant?

        http://taktuk.gforge.inria.fr/

    • [^] # Re: Un petit mot sur les performances

      Posté par . Évalué à 4.

      Autre chose coté performance, il vaut mieux ne pas trop croire et plutôt tester. J'ai eu de jolies déconvenues avec des script zsh dans les quels j'utilisais zargs (un outils pour utiliser un équivalent à xargs avec les globbings étendus) qui est assez peu efficace contrairement à ce que j'espérais.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Un petit mot sur les performances

      Posté par . Évalué à 3.

      Le shell (quelque soit le shell) n'est pas performant, c'est mou que ça en peu plus (même dash) par conception. Pourtant on peut faire des programmes très performants en shell !

      Cela dépend aussi fortement de la méthode. Il y a un "framework" Shell créé, éprouvé et utilisé à des fins de traitement de données massives qui s'appelle Unicage.
      Ils utilisent les concepts UNIX et ses outils à leur paroxysme. Il y a certes 2 ou 3 binaires custom et optimisés mais on peut se contenter d'utiliser les alternatives originales (join, date,…) si on ne recherche pas la vitesse ultime.

      Ça reste un peu austère, informatique japonaise oblige, mais il y a du très bon.

    • [^] # Re: Un petit mot sur les performances

      Posté par (page perso) . Évalué à 3.

      find le fait aussi très bien avec le terminateur + à la place de ; en fin de commande.

      Euh, pas le mien en tous cas. find ... -exec commande {} + permet simplement d'exécuter une seule commande cmd fichier1 fichier2 ... (éventuellement découpée en plusieurs commandes s'il y a plus de fichiers que la limite de nombre d'arguments autorisée) au lieu de faire cmd fichier1; cmd fichier2; .... C'est plus rapide parce que ça réduit considérablement le nombre de fork+exec, mais pas pour le parallélisme.

      J'ai peut-être raté un truc, mais en tous cas la page de man de ma version de find ne contient pas le mot « parallel ».

  • # Presque d'accord ...

    Posté par . Évalué à 6.

    Bonjour à tous,

    Cela fait plaisir de voir d'excellents conseils de programmation sur un truc aussi ancien que le shell :)

    Et je suis globalement d'accord, sauf sur la punition, il serait plus judicieux de lui supprimer son PC sous linux et de lui passer une tablette surface sous Windows 8 :) (2 jours devraient suffir)

    Sinon juste un petit truc : avant de vous lancer comme des fous dans l'awk, le join paste et consort … il est bien souvent beaucoup plus judicieux d'utiliser des langages plus adaptés comme le python, le ruby le perl (par exemple …)
    Même si cela ressemble a un fusil pour tuer les mouches, dans la durée les langages de scripts + une petite base comme sqlite sont plus facile à utiliser, a debugger et à modifier que du shell.
    Voire même du fichier ascii a plat comme intermédiaire.

    Par contre, je ne suis pas pour reléguer le shell juste aux bidouilles, aux trucs vite fait. tout dépend du projet. Parfois le mélange des deux donne d'excellents résultats.

    Mais je n'ai pas de recette toute faites pour dire ça c'est mieux en shell, tel autre en python.

    Sinon je ne connaissais pas lam (laminate), comme quoi même après 25 ans de shell on en apprend toujours :)

    Merci

    • [^] # Re: Presque d'accord ...

      Posté par . Évalué à 6.

      Mais je n'ai pas de recette toute faites pour dire ça c'est mieux en shell, tel autre en python.

      Il y a des cas simples et clairement en faveur de l'un des deux. Si tu lance des programmes externes le shell est ton amis, si tu manipule beaucoup le système de fichier le shell est ton amis, si tu fais pas mal de calcul le shell est ton pire ennemis,…

      Pour le reste ça dépend, des connaissances de celui qui développe, de la plateforme cible, etc

      Personnellement, il faut arriver à un gros niveau de complexité pour que je quitte le shell. Mais je n'hésite pas à prendre perl comme un awk amélioré (sauf si je fais trop d'aller-retour entre le shell et perl).

      Souvent le shell est mal maitrisé et t'a l'air d'un gourou dès que tu sort quelque chose d'un peu propre (et je ne parle pas d'awk…).

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Presque d'accord ...

      Posté par (page perso) . Évalué à 4.

      Sinon juste un petit truc : avant de vous lancer comme des fous dans l'awk, le join paste et consort … il est bien souvent beaucoup plus judicieux d'utiliser des langages plus adaptés comme le python, le ruby le perl (par exemple …)
      Même si cela ressemble a un fusil pour tuer les mouches, dans la durée les langages de scripts + une petite base comme sqlite sont plus facile à utiliser, a debugger et à modifier que du shell.
      Voire même du fichier ascii a plat comme intermédiaire.

      Je trouve que la question suffisamment subtile pour ne pas recevoir une réponse tout à fait tranchée. Bien-sûr utiliser un vrai langage et une vraie base de données a de gros avantages, mais a aussi quelques inconvénients.

      L'inconvénient le plus important de cette approche est que cela demande un effort de déboguer les I/O dans la base de données alors que dans la version “zen du shell” il suffit de débrancher un tube pour voir ce qui en sort. De plus l'opérateur de composition de tubes (le |) encourage une structuration très claire du programme et n'a pas toujours d'équivalent dans les autres langages de programmation. (À part bien sûr le |> de OCaml!)

      Ceci dit bien connaître OCaml un autre langage comme Python, Perl ou Ruby est bien-sûr un avantage énorme pour développer rapidement des traitements pas encore existants en shell.

      Néanmoins, bien connaître awk est tout de même une compétence dont il serait dommage de se passer quand on utilise un système UNIX.

      • [^] # Re: Presque d'accord ...

        Posté par . Évalué à 2.

        on peut utiliser perl avec des tubes | par exemple :

        printf "Emacs c'est le meilleur éditeur de texte\n" | perl -pe 's/Emacs/Vim/'

        • [^] # Re: Presque d'accord ...

          Posté par (page perso) . Évalué à 8.

          Je parlais plutôt de l'intérieur du programme… Est-ce qu'il y a un pipe dans perl qui permettrait de combiner deux fonctions?

          printf "Emacs c'est le meilleur éditeur de texte\n" | perl -pe 's/Emacs/Vim/'

          C'est inutilement compliqué, on peut simplifier en

          printf "Emacs c'est le meilleur éditeur de texte\n"

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à -1.

            Est-ce qu'il y a un pipe dans perl qui permettrait de combiner deux fonctions?

            question intéressante, par contre c'est vrai que le tube c'est pour associer des programmes généralement disparates (ou chaîner plusieurs traitements du même programme). Dans les autres langages, il y a des possibilités un peu similaire (même si moins graphiques) pour combiner plusieurs fonctions, par exemple récupérer un paramètre d'une fonction et la renvoyer à une autre.

            C'est inutilement compliqué, on peut simplifier en

            quitte à simplifier, autant utiliser directement vim pour éditer le texte d'origine… ;)

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 4.

            Pour communiquer à l'intérieur de ton programme rien ne vaut … les variables :)

            le pipe est fait pour la communication entre programme ou processus

          • [^] # Re: Presque d'accord ...

            Posté par (page perso) . Évalué à 4.

            En Perl, je ne sais pas, mais en Python il y a plusieurs projets qui reprennent la syntaxe du Pipe avec | pour enchaîner des traitements sur des flux de données:

            Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

      • [^] # Re: Presque d'accord ...

        Posté par . Évalué à 1.

        structuration très claire du programme et n'a pas toujours d'équivalent dans les autres langages de programmation.

        Ne faire qu'un chose et le faire bien :) …

        (À part bien sûr le |> de OCaml!)
        A cela éveille ma curiosité, si j'ai 2 minutes je vais voir de quoi il en retourne?

        Néanmoins, bien connaître awk est tout de même une compétence dont il serait dommage de se passer quand on utilise un système UNIX.

        Oui tout a fait d'accord, il faut connaître awk AVANT de faire du perl et du python.

        Mais bon de plus en plus de personnes ne savent même pas que l'on peut scripter des commandes.
        Et c'est bien dommage.

        • [^] # Re: Presque d'accord ...

          Posté par (page perso) . Évalué à 3.

          (À part bien sûr le |> de OCaml!)
          A cela éveille ma curiosité, si j'ai 2 minutes je vais voir de quoi il en retourne?

          Imaginons que tu ais trois fonctions :

          1. print_line: string -> unit
          2. read_file: file -> string
          3. open_file: string -> file

          Ces trois fonctions peuvent s'assembler pour afficher le contenu fichier.

          Une première manière est de faire :

          print_line( read_file ( open_file ("mon_fichier.txt") ) )

          Or, |> est une fonction définie comme ça :

          let (|>) x f = f x

          C'est à dire qu'elle prend un type x, une fonction f, et en inverse l'application. Par exemple cela permet d'écrire :

          "mon_fichier.txt" |> open_file

          Bien sûr ça n'a pas beaucoup de sens dans l'exemple ci dessus, mais ça permet de réécrire le premier bloc de code de la manière suivante :

             open_file "mon_fichier.txt"
          |> read_file
          |> print_line

          Et voilà :)

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 3.

            Je trouve pas le gain de ce genre de choses démesuré. L'intérêt du pipe sur les appels de méthode chainé c'est le parallélisme. Pour le reste c'est plus du sucre syntaxique.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 1.

            with open("fichier.txt") as f:
              data = f.read()
              print data
            

            C'est tout aussi clair et sans symbole ésotérique difficile a obtenir sur de l'azerty :)

            • [^] # Re: Presque d'accord ...

              Posté par (page perso) . Évalué à 2.

              C'est tout aussi clair et sans symbole ésotérique difficile a obtenir sur de l'azerty :)

              Je préfère la version qui ne parle pas de f ou de data qui ne véhiculent aucune information sur le programme.

              Bien-sûr un exemple de trois lignes n'est pas forcément ce qu'il y a de plus probant, mais en pratique cet opérateur est extraordinairement pratique.

              sans symbole ésotérique difficile a obtenir sur de l'azerty :)

              Peut-être, mais les maîtres utilisent un clavier QWERTY! ;)

              Blague à part, pour la programmation, c'est bien mieux qu'un AZERTY ou un QWERTZ, je n'utilise plus que ça depuis 8 ans, pas question de changer!

              • [^] # Re: Presque d'accord ...

                Posté par . Évalué à 1.

                Tout a fait d'accord pour le QWERTY :)
                pas pour le reste, j'aime bien les variables ;-)

                par contre Ocaml m'a l'air très intéressant, a voir c'est une horreur
                beaucoup d'habitudes doivent changer (# ~ et :: par exemple), et un clavier qwerty doit être nécessaire :)
                Par contre pour le reste, cela a l'air plus que bien … Merci d'avoir suscité ma curiosité
                j'ai peut être trouvé un nouveau jouet.

                • [^] # Re: Presque d'accord ...

                  Posté par (page perso) . Évalué à 1.

                  Par contre pour le reste, cela a l'air plus que bien … Merci d'avoir suscité ma curiosité
                  j'ai peut être trouvé un nouveau jouet.

                  OCaml est un langage extraordinaire! Les caractéristiques clefs:

                  • Le typage fort, qui me permet de placer assez d'invariants de mon code dans le système de types pour trouver la plupart de bogues pendant la compilation. En pratique il ne reste plus que les erreurs intelligentes!

                  • L'aspect fonctionnel. Plus jamais une boule for tu n'écriras! (Mais, bon, si tu aimes bien les variables…☺)

                  • La simplicité du modèle de compilation. Un bon programmeur C peut estimer facilement la complexité à l'éxécution du code OCaml.

                  • La relative facilité de lecture! Oui. À côté de Haskell par exemple, OCaml est très facile à lire!

                  • [^] # Re: Presque d'accord ...

                    Posté par (page perso) . Évalué à 4.

                    L'aspect fonctionnel. Plus jamais une boule for tu n'écriras! (Mais, bon, si tu aimes bien les variables…☺)

                    Moi j'écris des boucles for en OCaml de temps en temps… :) C'est d'ailleurs ce que j'aime dans ce langage, c'est que malgré le côté très rigoureux du système de types et l'aspect fonctionnel, tu peux quand même, quand c'est opportun, utiliser un langage assez impératif, avec des variables (les références), des tableaux mutables etc. Toutes ces choses sont assez alambiquées à faire en Haskell en comparaison, et c'est en fait pour ces choses là que Haskell devient moins facile à lire parfois.

                    • [^] # Re: Presque d'accord ...

                      Posté par (page perso) . Évalué à 2.

                      Et c'est opportun pour faire de l'IO, surprise surprise. Pour une question sur SO j'ai fait une petite comparaison en OCaml entre un style d'output impératif traditionnel et le style en monades: conclusion le style impératif est plus rapide.

                      Si je trouve Haskell si difficile à lire c'est qu'on peut définir des macros qui ont apparemment plein de pouvoir magiques, notamment à réécrire les do… en choses moins inoffensives qu'elles n'en ont l'air.

            • [^] # Re: Presque d'accord ...

              Posté par (page perso) . Évalué à 2.

              Tu ne compares pas vraiment la même chose. Ici, tu crées un environnement dans lequel le fichier reste ouvert tant que la variable f est accessible. J'avais pris l'exemple du fichier pour montrer le chaînage des fonctions parce que c'est plus parlant que f, g et h mais tu as également des structures pour construire des environnements comme celui que tu viens d'écrire en python.

          • [^] # Re: Presque d'accord ...

            Posté par (page perso) . Évalué à 3.

            Depuis quand utilise-t-on tant de parenthèses en Caml ?

            Quant à cette fonction, elle est visiblement infixe, ce qui vaut la peine d'être mentionné tout de même !

            • [^] # Re: Presque d'accord ...

              Posté par (page perso) . Évalué à 4.

              Sans parenthèses:

              print_line read_file open_file "mon_fichier.txt"

              serait un appel a la fonction print_line avec trois arguments. Dans l'exemple les seules parenthèses inutiles sont celles autour de "mon_fichier.txt".

              Et, en OCaml, hormis quelques built-in, les seules fonctions infixes sont celles formées uniquement de symboles, et elles sont déclarées avec des parenthèses autour let (<|) = .... Il n'y a pas de flexibilité à ce niveau.

              • [^] # Re: Presque d'accord ...

                Posté par (page perso) . Évalué à 4.

                Sans parenthèses [ce] serait un appel a la fonction print_line avec trois arguments. Dans l'exemple les seules parenthèses inutiles sont celles autour de "mon_fichier.txt".

                Oui, j'avais compris ça.

                Et, en OCaml, hormis quelques built-in, les seules fonctions infixes sont celles formées uniquement de symboles, et elles sont déclarées avec des parenthèses autour let (<|) = …. Il n'y a pas de flexibilité à ce niveau.

                Et il n'y en a pas forcément besoin d'ailleurs. Mais merci pour l'info, j'ignorais cela, et je dois dire que ça m'avais manqué, la possibilité de définir des opérateurs infixes.

                Mes expériences avec Caml datent un peu, mais j'aime toujours bien ce langage !

    • [^] # Re: Presque d'accord ...

      Posté par . Évalué à 2.

      perl

      awk et sed c'est pas mal, mais pour certaines regex, je trouve effectivement plus pratique d'utiliser perl (sans doute aussi parce que je ne suis pas un maître).

      À noter l'outil txt2regex permet d'aider à construire des regex pour perl, php, python, sed… (20 programmes en tout, y compris OpenOffice, pour les règles de parefeu sans doute ;) )

      • [^] # Re: Presque d'accord ...

        Posté par (page perso) . Évalué à 3.

        awk et sed c'est pas mal, mais pour certaines regex, je trouve effectivement plus pratique d'utiliser perl (sans doute aussi parce que je ne suis pas un maître).

        Disons que tant que c'est des regex simples, awk et sed font bien le travail, mais dès que tu as besoin d'utiliser des classes unicode, d'écrire sur plusieurs lignes tes regexs pour plus de lisibilité, de contrôler plus finement la façon de matcher (pour de l'analyse lexicale par exemple avec le modifieur \G en perl), … ce n'est juste plus possible avec awk et sed. Et avec awk, même les backreferences, qui sont bien pratiques, ne sont pas portables et n'existent que dans gawk (que je sache).

        • [^] # Re: Presque d'accord ...

          Posté par (page perso) . Évalué à 3.

          Je suis tout à fait d'accord avec toi: dès qu'on sort de l'extraction pure de données et qu'on a besoin “ne serait-ce” que d'UNICODE¹ awk montre ses limites. Quand à GNU Awk… pourquoi ne pas utiliser directement Perl? :-) Ce qui est intéressant dans le Awk “de base” c'est qu'il offre une balance très claire entre performance et petite taille d'un côté et expressivité de l'autre. Vu de loin, GAwk est un peu feature bloated — mais a l'immense intérêt d'être un Awk portable — et j'aurais tendance à passer directement à Perl. (Cette position pas hyper subtile n'engage que moi!)

          ¹ J'ai guillemetté à cause de la taille moyenne d'une bibliothèque supportant UNICODE de façon poussée ou quasi complète.

          • [^] # Re: Presque d'accord ...

            Posté par (page perso) . Évalué à 7. Dernière modification le 23/09/14 à 09:15.

            Ce qui est intéressant dans le Awk “de base” c'est qu'il offre une balance très claire entre performance et petite taille d'un côté et expressivité de l'autre.

            Un équilibre, en français. Ou mieux, vu la phrase, un compromis. Une balance, ce n'est qu'un instrument qui sert à mesurer la masse.

            • [^] # Re: Presque d'accord ...

              Posté par (page perso) . Évalué à 6. Dernière modification le 23/09/14 à 09:37.

              Un équilibre, en français. Ou mieux, vu la phrase, un compromis. Une balance, ce n'est qu'un instrument qui sert à mesurer la masse.

              Merci, le pire c'est que je ne me rends plus compte que j'écris des choses comme ça! Je ne vis plus en France depuis presque six ans et avec le tant qui passe j'ai de plus en plus de sympathie pour Jean-Claude van Damme! ☺

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 0.

            J'ai guillemetté à cause de la taille moyenne d'une bibliothèque supportant UNICODE de façon poussée ou quasi complète.

            Unicode sera quasi-toujours implémenté en UTF-8 au niveau du shell (pour la compatibilité avec l'ASCII) et pour le reste, il y a toujours la commande standard iconv. Quant à Gawk, s'il est vrai qu'il grossit de manière inquiétante, il demeure au moins aussi performant que la version de Kernighan, y compris en UTF-8 (non supporté par cette dernière — mais la plupart du temps, à part pour la longueur des chaînes dans le formatage des sorties, on s'en moque, car il n'y a aucune raison d'employer des caractères non-ASCII comme clés de formatage interne, bien au contraire).

            • [^] # Re: Presque d'accord ...

              Posté par . Évalué à 3.

              La gestion de l'unicode dans les expressions régulières ne se limite pas aux caractères litéraux, mais impacte les métacaractères type \w.

              • [^] # Re: Presque d'accord ...

                Posté par . Évalué à -1.

                C'est vrai, mais dans le cadre du shell, ça reste du contenu, pas de la syntaxe. Ce que je veux dire, c'est que tu prennes du HTML, du Mardown, du LaTeX, du Groff, de l'INI ou n'importe quel fichier de configuration, tu n'as besoin que de l'ascii pour en faire l'analyse syntaxique. Après, que tu puisses avoir besoin d'unicode pour l'analyse du contenu introduit par l'utilisateur, on est d'accord, mais on est alors au-delà de la stricte analyse syntaxique (en HTML, ça reviendrait à analyser du #PCDATA, ce pourquoi effectivement le shell/awk n'est probablement pas l'outil le plus adapté).

              • [^] # Re: Presque d'accord ...

                Posté par . Évalué à 1.

                (Au passage, ce que tu pointes précisément a en fait plus à voir avec la capacité à supporter la locale qu'avec unicode, une locale avec un encodage 8bits posant les mêmes difficultés quant à l'usage de classes de caractères « nommées ».)

            • [^] # Re: Presque d'accord ...

              Posté par (page perso) . Évalué à 3.

              Unicode sera quasi-toujours implémenté en UTF-8 au niveau du shell (pour la compatibilité avec l'ASCII)

              Ce qui sert justement à pouvoir en partie traiter un document Unicode avec des programmes ne supportant pas Unicode.

              pour le reste, il y a toujours la commande standard iconv

              La commande iconv sert à faire de la transcription tandis qu'une bibliothèque Unicode permet de:

              1. Analyser les catégories de caractères
              2. Analyser d'autres types d'attributs
              3. Travailler avec des expressions rationnelles ou écrire des lexers Unicode
              4. Compter correctement le nombre de caractères dans un texte
              5. Faire du transcodage

              Tu vois bien que iconv fait quand même beaucoup moins qu'une vraie bibliothèque Unicode.

              Dans mon commentaire, je dis que awk montre ses limites quand on a besoin de Unicode et tu réponds que ce n'est pas le cas parcequ'on a en pratique pas très souvent d'Unicode: c'est une réponse à côté de la plaque!

              • [^] # Re: Presque d'accord ...

                Posté par . Évalué à -2.

                Dans mon commentaire, je dis que awk montre ses limites quand on a besoin de Unicode et tu réponds que ce n'est pas le cas parcequ'on a en pratique pas très souvent d'Unicode: c'est une réponse à côté de la plaque!

                Non, dans ma réponse, je dis plusieurs choses :

                1. tu parles d'Unicode, mais dans le contexte du travail en shell, il vaudrait mieux parler d'UTF-8, qui sera soit l'encodage Unicode des documents sources, soit pourra être obtenu en passant ceux-ci à iconv (ex. une page HTML UTF-16 peut sans problème se traduire en UTF-8) ;
                2. les cas où on a un réel besoin de l'UTF-8 pour travailler sont rares ;
                3. au pire, si vraiment on en a besoin, on a GAWK. qui gère (très bien) l'UTF-8 (comme lo Heirloom d'ailleurs, mais je ne sais pas ce qu'elle vaut).

                Plus généralement, le problème que tu pointes n'a encore ici que peu à voir avec Unicode, mais plus avec le support de l'encodage en général. On pourrait plutôt dire que dès que tu ne peux plus supposer que l'utilisateur traite des documents en accord avec son LC_CTYPE, le shell ne convient pas (même en Perl, ce n'est pas une partie de plaisir : extraire l'encodage du source, langinfo &cie pour obtenir le charset local, configuration des différents descripteurs… )

                • [^] # Re: Presque d'accord ...

                  Posté par . Évalué à 3.

                  Plus généralement, le problème que tu pointes n'a encore ici que peu à voir avec Unicode, mais plus avec le support de l'encodage en général

                  Ben non, raté. Je cite le message auquel tu réponds :

                  « La commande iconv sert à faire de la transcription tandis qu'une bibliothèque Unicode permet de: [etc.] »

                  • [^] # Re: Presque d'accord ...

                    Posté par . Évalué à 2. Dernière modification le 26/09/14 à 10:43.

                    C'est vrai que dans le premier message auquel je réponds, Mickaël parle de problèmes au-delà de « l'extraction de pures données », ce qui anticipe l'essentiel de mes objections. Du coup on est bien d'accord qu'il faut plus qu'iconv et un AWK UTF-8 dans ces cas-là. Manque d'attention de ma part : Sorry for the noise, comme qu'on dit…

    • [^] # Re: Presque d'accord ...

      Posté par . Évalué à -8.

      Et je suis globalement d'accord, sauf sur la punition, il serait plus judicieux de lui supprimer son PC sous linux et de lui passer une tablette surface sous Windows 8 :) (2 jours devraient suffir)

      Bonne idee, comme ca il pourra decouvrir PowerShell et se demander pourquoi ces hommes des cavernes s'acharnent a utiliser un outil prehistorique qui n'a aucune structure et qui est si facilement sujet a erreur.

      • [^] # Re: Presque d'accord ...

        Posté par (page perso) . Évalué à 7.

        Bonne idee, comme ca il pourra decouvrir PowerShell et se demander pourquoi ces hommes des cavernes s'acharnent a utiliser un outil prehistorique qui n'a aucune structure et qui est si facilement sujet a erreur.

        Le shell est lent, vieux et pas simple d'accès. Il a cependant une qualité importante qui met à peu près tout le reste KO: en vertu de son grand âge:

        • les méthodologies pour bien l'utiliser sont connues et abondamment documentées
        • ses défauts et limitations sont connus et abondamment documentés
        • il existe des mégaoctets de scripts shell disponibles sous forme de logiciel libres qui peuvent servir d'inspiration aux débutants.

        Pour ce qui est de la comparaison avec le PowerShell je me contenterai d'observer qu'il ne fonctionne pas sur les systèmes d'exploitation qui m'intéressent (FreeBSD, Mac OS X et Debian GNU/Linux). Mais pour ceux qui utilisent des systèmes Microsoft c'est sûrement un outil très utile si cela ressemble au shell. Ceci dit, on peut répondre aux gros trolls velus sans cracher sur ses propres parents!

        • [^] # Re: Presque d'accord ...

          Posté par . Évalué à -2.

          Il y a un port pour Mono en Alpha, toujours developpe : https://github.com/Pash-Project/Pash/

        • [^] # Re: Presque d'accord ...

          Posté par (page perso) . Évalué à 3.

          Mais pour ceux qui utilisent des systèmes Microsoft c'est sûrement un outil très utile si cela ressemble au shell.

          ça ressemble plutôt à un mix entre les .BAT et le VBS, avec une logique objet et quelques commandes correctement documentées

          Ceci dit, on peut répondre aux gros trolls velus sans cracher sur ses propres parents!

          malencontreusement, windows a peu d'héritage d'Unix :/ Hormis si tu installes un cygwin (mais dans ce cas, autant utiliser une distribution GNU/Linux…).

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 2.

            Il m'arrive parfois de devoir utiliser Windows pour utiliser certains logiciels ou jeux. Crois moi, Cygwin change la vie.

      • [^] # Re: Presque d'accord ...

        Posté par (page perso) . Évalué à 3.

        Je n'ai jamais compris pourquoi Microsoft a redéveloppé un nouveau langage de shell plutôt que s'orienter vers un Python ou Ruby, qui s'adaptent très bien à ce genre d'usage, et bénéficier ainsi de la portabilité et de tout l'écosystème qu'ils apportent.

        (enfin, "jamais"… j'ai bien une idée du pourquoi)

        Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

        • [^] # Re: Presque d'accord ...

          Posté par . Évalué à 4.

          L'intérêt de PowerShell est vraiment dans le runtime .Net, c'est ce qui permet de construire des bouts avec les différents langages de la plateforme .Net tout en gardant les concepts objets.

          Mais j'ai l'impression que PowerShell a du mal à décoller. Les admin/dev windows ne sont pas très enclins à s'y mettre et je ne sais pas si les gros logiciels MS ont une API PowerShell intéressantes (est-il envisageable de manipuler word via PowerShell par exemple ?).

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Presque d'accord ...

            Posté par (page perso) . Évalué à 2.

            J'ai lu qq part qu'il y avait des outils wrappers COM vers CLR, et le package pywin32 donne directement accès à COM, donc au CLR pour peu que les wrappers aient été créés. Et il semble y avoir d'autres façons. Je reste dubitatif sur l'intérêt d'avoir créé un nouveau langage.

            Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 1.

            Ca dépend pour quoi on veut utiliser PowerShell. Perso, je l'utilise surtout pour gérer un peu et sortir directement des feuilles Excel de mon vCenter et de mes ESXi.

            Y pas mal d'exemples là : http://www.hypervisor.fr/?page_id=3637

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 2.

            Tu peux controler n'importe quel objet COM a travers PowerShell, et a peu pres tous les softs d'entreprise MS ont des cmdlet PowerShell specifiques, ainsi que de plus en plus de softs d'entreprise d'autres boites.

            Maintenant, changer les habitudes des sysadmins, on sait tous que c'est pas simple…

          • [^] # Re: Presque d'accord ...

            Posté par (page perso) . Évalué à -1.

            L'intérêt de PowerShell est vraiment dans le runtime .Net (…)

            …M.. Mais, t'es sérieux en fait !? Moi qui plussait bêtement tous tes comms "Powershell c'est mieux" en rigolant…

        • [^] # Re: Presque d'accord ...

          Posté par . Évalué à 0.

          Il n'y a pas que les scripts dans la vie. Pour l'utilisation en ligne de commande intéractive, Powershell est plus adapté que Python ou Ruby (malgré ses commandes à rallonge).

          • [^] # Re: Presque d'accord ...

            Posté par (page perso) . Évalué à 3.

            As-tu déjà essayé ipython ?

            Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

      • [^] # Re: Presque d'accord ...

        Posté par . Évalué à 4.

        Rien que le nom POWERShell me fait marrer …
        cela fait penser aux personnes qui ont une GROSSE voiture style 4x4 pour compenser … quelque chose :)

        (désolé j'ai pas pu résister, mais je sais j'aurais du attendre dredi)

    • [^] # Re: Presque d'accord ...

      Posté par . Évalué à 4.

      avant de vous lancer comme des fous dans l'awk, le join paste et consort … il est bien souvent beaucoup plus judicieux d'utiliser des langages plus adaptés comme le python, le ruby le perl

      Justement non.
      Les outils comme awk, join, sort et autres offrent un niveau d'abstraction supérieur et une plus grande spécialisation que python/perl/riby.
      Il faut donc d'abord regarder si ils peuvent résoudre ton problème avant de passer à un outil plus généraliste.
      Dans ta logique il faudrait tout faire en C/asm.

      • [^] # Re: Presque d'accord ...

        Posté par . Évalué à 3.

        L'asm ya que ca de vrai :)

        D'accord pour le "spécialisation", c'est vrai, mais bien souvent tu pars d'un bout de code pour résoudre ton problème, ensuite tu le mets dans un cron (par exemple) et donc tu as besoin de log, puis d'historiser les logs, ensuite on te demandes un petite modif, puis une autre etc …
        Et je ne parle pas d'ergonomie pour l'utilisateur final :)
        Bref le coté spécialisation c'est bien, mais au bout de quelques temps tu va vers le coté généraliste.

        Quand tu as un bon marteau, tout les problèmes ressemblent à des clous :)

        Mais tu as tout a fait raison que si awk join paste (et lam que je connaissais pas :) ) sont toujours la c'est pas pour rien. et pour longtemps :)
        (pas comme le power shell ;-) … (ah zut on est que mardi) )

        • [^] # Re: Presque d'accord ...

          Posté par . Évalué à 2.

          donc tu as besoin de log, puis d'historiser les logs

          Une commande logger et tu gère tes logs via syslog (ou le deamon de Lennart) comme un pro (avec rotation etc).

          ensuite on te demandes un petite modif, puis une autre etc

          C'est toujours subtile de savoir quel est le meilleur outil pour une tâche donnée, mais il faut pas croire que le shell est fait pour des scripts jetables. Le fait d'être à un très haut niveau d'abstraction permet de faire énormément de choses de manière très simple/efficace.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Presque d'accord ...

        Posté par . Évalué à 2.

        Ce qui manque à Perl selon moi, c'est la possibilité d'utiliser les pipes à l'intérieur d'un programme Perl (un peu comme |> qui existent OCaml comme l'a expliqué Michaël, mais aussi Scala et le cousin d'OCaml, F#). Mais à part ça, je ne vois pas en quoi le shell est tellement plus abstrait que Perl/Python/Blah, surtout si le langage a une REPL.

        En Perl, par exemple, je peux faire un truc du genre alias perlstd='perl -ne' pour avoir un interpréteur Perl en ligne de commande (mais il sera très fragile, assurément). Ça me permettra d'écrire ce genre de choses:

        perlstd 'print' /usr/include/stdio.h # cat en plus lent, WOUHOU!
        perlstd 'print reverse <>' < /usr/include/stdio.h # rev en plus lent, WOUHOU!

        Donc évidemment, s'il existe des commandes pour faire la même chose, qui en plus ne nécessiteront pas de créer une instance d'interpréteur perl, mieux vaut les utiliser (et en plus les noms seront plus cours, même si je pourrais faire les alias qui vont bien pour pallier ce genre de problème).

        Par contre, avec ce même alias perlstd, je peux aussi faire tout ce que proposent sed, awk, grep, ce qui par définition indique que Perl est plus abstrait que ces outils. Et plus général aussi, bien sûr. Bref, ceci est de la sodomie de diptères pour dire que je ne suis pas d'accord pour dire que le shell est plus abstrait. Au contraire, le shell est moins abstrait, et c'est ce qui fait sa force.

        • [^] # Re: Presque d'accord ...

          Posté par (page perso) . Évalué à 3. Dernière modification le 24/09/14 à 21:47.

          Ce qui manque à Perl selon moi, c'est la possibilité d'utiliser les pipes à l'intérieur d'un programme Perl (un peu comme |> qui existent OCaml comme l'a expliqué Michaël, mais aussi Scala et le cousin d'OCaml, F#).

          En Perl tu ne peux pas faire une pipe qui permette de paralléliser des choses (en OCaml non plus d'ailleurs, pour le moment, je crois), mais tu peux écrire facilement une fonction pipes qui prend en argument une valeur, puis une liste de fonctions, et renvoie la valeur composée de ces fonctions.

          sub pipes {
              my ($val, @subs) = @_;
              foreach my $sub (@subs) {
                   $val = $sub->($val);
              }
          }
          
          my $val = pipes("valeur", \&fun1, \&fun2, ...);

          Et le |> d'OCaml c'est juste (en plus performant) let (|>) x f = f x, c'est juste du sucre syntaxique pour la composition en utilisant le parenthésage de priorité par défaut pour les opérateurs. Bref, n'importe quel langage qui permet de passer des fonctions en argument à d'autres fonctions permet de faire la même chose.

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 2.

            L'avantage du |> c'est que c'est lisible et donne naturellement l'ordre des opérations. Quelque part, le fait qu'il y ait une écriture de type fonction op fonction op fonction ... op ... me permet de visuelle situer ce qui se passe, alors qu'avec ta solution (qui fonctionne, pas de souci là-dessus), il faut que mon cerveau réfléchisse un chouïa plus.

            Concernant le parallélisme : en OCaml je ne sais pas si les pipes le permettent, mais j'avais cru comprendre qu'en F# c'était faisable.

        • [^] # Re: Presque d'accord ...

          Posté par . Évalué à 3.

          Utiliser perl -ne ou -la n'a pas grand chose avoir avec l'utilisation de perl en remplacement de script shell. Ils permet de créer facilement des filtres qui utilisent des comportements proches de grep, sed ou awk. Ils sont vraiment très bien et je m'en sert régulièrement (l'exemple ne marche pas je n'avais pas pu tester).

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Presque d'accord ...

            Posté par . Évalué à 1.

            Disons que c'est peut-être ma méconnaissance de sh/bash (zsh je ne connais vraiment pas), mais un script qui fait plus de 20 lignes en (ba)sh, ça me pique les yeux, même si c'est moi qui l'écris. Dès que j'ai besoin d'écrire des fonctions, j'ai tendance à très vite aller voir du côté de Perl. Ça a aussi sans doute à voir avec le fait que si je ne peux pas simplement chaîner des commandes et que j'ai besoin d'ajouter de la logique, s'il ne s'agit pas de boucles très simples où tout est visible depuis celle-ci, je trouve le shell trop peu expressif (bon c'est pas exactement comme ça que je le pense1, mais j'ai le cerveau embrumé ce matin…).

            En fait je suppose que dès qu'on parle de manipulation « fine » de chaînes de caractères, j'ai tout de suite tendance à penser à un langage plus expressif (et avec plus de modules déjà écrits pour moi) que le shell.


            1. J'ai écrit tout plein de scripts bash qui appelaient d'autres scripts (Perl ou bash) pour traiter des logs, générer de jolis graphiques, et m'envoyer un mail quand le traitement était fini. 

            • [^] # Re: Presque d'accord ...

              Posté par . Évalué à 4.

              L'intérêt du shell c'est :

              • gestion du parallélisme trivial
              • manipulation de programme et de fichier trivial (find, globings)
              • la manipulation d'autres programmes

              Le parallélisme en perl c'est mort bien souvent et utiliser POE c'est euh… comme chercher à détruire l'univers pour tuer une mouche. La gestion des fichier, je crois qu'il y a un module pour avoir comme les globings, mais c'est peu utilisé je sais pas si c'est dans les distrib' de base. La manipulation de programme est pas trop mal avec la possibilité d'ouvrir de lancer des programme comme on ouvre des fichiers.

              Comprends bien j'aime perl (vraiment), mais ce que je fais avec perl et ce que je fais en shell forment 2 ensembles totalement disjoints.

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: Presque d'accord ...

                Posté par . Évalué à 3.

                Je ne comprends pas bien pour le globbing. J'utilise fréquemment ce genre de code (simplifié avec say pour l'exemple) :

                use v5.014;
                say for ( <*.txt>,<subdir/*.txt> ); 
                
                # Ou en plus explicite : 
                
                use strict;
                use warnings;
                use v5.014;
                for my $file (<*.txt>,<subdir/*.txt>) {
                    say $file
                }

                Certes, il faut rajouter les < et >, mais ça s'arrête globalement là. N'importe quel bouquin qui enseigne le Perl correctement (je pense entre autres aux bouquins de chez O'Reilly) expliquent comment utiliser le globbing.

              • [^] # Re: Presque d'accord ...

                Posté par . Évalué à 2.

                L'intérêt du shell c'est :

                J'ajouterais un quatrième point : des implémentations simples et sécurisées.

  • # $(...) et /bin/sh

    Posté par . Évalué à 10. Dernière modification le 22/09/14 à 12:47.

    L'entreprise pour laquelle je travaille traîne de vieux Solaris 8 (SunOS 5.8).
    Sur ces systèmes le /bin/sh n'accepte pas les $(…) l'utilisation des backquotes est obligatoire.

    Par contre, je n'ai pas fouillé les faux plafonds.

    Cette syntaxe $(…) ne fait pas pour moi partie de la syntaxe sh "originelle".

    • [^] # Re: $(...) et /bin/sh

      Posté par (page perso) . Évalué à 10.

      Cette syntaxe $(…) ne fait pas pour moi partie de la syntaxe sh "originelle".

      C'est exact mais la syntaxe $(…) fait partie de POSIX depuis un bon moment maintenant — bien-sûr quand on fait de l'archéologie, on est dans un cas particulier!

    • [^] # Re: $(...) et /bin/sh

      Posté par . Évalué à 1. Dernière modification le 22/09/14 à 13:36.

      Poste supprimé (j'ai oublié de rafraîchir avant de poster, ce que je voulais écrire a déjà été écrit)

  • # Paramètres d'xargs

    Posté par (page perso) . Évalué à 5.

    Prendre quelques minutes pour consulter la page de manuel d'xargs est probablement un bon investissement.

    Une option ultra classique (avec -0/--null, déjà mentionnée) est -r/--no-run-if-empty, qui permet typiquement d'éviter de lancer une commande sans paramètre, ce qui peut éviter un code de retour en erreur et un script/crontab/etc. qui termine un peu trop vite parce que « set -e » avait été déclaré.

    Debian Consultant @ DEBAMAX

    • [^] # Re: Paramètres d'xargs

      Posté par . Évalué à 7.

      Prendre quelques minutes pour consulter la page de manuel d'xargs est probablement un bon investissement.

      Sauf que je la trouve imbitable, comme celle de sed. Les manpage de tels programmes auraient gagné à etre plus concis et un peu plus étoffés en exemples.
      Les manpage GNU, c'est d'une complexité effarante.

      • [^] # Re: Paramètres d'xargs

        Posté par . Évalué à 2.

        La manpage de xargs est trop complexe? A la rigueur, je peux comprendre pour celle de bash mais celle de xargs me semble claire et concise. Une page de man n'est pas un tutoriel. Elle donne une courte description de la commande et de ses arguments avec quelques exemples.

  • # Portabilité

    Posté par . Évalué à 8.

    La gestion de la portabilité est un sujet assez épineux pour du shell. On parle souvent de se contenter de POSIX, il arrive que ce soit assez contraignant pour un intérêt limité.

    Lorsque le code est distribué à l'ensemble de la planète, c'est normal de chercher la plus grande portabilité possible (et donc de choisir POSIX), mais dans bien d'autres cas ce n'est pas aussi clair :

    • la portabilité d'un script est égale à la portabilité du programme le moins portable utilisé par ce script. Par exemple si on crée un script qui va utiliser apt-xapian (et dont c'est la raison d'être), il est inutile de chercher à ce que le script fonctionne sur Solaris, MacOS et git bash pour windows
    • des fois (souvent ?) on a une plateforme cible, si on la connaît je ne vois personnellement pas de problème à la cibler plus particulièrement

    Une autre approche de la portabilité peut être d'utiliser le moins de programmes possible. On peut par exemple créer un script zsh qui n'utilisera jamais le moindre outil GNU. Ça peut simplifier les dépendances (on ne dépend plus que de zsh) tout en ayant des fonctionnalités plus pratiques performantes.

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # ls et globbing

    Posté par . Évalué à 5.

    En bref la règle d'or est que la commande ls sert uniquement pour l'utilisation interactive du shell, dans le cas d'un script shell, on utilise soit le globbing soit la commande find en conjonction avec xargs.

    for file in *; do
       printf '%s\n' "$file"
    done

    C'est moyen, comme "règle d'or".
    Cette boucle dans un répertoire vide va te renvoyer '*' comme nom de fichier. De même si le masque du globbing n'a aucune correspondance, c'est le masque lui même qui est renvoyé. Ce qui oblige à tester la nature du retour. La forme $(ls * 2>/dev/null) par contre est moche, mais pas piégeuse.

    • [^] # Re: ls et globbing

      Posté par . Évalué à 4.

      Le problème du masque étendu en lui même quand rien ne 'match' peut se résoudre facilement avec l'option

      shopt -s nullglob

      Cela marche dans Bash. Pour les autres shells c'est à vérifier. Il y a aussi l'option 'dotglob' qui permet de ne pas ignorer les fichiers cachés (donc commençant par un '.')

      Il m'est aussi arrivé de tomber sur des scripts utilisant 'ls' pour obtenir des informations sur les fichiers. C'est assez risqué car il existe de nombreuses variantes de 'ls' et le résultat peut aussi dépendre de la locale.

      Une meilleure solution est d'utiliser 'stat'

      for file in *; do
      stat -c "SIZE='%s' NAME='%n'" $file
      done

      • [^] # Re: ls et globbing

        Posté par . Évalué à 3.

        il existe de nombreuses variantes de 'ls' et le résultat peut aussi dépendre de la locale.

        Je suis d'ailleurs surpris de ne rien lire au sujet de la locale dans ce journal des bonnes pratiques. C'est en effet généralement une bonne idée de toujours commencer par positionner LC_ALL (qui. rappelons-le, ne devrait jamais être définie dans l'environnement courant) à "C", afin que les commandes aient des sorties formatées conformément à POSIX (en plus de gagner en performances pour les commandes où l'ajout d'utf-8 s'est fait avec un certains coût), quitte ensuite à l'enlever à certains points précis (avant mailx, par exemple, qui dans sa version Heirloom échouera à envoyer le message si son encodage n'est pas en accord avec la locale).

        Dans le même genre, on peut aussi mettre systématiquement le masque à 022, afin d'éviter les mauvaises surprises si le script doit créer des dossiers/fichiers.

    • [^] # Re: ls et globbing

      Posté par (page perso) . Évalué à 4.

      C'est moyen, comme "règle d'or".

      La règles d'or c'est que:

      1. Il ne faut pas parser la sortie de ls.
      2. Il ne faut pas parser la sortie de ls.
      3. Il ne faut pas parser la sortie de ls.
      4. Au lieu de ls on peut utiliser le globbing ou find — pas sans discernement, sans que tu le soulignes.

      La forme $(ls * 2>/dev/null) par contre est moche, mais pas piégeuse.

      Elles est beaucoup plus piégeuse que le globbing ou l'utilisation de find, précisément pour les raisons évoquées ci-dessus.

  • # Lire les recommandations POSIX!

    Posté par (page perso) . Évalué à 7.

    On parle souvent de POSIX, alors en lire le contenu est une bonne chose !

    On y apprend, par exemple, qu'il est préférable d'utiliser printf au lieu d'echo

    • [^] # Re: Lire les recommandations POSIX!

      Posté par (page perso) . Évalué à 5.

      Et qu'il est conseillé d'utiliser plusieurs test joints par des && ou || plutôt qu'un seul test avec ses opérandes intégrées -a et -o, celles-ci étant vouées à la caducité à plus ou moins court terme.

      • [^] # Re: Lire les recommandations POSIX!

        Posté par (page perso) . Évalué à 3.

        plutôt qu'un seul test avec ses opérandes intégrées -a et -o, celles-ci étant vouées à la caducité à plus ou moins court terme.

        Pourrait-on avoir plus d'info sur ce sujet ?

        * Ils vendront Usenet^W les boites noires quand on aura fini de les remplir.

    • [^] # Re: Lire les recommandations POSIX!

      Posté par (page perso) . Évalué à 3.

      On y apprend, par exemple, qu'il est préférable d'utiliser printf au lieu d'echo…

      C'est pas comme si la dépêche racontait n'importe quoi! :-)

  • # Gestion des dates

    Posté par . Évalué à 10.

    Les dates sont une plaie en informatique. C'est vraiment très compliqué à gérer (entre les mois de 30 à 31 jours, sauf un qui fait 28 jours sauf tout les 4 ans, les secondes intercalaires qui peuvent être ajoutées ou supprimée de temps en temps, le changement d'heure…) et on trouve régulièrement des bug dans des logiciels pourtant éprouvés (la libc par exemple).

    Mon conseil, ne gérer pas les dates vous même (en shell comme ailleurs). Laissez-ça à des API qui font ça très bien. En shell cette API c'est date (c'est un standard POSIX utilisable avec n'importe quel shell).

    Son usage est assez simple (mais ça ne dispense pas de lire le man), l'idée c'est de convertir toutes les dates en entier (le nombre de secondes depuis epoch) puis de comparer ces entiers entre eux, puis de les convertir dans le format que l'on souhaite.

    Par exemple si je souhaite dans mon script savoir si nous sommes après ou avant le 6 octobre 2014 je vais faire ça ainsi :

    limit=$(date '+%s' -d '06/10/2014')
    now=$(date '+%s')
    if [ "${limit}" -lt "${now}" ] ; then
        echo "On a passé la date car $(date '+%d/%m/%y' -d "@$now") est plus grand que $(date '+%d/%m/%y' -d "@$limit")"
    fi

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Gestion des dates

      Posté par (page perso) . Évalué à 3.

      Note que normalement date ne gère pas les secondes intercalaires, car ce n'est pas répercuté dans le timestamp.

      • [^] # Re: Gestion des dates

        Posté par . Évalué à 3.

        Oui l'exemple de la libc n'est peut être pas le meilleur, mais si déjà t'évite les bugs classiques comme les mois de 28 à 31 jours par exemple c'est déjà pas mal.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Gestion des dates

      Posté par (page perso) . Évalué à 5.

      Je teste les maths plutôt comme ça :

      if (( $limit < $now ))
      then
    • [^] # Re: Gestion des dates

      Posté par . Évalué à 2.

      Exactement. Et autant éviter de gérer les changements d'heure été/hiver à la main.

    • [^] # Re: Gestion des dates

      Posté par (page perso) . Évalué à 2.

      Mais est-ce que ce sont des heures UTC ou des heures locales ?

      Perso, je fais tous mes scripts en Python dès qu'il y a des dates (j'ai alors des objets datetime qui ont un fuseau horaire) ou des caractères bizarres (j'ai alors des objets qui gère proprement l'Unicode). Mais bon, ça reste toujours galère de gérer les fichiers avec des accents sous Linux, ne serait-ce parce qu'on peut avoir n (avec n grand) fichiers différents ayant tous le même nom unicode.

      • [^] # Re: Gestion des dates

        Posté par . Évalué à 5.

        Locale pour UTC, il faut l'indiquer explicitement. Tu peut aller voir info date pour tous les détails.

        Perso, je fais tous mes scripts en Python dès qu'il y a des dates (j'ai alors des objets datetime qui ont un fuseau horaire)

        Ça tombe bien la commande date gère les fuseaux horaires.

        Pour de gros besoin de date oui le shell n'est pas une bonne idée, de même pour les mathématiques, pour des choses simples, il n'y a pas de raison de tout réécrire pour si peu.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Gestion des dates

        Posté par (page perso) . Évalué à 5.

        Mais bon, ça reste toujours galère de gérer les fichiers avec des accents sous Linux, ne serait-ce parce qu'on peut avoir n (avec n grand) fichiers différents ayant tous le même nom unicode.

        Hein, comment ça ? Deux fichiers ne peuvent pas avoir le même nom sur un même système de fichiers non…

    • [^] # Re: Gestion des dates

      Posté par . Évalué à 1.

      ce n'est pas plutôt mois/jour/année le format et donc le 10 juin 2014 plutôt ?

      • [^] # Re: Gestion des dates

        Posté par . Évalué à 3.

        En effet j'ai écris l'exemple un peu vite. Ça montre que ça reste casse gueule.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # NULL dans IFS?

    Posté par . Évalué à 1.

    Je suis assez surpris par l'exemple consistant à mettre le caractère NUL (\000) dans IFS.
    Autant que je sache, cela ne marche pas dans Bash qui ne peut pas manipuler NUL dans les variables.
    Il semble que cela fonctionne dans d'autres shells comme ZSH.

    Pour Bash, la solution réside dans l'option -d de 'read'

    find . -print0 | while read -d $'\0' file ; do echo == $file ; done

    La syntaxe $'…' active l'interprétation des séquences d'échappements dans la chaine …
    C'est équivalent à $(printf "…")

  • # un ptit mot sur dash

    Posté par . Évalué à 4.

    Merci pour l'article, j'ai notamment appris que $(..) et printf étaient maintenant dans POSIX :)

    Je voulais noter que si vous avez besoin de doc sur le shell, la page man de dash est vraiment bien foutue.

  • # shellcheck

    Posté par . Évalué à 2.

    Le tout premier lien dirige vers S_h_ellCheck, et non pas vers S_p_ellCheck, comme indiqué. Si quelqu'un pouvait corriger.

  • # Advanced Bash-Scripting Guide

    Posté par . Évalué à 6.

    Je suis étonné que personne ait donné les liens vers abs :
    http://www.tldp.org/LDP/abs/html/
    et en fr :
    http://abs.traduc.org/abs-fr/

  • # une discussion intéressante

    Posté par . Évalué à 1.

  • # passionnant post

    Posté par . Évalué à 2.

    Merci pour ce post très intéressant, j'ai appris plein de choses.

  • # Et le set ?

    Posté par . Évalué à 8.

    Le shell est très permissif avec les variables, et je demande toujours que les scripts soient développés avec:

    set -u: Write a message to standard error when attempting to expand a variable that is not set, and if the shell is not interactive, exit immediately.

    set -e: If not interactive, exit immediately if any untested command fails. The exit status of a command is considered to be explicitly tested if the command is used to control an if, elif, while, or until; or if the command is the left hand operand of an “&&” or “||” operator.

    C'est pénible au début; mais on évite pas mal de bugs bien compliqués, parce que lorsqu'on a un soucis et que le script continue a exécuter 20 lignes de codes derrière avec des mauvaises valeurs; on peut faire de beaux feux d'artifices.

    • [^] # Re: Et le set ?

      Posté par . Évalué à 2.

      C'est triste ton avis sur les feux d'artifices. :'(

      "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

    • [^] # Re: Et le set ?

      Posté par (page perso) . Évalué à 6.

      Pour info, set -e est la norme pour les scripts liés à l'empaquetage Debian (genre le postinst d'un paquet).

      À noter que la solution de mettre #! /bin/sh -e en tête de fichier n'est pas équivalente, parce qu'elle n'applique cette option qu'aux scripts lancés directement en ./script et non à ceux qu'on lancerait explicitement via un shell en sh script.

  • # Wiki ?

    Posté par . Évalué à 1.

    Ça vaudrait peut-être le coup de transformer cette dépêche en page Wiki, non ?

    Envoyé depuis ma Debian avec Firefox

  • # typo

    Posté par . Évalué à 1.

    Il me semble qu'il manque une quote dans la section sur l'Analyse de la sortie de la commande ls :

    NUL=$(printf '\000)

    Super dépêche et les commentaires ne sont pas en reste !

    • [^] # Re: typo

      Posté par (page perso) . Évalué à 6.

      Corrigé, merci. Ça aurait été dommage que les gens utilisant le shell aient un choc…

Suivre le flux des commentaires

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