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

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

Shell et commandes GNU Discussion :

Décrémenter un compteur depuis une fonction lancée par un find


Sujet :

Shell et commandes GNU

  1. #1
    Membre du Club
    Homme Profil pro
    Technicien d'étude
    Inscrit en
    Septembre 2007
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien d'étude

    Informations forums :
    Inscription : Septembre 2007
    Messages : 56
    Points : 49
    Points
    49
    Par défaut Décrémenter un compteur depuis une fonction lancée par un find
    Bonjour à tous et à toutes,

    Dans un script bash, je voudrai décrémenter un compteur à chaque fois qu'une fonction c'est exécutée. Cette fonction est elle même appelée par un find.
    Actuellement, j'ai bricolé un truc comme ça :
    Une première fonction va trouver les fichiers qui vont être traités par la suite dans mon arborescence
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    function comptage
    {
        xxxx=$(find $source -type f \( -name "*.doc" -or -name "*.xls" \) | wc -l)
        echo "$xxxx"
    }
    export -f comptage
    Cette fonction elle même est appelée par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    compteur=0 && export compteur
    compteur=$(comptage)
    Jusque là tout fonctionne.

    J'ai ensuite ma seconde fonction qui, elle va faire le traitement proprement dit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function traitement
    {
        echo "$compteur et du texte"
        "mon code qui fonctionne"
        $compteur=`expr $compteur - 1`
    }
    export -f traitement
    Le traitement lui-même n'a pas d'importance dans mon souci puisqu'il fonctionne parfaitement

    La fonction traitement est appelée par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    find $source -type f \( -name "*.doc" -or -name "*.xls" \) -exec bash -c 'traitement '$0' ' {} \; ;
    Là aussi, l'appel de la fonction marche sans aucun souci.

    Là où le bas blesse, c'est que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    echo "$compteur et du texte"
    dans la fonction affiche toujours la valeur initiale, non décrémentée.

    Si quelqu'un a une idée, je l'en remercie par avance.

  2. #2
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 389
    Points
    19 389
    Par défaut
    Bonjour,

    ce n'est pas possible ainsi, car le bash -c ne "remontera" pas la valeur du compteur dans le shell qui appelle bash -c !

    les processus enfants ne peuvent pas modifier les variables de leur parent.
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  3. #3
    Membre du Club
    Homme Profil pro
    Technicien d'étude
    Inscrit en
    Septembre 2007
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien d'étude

    Informations forums :
    Inscription : Septembre 2007
    Messages : 56
    Points : 49
    Points
    49
    Par défaut
    Bonsoir et merci.

    Je vais voir si je trouve une autre approche. De toutes façons c'est pas dramatique, c'est "juste" pour avoir une approche du déroulement du script.

    Je laisse ouvert, au cas où je trouve une solution.

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Mai 2016
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2016
    Messages : 12
    Points : 36
    Points
    36
    Par défaut
    Salut,

    Avec les "sh" (et donc avec bash), il est toujours plus difficile de faire remonter les valeurs de variables vers la fonction ou le script appelant.

    Le plus souvent je "remonte" une (ou plusieurs) valeur(s) par le(s) comtenu(s) d'un (ou plusieurs) fichier(s)

    Dans l'exemple ci-dessous, "compteur" est en fait un fichier.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    # pour ne pas a avoir a repeter la commande de find, j'en fais une variable qui sera transformee en commande par 'eval'
    CMD_FIND='find $source -type f \( -name "*.doc" -or -name "*.xls" \) '
     
    comptage()
    {
       eval "${CMD_FIND:?}"  | wc -l
    }
     
    # initialisation du compteur
    comptage >compteur
     
    # le traitement, j'en fais un script (un "here document") qui decrementera le compteur
    #     prendre garde a echapper certains caracteres tels que le "$"
    cat >traitement.sh <<EOF
    echo \$((\$(cat compteur) - 1 )) >compteur
    echo mise au point compteur contient \$(cat compteur) pour le fichier \$1 >&2
    EOF
     
    # j'aime bien voir le contenu des fichiers que je cree, c'est bien
    # utile pour la mise au point:
    echo ===========  le fichier traitement.sh contient ========
    cat traitement.sh
    echo =======================================================
     
    # le decomptage appelle le script traitement.sh en argument du find
    decomptage()
    {
        eval "${CMD_FIND:?} -exec bash traitement.sh {} \;"
    }
     
    # le decomptage manipule le "compteur"
    decomptage
    Attention à mettre le fichier "compteur" au bon endroit, si tu ne veux pas qu'il soit ...compté et decompté

  5. #5
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 389
    Points
    19 389
    Par défaut
    Bonjour,

    pour ne pas a avoir a repeter la commande de find, j'en fais une variable
    et si tu en faisais une fonction ou un alias ? ça éviterait d'avoir à évaluer une variable !

    et, plutôt qu'utiliser un fichier, pourquoi ne pas utiliser une substitution de commande qui serait affectée à une variable ?

    enfin, si tu veux qu'aucun développement ne soit effectué dans un Document en ligne, place le premier délimiteur entre apostrophes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $ var=maVar
    $ cat <<'eof'
    > echo "$var"
    > eof
    echo "$var"
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Mai 2016
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2016
    Messages : 12
    Points : 36
    Points
    36
    Par défaut
    Salut,

    Merci pour tes remarques (c'est justement pour découvrir d'autres façons de programmer, et accroître mon expérience, que je viens sur ce forum).

    OK pour utiliser un alias plutôt qu'un "eval de variable"

    OK pour le premier délimiteur de "Document en ligne" commençant par une apostrophe.

    Cela donne un script du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    alias cmd_find='find $source -type f \( -name "*.doc" -or -name "*.xls" \) '
     
    comptage()
    {
       cmd_find | wc -l
    }
     
    comptage >compteur
     
    # le traitement (sous forme de "Document en ligne")
    cat >traitement.sh <<'EOF'
    echo $(($(cat compteur) - 1 )) >compteur
    echo mise au point compteur contient $(cat compteur) pour le fichier $1 >&2
    EOF
     
    # le decomptage appelle le script traitement.sh en argument du find
    decomptage()
    {
        cmd_find -exec bash traitement.sh {} \;
    }
     
    decomptage
    Citation Envoyé par N_BaH Voir le message
    et, plutôt qu'utiliser un fichier, pourquoi ne pas utiliser une substitution de commande qui serait affectée à une variable ?
    Par contre, je ne saisis pas comment (et quel fichier? ) remplacer par une "substitution de commande qui serait affectée à une variable".
    S'il s'agit du fichier compteur de mon exemple, j'ai utilisé cette méthode justement parce que, dans l'intervention #1, la variable $compteur ne perdurait pas d'un appel à l'autre de la fonction traitement par la commande find.
    S'il s'agit du fichier "traitement.sh", je n'ai pas réussi à écrire correctement la chaîne de caractères en paramètre du -exec de find, et qui remplacerait "traitement.sh".
    Si tu peux m'éclairer...

  7. #7
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 389
    Points
    19 389
    Par défaut
    le code de Aenean n'est pas super clair, à l'instar de sa démarche, mais decomptage devrait être appelé comme comptage.
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  8. #8
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 280
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 280
    Points : 12 729
    Points
    12 729
    Par défaut
    Bonjour,

    On peut aussi passer par un pipe nommé que l'on anonymise, ce qui donnerait par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #!/bin/bash
     
    compteur=100
     
    #On crée le pipe nommé
    PIPE=$(mktemp -u)
    mkfifo $PIPE
    # on attache le FD 3 sur le pipe nommé
    exec 3<>$PIPE
    #Puis on delete tout de suite le pipe nommé
    rm $PIPE
     
    # Initialisation de la fonction traitement qui lira la valeur du compteur dans le pipe
    # et ecrira la nouvelle valeur du compteur dans meme pipe
    traitement ()
    {
    	read compteur 0<&3;
    	echo "$compteur et du text";
    	echo $((compteur - 1)) 1>&3
    }
     
    #IMPORTANT: il faut ecrire une ligne avant de lire, sinon le process de lecture restera bloqué
    echo $compteur >&3
    find  . -exec bash -c 'traitement '$0' ' {} \;
     
    #On n'oublie surtout pas de refermer le pipe
    exec 3>&-
    Et ce qui donne chez moi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    $ bash script_FDANON.sh 
    100 et du text
    99 et du text
    98 et du text
    97 et du text
    96 et du text
    95 et du text
    94 et du text
    93 et du text
    92 et du text
    91 et du text
    90 et du text
    89 et du text
    88 et du text
    87 et du text
    86 et du text
    85 et du text
    84 et du text
    83 et du text
    82 et du text
    81 et du text
    80 et du text
    79 et du text
    78 et du text
    77 et du text
    Cordialement.

  9. #9
    Membre du Club
    Homme Profil pro
    Technicien d'étude
    Inscrit en
    Septembre 2007
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien d'étude

    Informations forums :
    Inscription : Septembre 2007
    Messages : 56
    Points : 49
    Points
    49
    Par défaut
    J'ai trouvé une solution qui fonctionne. Elle n'est sans doute pas optimisée mais bon...

    Cette solution présente, à mon sens, un intérêt supplémentaire : Le ls stocké dans une variable qui est ensuite découpée rend la main sur le système de fichier beaucoup plus vite que le traditionnel find. En effet, d'après ce que j'ai pu voir, le find ne stocke pas dans une "variable" à lui le contenu de sa scrutation. Il trouve la première occurrence, fait le job, puis passe à l'occurrence suivante. Ainsi, si le traitement de la fonction appelée par le find est long (encodage vidéo par exemple), rajouter des fichiers sur le système de fichier à traiter engendre des fonctionnements aléatoires (certains fichiers ajoutés sont traités, d'autres pas, en fonction de l'endroit où le find est passé ou pas).

    Ensuite, comme je fais un traitement en fonction de l'ancienneté (pour ne pas traiter les fichiers déjà traités), il n'a pas été bien compliqué, avec awk, de récupérer date et heure de chaque fichier.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    declare -a arr
    filelist=$(ls -dlRh --time-style long-iso $(find $source)|  awk '{print $6 $7 $8}')
    # echo "$filelist"
    OLDIFS=$IFS
    IFS=$'\n'
    IFS=$OLDIFS
     
    pointeur=0 && export pointeur
     
    for word in $filelist;
    do
      # arr[${#arr[@]}+1]=$(echo "$word");
     
      pointeur=$((pointeur+1))
      if [[ $word =~ .avi ]] || [[ $word =~ .mp4 ]]
        then
            datefichier="${word:0:10}"
            heurefichier="${word:10:5}"
            dateheure="$datefichier $heurefichier"
            fichier="${word:15}"
            todate=$(date -d "$dateheure" "+%Y%m%d%H%M")
            cond=$(date -d "$LASTDATE" "+%Y%m%d%H%M%S")
            # echo "-$todate-$cond-"
            if [[ $todate > $cond ]];
            then
                fonction_qui_traite $fichier $pointeur
            fi
        fi
    done

  10. #10
    Expert confirmé
    Homme Profil pro
    Développeur informatique en retraite
    Inscrit en
    Avril 2008
    Messages
    2 101
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique en retraite

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 101
    Points : 5 849
    Points
    5 849
    Par défaut
    Je te souhaite de ne pas avoir de fichier dont le nom contienne des caractères spéciaux (comme "espace" "return" etc.)!
    (qui ne passent pas bien l'énumération avec "for")

    Et si jamais N_BaH voit un appel de "ls" dans un script, je te dis pas...

  11. #11
    Membre du Club
    Homme Profil pro
    Technicien d'étude
    Inscrit en
    Septembre 2007
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien d'étude

    Informations forums :
    Inscription : Septembre 2007
    Messages : 56
    Points : 49
    Points
    49
    Par défaut
    J'avoue humblement que c'est pas la panacée mais pour mon besoin, ça marche nickel

    Mes noms de fichiers sont "propres" et j'en ai pas tant que ça

    J'en conviens, il y a sans aucun doute bien mieux mais là, une fois de plus, j'atteins mon niveau d'incompétence !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 8
    Dernier message: 18/02/2016, 04h56
  2. Réponses: 3
    Dernier message: 21/12/2005, 11h55
  3. Réponses: 3
    Dernier message: 16/12/2005, 15h44
  4. Retourner un tableau depuis une fonction
    Par obalais dans le forum C++
    Réponses: 2
    Dernier message: 20/10/2005, 16h49
  5. Ecrire dans un tableau html depuis une fonction js ?
    Par botanica dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 05/10/2005, 12h48

Partager

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