Forum Programmation.shell Pas trouver fichier

Posté par  . Licence CC By‑SA.
Étiquettes :
4
8
août
2024

Hello,

J’ai une question à la con, tellement à la con que je ne pourrais trouver le sommeil sans avoir au moins un indice.

Voici le code que j’ai (j’ai volontairement “anonymisé” le nom des variables pour que ce soit plus générique). Ce que fait le grep n’a pas grande importance, l’idée c’est que je ne traite que certaines lignes de chaque fichier. Le point important c’est de traiter les fichiers dans l’ordre chronologique, d’où les options -rt passées à ls.

declare -a files=($(ls -rt "${@}"))
shift "${#files[@]}"
for line in $(grep -Eh '^<'  "${files[@]}" | tr -d '\\'); do
    echo "${program} ${line} ${global_var}" "${@}" >&2
    eval "${program} ${line} ${global_var}" "${@}"
done

Alors voilà l’idée, le script est appelé avec comme arguments un nombre arbitraire de fichiers, potentiellement des “globs” (ie: plop*.foo) qui se développeront donc eux-mêmes en un nombre imprévisibles d’arguments.

Ce que je voudrais faire, et qui me semble de plus en plus con en avançant dans la rédaction de ce post, c’est que les arguments restant qui ne sont pas des fichiers puissent se retrouver dans "${@}", pour être passés à la commande qui est lancée dans la boucle for pour les lignes dans chaque fichier. D’où le shift du nombre d’éléments du tableau "files", correspondant au nombre de fichiers que le globbing aura généré.
Je précise que j’assume que tous les arguments qui sont des fichiers sont en premiers, et ceux à reporter se trouvent tous à la suite. Pas d’intrication entre les deux.

Le « problème » que j’ai, c’est que la commande ls va forcément se plaindre que les arguments qui ne sont pas des fichiers, et bien, n’en sont pas (forcément _o_). Et quelque part ça me gêne. Mais j’ai du mal à voir où.

Car en effet, il suffit qu’un seul argument soit bien un fichier (ou un “glob”) pour que l’affectation d’un élément au tableau "files" soit un succès, faisant qu’ainsi même avec l’option errexit ça ne pose aucun problème. Et je sais qu’un simple 2>/dev/null sur le ls ôterait ces vilains messages de ma vue me permettant d’oublier que je fais du mal à un utilitaire du shell mais il y a quelque chose qui me gêne.

À priori j’aurais le même genre de soucis avec find/xargs/sort, et le problème est peut-être de me cantonner à des arguments positionnels (ie: pas de getopts), mais j’ai une raison pour ça dans ce cas précis (qui n’est pas le sujet du post en l’occurrence).

Une piste éventuelle que j’ai serait de tester chaque argument pour savoir s’il désigne un ou plusieurs fichiers, et ne passer à ls que ces arguments, mais je suis vraiment pas sûr que ce soit optimal.

D’aucun ne manqueront pas de penser que je suis masochiste à utiliser Bash de cette façon (voir tout court ^^), ou que quand on peut mettre un 2>/dev/null tout simple faut vraiment être tordu pour se poser ce genre de question, ou encore que « eval c’est le mal » ou « les arguments positionnels LOL!  » , et bien qu’ils n’hésitent pas à me faire part de leur avis, ce sera sans doute constructif comme toujours.

(non désolé je n’ai pas d’aspirine)

  • # passer tes arguments autrement ?

    Posté par  (site web personnel) . Évalué à 8 (+5/-0). Dernière modification le 08 août 2024 à 10:21.

    Tu pourrais passer tes arguments (ou tes fichiers au choix) via l'entrée standard ou une variable d'environnement histoire de ne pas mélanger, ce qui te permettrait un find -type f pour éviter liens, répertoires, sockets et autres joyeusetés, ainsi que le non-existant (restera le non-lisible, les répertoires non exécutables, etc.).

    (Et shellcheck est ton ami quand tu te lances dans de grosses basheries)

    J'aurais mis un -- dans le ls pour bloquer des options éventuelles, mais là tu les veux explicitement… et une variable pour la chaîne construite deux fois à la fin, pour déboguer j'imagine.

    Par contre je crains que ça soit trop tard pour ta nuit de sommeil.

    • [^] # Re: passer tes arguments autrement ?

      Posté par  (site web personnel) . Évalué à 5 (+3/-0). Dernière modification le 08 août 2024 à 09:37.

      le   --   est interprété en tiret long par le Markdown de LinuxFr.org ;-)

      j'aurais aussi ajouté un -1 pour lister ligne par ligne pour s'éviter tout bug avec des fichiers qui contiendraient un espace

      • [^] # Re: passer tes arguments autrement ?

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

        le — est interprété en tiret long par le Markdown de LinuxFr.org ;-)

        Bien vu, échappé, merci.

      • [^] # Re: passer tes arguments autrement ?

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

        Si on veut quelque chose de robuste, il faut passer par \0 (cf. find -print0 et xargs -0).

        Trop de blagues possibles autrement (coucou les apostrophes, guillemets simples ou doubles, entre autres choses).

        Debian Consultant @ DEBAMAX

        • [^] # Re: passer tes arguments autrement ?

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

          C’est tout à ton honneur de signaler ce point très important Il est indispensable pour qui veut utiliser un shell comme Bash/KSH/etc.. Mais c’est un peu à côté de la plaque pour ma problématique.

          xargs -0

          shuf -z

          et bien d’autres…

          Perso parfois j’utilise les caractères qui ont été )

          Pour quelqu’un qui vient de Windows c’est à mon avis assez déstabilisant d’apprendre qu’il n’y a en tout et pour tout que que deux caractères qui ne peuvent pas se trouver dans un nom de fichier : “NULL”, aka \0. C’est à cause de son utilisation à plus bas niveau si j’ai bien compris, il sert à marquer la fin des chaînes en C).

          L’autre c’est '/', puisque il sert précisément à permettre l’arborescence de fichier (il me semble qu’on peu, on recompilant Linux et, je suppose la glibc, utiliser un autre caractère. Je sais même pas si c’est encore possible mais à ce niveau…

  • # Des exemples svp :)

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

    C'est compliqué de donner une solution sans trop savoir comment tu appelles ton script. Quand tu appelles ton script, comment fais-tu la différence entre les fichiers et les options que tu souhaites passer à ton programme ? J'ai bien compris que les options sont placées après les fichiers, mais la question est de savoir comment déterminer le point de passage des fichiers aux options.
    As-tu déjà déterminer des conventions pour ce point ? (genre toutes les options doivent commencer par --, et j'accepte le fait que ça ne prend pas en compte les noms de fichiers qui commencent par --)

    Si tu as des exemples, ce sera plus simple pour comprendre le contexte.

    Cela dit, voici une maigre proposition qui part du principe que les arguments qui sont des fichiers qui existent sont passés au grep et les autres arguments sont passés à ton programme.

    #!/usr/bin/env bash
    
    FILES=()
    OPTS=()
    for var in "$@"; do
            [[ -f "$var" ]] && FILES+=("$var") || OPTS+=("$var")
    done
    
    echo "Fichiers par ordre chronologique: $(ls -rt "${FILES[@]}")"
    echo "Options pour la commande: ${OPTS[*]}"

    Une fois que tu as tes fichiers d'un côté et des options de l'autre, tu peux en faire un peu ce que tu veux (comme trier les fichiers par ordre chronologique).

    • [^] # Re: Des exemples svp :)

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

      C'est astucieux mais insuffisant ;)
      Si un fichier n'existe pas, à cause d'une faute de frappe, par exemple, son nom sera placé dans la liste des options.
      Il vaudrait que les options correspondent à une liste prédéfinie puis que les fichiers existent.

      N.B. : je ne suis spas sûre d'avoir pleinement compris le problème initial ;)

      • [^] # Re: Des exemples svp :)

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

        Grr
        Il vaudrait mieux vérifier que les options…

      • [^] # Re: Des exemples svp :)

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

        C'est astucieux mais insuffisant ;)

        Ouais, je sais bien :) C'était juste une proposition pour lancer la discussion. Une autre grosse lacune est que ça ne fonctionne qu'avec les fichiers et pas avec les dossiers, mais ça fait parti des zones d'ombre de la demande.

        Pour être plus générique et avec la convention "tous les arguments qui sont commencent par --* sont des options", et on pourrait arriver sur un truc du genre :

        #!/usr/bin/env bash
        
        FILES=()
        OPTS=()
        for var in "$@"; do
          case "$var" in
            --*)
              OPTS+=("$var")
              ;;
            *)
              FILES+=("$var")
              ;;
          esac
        done
        
        echo "Fichiers par ordre chronologique: $(ls -rt "${FILES[@]}")"
        echo "Options pour la commande: ${OPTS[*]}"

        Mais on pourrait également imaginer de séparer les fichiers des options avec un -- (par exemple monscript.sh fichier1 file*.txt -- --option1 -truc2 :

        #!/usr/bin/env bash
        
        FILES=()
        OPTS=()
        DEST="FILES"
        
        for var in "$@"; do
          case "$var" in
            --)
              DEST="OPTS"
              ;;
            *)
              case "$DEST" in
                FILES)
                  FILES+=("$var")
                  ;;
                OPTS)
                  OPTS+=("$var")
                  ;;
              esac
              ;;
          esac
        done
        
        echo "Fichiers par ordre chronologique: $(ls -rt "${FILES[@]}")"
        echo "Options pour la commande: ${OPTS[*]}"
        • [^] # Re: Des exemples svp :)

          Posté par  . Évalué à 4 (+3/-0). Dernière modification le 09 août 2024 à 19:50.

          Bonjour

          … séparer les fichiers des options avec un -- (par exemple monscript.sh fichier1 file*.txt -- --option1 -truc2

          C'est ce que fait bash, mais il fait l'inverse : les options d'abord.
          monscript.sh --option1 -truc2 -- fichier1 file*.txt

          Extrait du manuel de bash :

          … Deux caractères -- indiquent la fin des options et désactivent le traitement des arguments.
          Tous les arguments après le -- sont traités comme des noms de fichiers et paramètres.
          - est équivalent à --.
          …
          

          Voir le retour de la ligne de commande suivante :

          man --pager='less -p "\-\- "' bash
          
          • [^] # Re: Des exemples svp :)

            Posté par  . Évalué à 1 (+0/-0). Dernière modification le 11 août 2024 à 13:36.

            En êtes-vous bien sûr ? Avez-vous testé ?
            -- est une option pour invoquer bash pas pour les scrpits bash.

Envoyer un commentaire

Suivre le flux des commentaires

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