Forum Programmation.shell bash : créer des fichiers numérotés successifs

Posté par (page perso) . Licence CC by-sa.
Tags : aucun
1
16
mar.
2019

Bonsoir,

Je suis un peu (beaucoup en fait) faible en script aussi j'ai besoin d'aide…

Je veux faire un script (lancé par cron) qui enregistre un fichier dans un répertoire. Le fichier doit être numéroté au format 00000.ext puis 00001.ext 00002.ext etc.

Comme c'est destiné à un raspberry pi susceptible de subir des interruptions et redémarrages j'aimerai que le script recherche le fichier de numéro le plus élevé et l'incrémente pour le suivant.

Ou bien que le script enregistre le numéro du dernier fichier dans un fichier et le lise au début du script puis le mette à jour à la fin. Peut-être est-ce mieux ?

Il n'y a pas d'horloge RTC ni de ntp disponible, donc je ne peux pas me fier à l'horodatage des fichiers :-(

Merci
Christian

  • # Quelques pistes

    Posté par (page perso) . Évalué à 6 (+5/-0).

    En combinant find (éventuellement sed pour être sûr de ne garder que la partie numérique) et sort (éventuellement avec son option -n), tu devrais pouvoir t'en sortir facilement. Cf. également printf pour la partie génération du fichier n+1 au bon format.

    Debian Consultant @ DEBAMAX

  • # moi

    Posté par (page perso) . Évalué à 3 (+2/-0). Dernière modification le 16/03/19 à 15:28.

    Moi je ne m'embêterai pas

    mylast_file=$(ls -1rt | tail -1)
    my_cpt=${mylast_file%%.*}
    

    Is it a Bird? Is it a Plane?? No, it's Super Poil !!!

    • [^] # Re: moi

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

      Comme criait mon prof de maths : « Il faut LIRE l'énoncé. »

      Debian Consultant @ DEBAMAX

      • [^] # Re: moi

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

        Tu veux pas que je t'écrive tout non plus ? Tu coinces où ?

        Is it a Bird? Is it a Plane?? No, it's Super Poil !!!

        • [^] # Re: moi

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

          Non, il ne veut pas que tu écrives plus, mais que tu lises plus ;-) (si j'étais taquin, j'ajouterais que tu l'aurais compris si tu avais lu son commentaire, mais n'allons pas jusque là ;-) ). En particulier le moment où il dit qu'on ne peut pas se baser sur les timestamps je suppose.

          • [^] # Re: moi

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

            cela dit, ls trie par ordre alphabétique, donc si le répertoire ne contient que des fichiers convenablement nommés, un ls -r | head 1 pourrait peut-être marcher.

            • [^] # Re: moi

              Posté par (page perso) . Évalué à 2 (+0/-0). Dernière modification le 17/03/19 à 12:21.

              Voire, inutile de demander un -r à ls, utiliser tail plutôt que head à la place.

              ls -1 | tail -1 | cut -d '.' -f 1

              donne la valeur, si le format de fichier est nnnn.YYYY

              • [^] # Re: moi

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

                Voire, inutile de demander un -r à ls, utiliser tail plutôt que head à la place.

                ça se discute. Vu que ls trie de toute façon, lui demander d'afficher dans l'ordre inverse du tri, ça doit pas coûter grand chose. Par contre, avec tail, tu dois parcourir tout le flux pour n'en garder que la fin. Sur des gros volumes de données, la différence de performance entre head et tail peut-être très significative. Je préfère donc ls -r | head, mais je peux changer d'avis si j'ai raté des éléments en faveur de tail.

                • [^] # Re: moi

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

                  Par contre, avec tail, tu dois parcourir tout le flux pour n'en garder que la fin

                  Non, pas lorsque l'entrée est un fichier standard.

                  tail remplit un buffer en remontant depuis la fin du fichier, puis le parcours à la recherche d'une fin de ligne en marche arrière à coup de memrch.

                  • [^] # Re: moi

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

                    qu'appelles-tu "fichier standard" ? tu parles de l'entrée standard ?

                    tail remplit un buffer en remontant depuis la fin du fichier,

                    sur un fichier, peut-être, mais comment il fait ça avec un flux sur stdin (dont la taille est potentiellement plus large que tout la mémoire disponible pour le système) ?

                    • [^] # Re: moi

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

                      sur un fichier, peut-être, mais comment il fait ça avec un flux sur stdin
                      (dont la taille est potentiellement plus large que tout la mémoire disponible pour le système) ?

                      Il s'agit alors d'une pipe dont la taille des buffers sera limitée à 64K (4K de base, soit une page, de mémoire).
                      tail va, lui, créer une chaîne de buffer, chacun limité, il me semble, au BUFSIZ de stdio.
                      ( 1KB, je crois )

                      Donc, oui, tail doit attendre la fin du flux et ça peut devenir un problème.
                      Mais ce cas est hautement improbable avec notre sortie de ls;
                      mieux, si ce risque existe, un faut revoir complètement le principe, le nœud problème se situant alors en amont de tail ou de head, AMHA.

                      • [^] # Re: moi

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

                        Et donc quand on est en situation de faire un choix arbitraire entre head et tail (ce qui est le cas ici avec ls ou ls -r), head est meilleur. Certes, sur des petits volumes, ce n'est pas significatif. Mais quand le choix ne coûte rien, autant prendre la solution qui supportera le mieux un changement d'échelle.

                        Mais ce cas est hautement improbable avec notre sortie de ls

                        soit dit en passant, j'ai déjà vu des systèmes de fichiers contenant beaaauuucoup de petits fichiers (au point d'atteindre le maximum d'inodes supporté) dans peu de répertoires.

                  • [^] # Re: moi

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

                    Bah oui, mais là tail lit sur un pipe …

          • [^] # Re: moi

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

            Sauf que, de base dans Raspbian est installé le package fake-hwclock.

            Du coup, même sans RTC ni connexion au réseau permettant de se synchroniser avec NTP, les timestamps seront tout de même cohérents. Probablement faux certes mais cohérents: un fichier y créé après un fichier x disposera bien d'un horodatage postérieur à celui de x.

            Le seul souci qu'on peut constater dans ce cas est une dérive aléatoire de l'horloge du Pi par rapport à l'heure réelle. Cette dérive sera d'autant plus importante que le Pi sera éteint longtemps et/ou fréquemment: fake-hwclock va enregistrer l'heure courante dans un fichier lors de l'extinction propre du Pi puis va relire le fichier au prochain démarrage et considérer que c'est l'heure 'juste' même si 15 jours se sont passés entre temps.

            • [^] # Re: moi

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

              Merci, c'est très intéressant.

              Mais comment est-ce que cela se passe en cas d'extinction brutale ? dans le cas qui m'intéresse, c'est un montage autonome, aucune chance d'avoir une interruption propre : s'il y a coupure d'alim, c'est brutale. Peu probable mais brutale.

              • [^] # Re: moi

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

                Au vu de la page man, si je rajoute un

                fake-hwclock save
                en crontab je limite déjà pas mal la casse.

                • [^] # Re: moi

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

                  Tu n'as même pas besoin de faire ça puisque c'est déjà en place.
                  Si tu jette un œil dans /etc/cron.hourly tu vas trouver un joli script fake-hwclock qui contient ça

                  #!/bin/sh
                  #
                  # Simple cron script - save the current clock periodically in case of
                  # a power failure or other crash
                  
                  if (command -v fake-hwclock >/dev/null 2>&1) ; then
                    fake-hwclock save
                  fi
                  

                  Cela dit, ajoute un DS3231. C'est presque plug'n'play, ça te prend 4 pins et en cadeau bonux, ça t'ajoute un capteur de température.

                  Juste par curiosité, c'est quoi le projet avec ton Pi ?

                  • [^] # Re: moi

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

                    Du timelapse (au fond du jardin pour le moment, en pleine nature ensuite) et ensuite essayer de faire du piégeage photo (avec détection de mouvement)

                    • [^] # Re: moi

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

                      Ah, du timelapse, ça c'est cool comme projet.
                      J'en ai fait un aussi il y a quelques temps en récupérant la carcasse d'un vieux projo halogène pour abriter un RPi 3, un disque dur, une caméra et une poignée de capteurs.

                      Avec un peu de bash, en plus de faire 'station météo', tout ce fourbi me pond ce genre de choses
                      Donc si jamais tu veux échanger plus avant sur le sujet… ;-)

                  • [^] # Re: moi

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

                    T’embêtes pas, j'ai creusé un peu moodle-box…
                    :-D
                    Joli projet !

                    • [^] # Re: moi

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

                      nan, là ce n'est pas pour la moodlebox. D'ailleurs j'ai lâché le développement de celle-ci, Nicolas Martignoni fait ça bien mieux et plus efficacement que moi ;-)

                      mais je continue à l'utiliser avec mes élèves…

  • # (Presque) hors-sujet

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

    Pourquoi se priver d'une horloge matérielle sur son Raspberry Pi ?

    Un DS3231 coûte 3 fois peanuts (par exemple ) et la configuration du bouzin est à la portée de n'importe qui en suivant les instructions qui vont bien.

  • # awk

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

    Je verrais bien ça en awk:

    $ FILENAME="$(ls | awk 'BEGIN{FS="."}/ext/{i = $1}END{i++; printf "%05d\n", i}').ext"; echo $FILENAME
    00003.ext
    $

  • # Fichier d'index

    Posté par (page perso) . Évalué à 2 (+0/-0). Dernière modification le 17/03/19 à 10:51.

    En ce qui concerne le deuxième cas, si on maîtrise l'environnement, on peut utiliser stocker dans un fichier le dernier index fourni:

    #!/bin/sh
    
    [ -w "./last.env" ] && . ./last.env
    LASTID=${LASTID:+`expr ${LASTID} + 1 `} 
    LASTID=${LASTID:-0}
    echo "LASTID=${LASTID}" > last.env
    printf "FILES.%05d.ext\n" ${LASTID}
    exit 0

    ex: touch `./ceScript.sh`
    L'inconvénient est que l'on repart à zéro si le fichier d'index est perdu, mais l'on peut ajouter des contrôles pour éviter d'écraser un fichier déjà existant.

    L'avantage est que l'on garde l'indexation même si les derniers fichiers sont effacés, ce qui va créé des trous.

    C'est généralement ce cas là que j'utilise dans mes scripts, combiné avec une recherche en cas de doute ou d'erreur comme la demande de création d'un fichier existant.

  • # Recherche du dernier

    Posté par (page perso) . Évalué à 2 (+0/-0). Dernière modification le 17/03/19 à 11:16.

    La deuxième solution est la recherche du dernier index crée, que l'on peut voir comme un complément
    au fichier d'index.

    #!/bin/sh
    
    PREFIX="TOTO"
    
    ls ${PREFIX}.*.ext 1>/dev/null 2>1
    if [ ${?} -eq 0 ]; then
    LASTID=`ls -1 ${PREFIX}.*.ext | tail -1 | cut -d . -f 2`
    LASTID=${LASTID:+`expr ${LASTID} + 1 `} 
    else
    LASTID=${LASTID:-0}
    fi
    printf "${PREFIX}.%05d.ext\n" ${LASTID}
    exit 0

    ls trie par défaut par ordre alphabétique, il suffit donc d'extraire avec cut la partie située entre les deux points du dernier élément ( tail ) et de l’incrémenter. Le test est là pour pouvoir créer un premier fichier, ls renvoie une erreur dans ce cas.

    ex: touchsh ./ceScript.sh

  • # merci

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

    merci à tous pour vos pistes, je vais creuser ça …

Envoyer un commentaire

Suivre le flux des commentaires

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