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

Scripts/Batch Discussion :

Explication de l'expansion retardée [Batch]


Sujet :

Scripts/Batch

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    381
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 381
    Points : 231
    Points
    231
    Par défaut Explication de l'expansion retardée
    Bonjour à tous,

    je suis en train de m'initier à la programmation de batch windows, et en parcourant les tutoriels, je suis tombé plusieurs fois sur la problématique de l'expansion retardée, qui m'a l'air importante.
    A chaque fois, l'exemple et les explications données sont les mêmes :

    ************
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    set VAR=avant
    if "%VAR%" == "avant" (
    set VAR=après
    if "%VAR%" == "après" @echo Cela marche si vous lisez ce message
    )
    n'affichera jamais le message car la variable %VAR% présente dans les deux
    instances de IF est remplacée à la lecture de la première instance de IF,
    étant
    donné que le corps de IF, instruction composée, est logiquement inclus. Donc
    le
    IF compris dans l'instance compare "avant" avec "après" et il n'y aura
    jamais
    égalité.
    ************

    ce qui correspond à l'aide standard de la commande "set" me semble-t-il...
    Cependant je ne comprends pas cette explication, et je ne comprends pas pourquoi sans expansion retardée, le message n'est pas affiché
    Intuitivement ca a l'air correct, et si j'écris l'équivalent dans d'autres langages, ca passera très bien. Quelle est la différence ?

    Merci

  2. #2
    Membre éprouvé
    Avatar de maxim_um
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    895
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 895
    Points : 1 018
    Points
    1 018
    Par défaut
    Salut,

    Citation Envoyé par nox75 Voir le message
    en parcourant les tutoriels, je suis tombé plusieurs fois sur la problématique de l'expansion retardée, qui m'a l'air importante.
    A chaque fois, l'exemple et les explications données sont les mêmes :
    /...
    ce qui correspond à l'aide standard de la commande "set" me semble-t-il...
    Et bien, c'est très simple. Beaucoup l'utilisent ou plutôt savent la reproduire, mais tous ne connaissent pas réellement sa signification, ni même sa véritable portée.

    Pour ce qui est des différents types de langages de programmation auxquels tu pourrais faire allusion, sur un plan conceptuel ils se rapprochent, mais sur le plan fonctionnel ils sont différents. Pour n'en retenir que deux, programme compilé et programme interprété, on peut dire que là où un programme compilé (.exe) sera naturellement performant de par même sa construction (build), un programme interprété (script .cmd dans notre cas) se verra diminuer au niveau de ses performances parce qu'il fait notamment appel à un intermédiaire, l'interpréteur de commande (pour Windows c'est cmd.exe [le cas de command.com/ntvdm ne sera pas abordé ici] ).

    Dans tous les cas, l'un comme l'autre, tendent à disposer de routines optimales pour justement gagner en fonctionnalité, efficacité et rapidité d'exécution. À ce juste titre, l' «expansion retardée des variables d'environnement» est une évolution intéressante du vieillissant interpréteur de commande Windows, même si la manière dont elle a été intégrée reste discutable.

    Aussi on aurait tendance à croire que l'interpréteur de commande est composé d'une seule entité, parce qu’un seul programme. En fait, il y en a deux, le préprocesseur de commande et le processeur de commande.
    Le préprocesseur, dans ses fonctionnalités principales, se charge de la lecture et de l'interprétation symbolique de l'instruction. Le processeur de commande, lui, se charge de faire l'interprétation littérale de l'instruction (instruction transmise par le préprocesseur) puis procède à son exécution. Tout cela se fait selon un ordre séquentiel, c'est à dire ligne par ligne.
    Enfin, pour en terminer avec cet aspect, notez qu'au moment de l'optimisation du code du Shell, les phases d'interprétations sont noyées dans les sources pour former l'analyseur lexicosyntaxique (parser). De là, on finit par donner à un moteur de script, et en particulier au Shell, tout naturellement le nom d'interpréteur.

    Maintenant que la programmation d'une interface en ligne de commande a été mise en lumière, attachons-nous aux quelques particularités du Shell qui nous préoccupe.

    Ainsi, en ce qui concerne «cmd.exe», les blocs d'instructions (For, If, bloc d'instructions multiples) et les lignes d'instructions composées (... & ...) ont été définies par défaut comme suit : faire la lecture et l'interprétation de l'instruction en une passe avant de la transmettre en vue de son exécution. Ceci à ces avantages (rapidité et compatibilité ascendante) et ces inconvénients (adjonction d'une nouvelle syntaxe, nécessité de plus de mémoire). Ainsi, ce qu'il faut bien gardé à l'esprit, c'est qu'avant même que le processeur de commande n'ait exécuté quoi que ce soit (ni même les assignations de type «set ...»), le bloc (ou l'instruction composée) a déjà été parcouru (lu) et traduit (interpréter) dans sa globalité par le préprocesseur de commande. Fort de cette information, les choses vous paraissent certainement plus claires.

    Mais peut-être qu'un exemple sera plus parlant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ::Voici une instruction:
    echo salut
    
    ::Voici une instruction combinée:
    echo salut |pause
    
    ::Voici une instruction composée (de deux instructions):
    echo salut & echo alpha
    
    ::Voici un bloc d'instructions:
    (echo salut
    echo alpha)
    Lorsque vous utilisez des instructions de ce genre dans votre script, tout vous semble se dérouler normalement, car l'affichage que vous obtenez correspond bien à vos attentes. Là où ça peut paraitre se gâter, c'est quand vous y incluez des variables et précisément quand ces mêmes variables sont précédées de leurs assignations. Agir de la sorte dans le mode par défaut du Shell, c'est sans compter sur le travail accompli par le préprocesseur de commande.

    Voilà comment il opère dans ce contexte:
    (attention, pour alléger le texte je vais intentionnellement passer sous silence certaines étapes, et faire un abus de langage en utilisant le mot Shell en lieu est place de processeur de commande, ceci afin d'éviter toute confusion avec le préprocesseur).

    Soit un script quelconque:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    set var=aaa
    (set var=ccc
    echo %var%)
    echo %var%

    Le préprocesseur de commande lit la première instruction (ligne 1), qu'il délimite par le passage à la ligne.
    Ne reconnait rien de particulier (attention, quand je dis qu'il ne reconnait rien de particulier, ça ne veut pas dire qu'il ne fait rien!!!).
    Transmet son exécution
    [conséquence: le Shell exécute la commande interne «set var=aaa» et la variable «var» contient maintenant la chaine "aaa"]

    Nouvelle instruction, nouvelle analyse par le préprocesseur de commande.
    Le préprocesseur de commande lit la deuxième instruction.
    Il reconnait un bloc d'instruction [(...)], l'analyse en une seule passe par décomposition en simple instruction.
    Lit la première instruction décomposée [ligne 2: «set var=ccccc»], ne reconnait rien de particulier, passe à la suivante.
    Lit la seconde instruction décomposée [ligne 3: «echo %var%»], reconnait une variable [%], étend cette dernière à la valeur qui lui correspond à cet instant de l'analyse,
    comme à cet instant «var» vaut "aaa", la seconde instruction est traduite (interpréter) de cette manière [echo aaa], ainsi, se qui se trouve maintenant en mémoire ce n'est plus ce que nous avions écrit au départ [echo %var%] mais bien [echo aaa].
    Fin de l'interprétation du bloc, transmission des instructions pour exécution.
    Transmission de [set var=ccc]
    [conséquence: le Shell exécute la commande «set var=ccc» et la variable «var» contient maintenant la chaine "ccc"].
    Transmission de [echo aaa].
    [conséquence: le shell exécute la commande «echo aaa» et affiche la chaine "aaa"].

    Notez qu’en aurait eu le même résultat avec ce script où vous reconnaissez une instruction composée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    set var=aaa
    set var=ccc & echo %var%
    echo %var%

    Cependant, il reste possible d'obliger (enable) l'interpréteur de commande à attendre le dernier moment (delayed) pour étendre (expansion) localement (...local) les variables d'environnement, et ainsi forcer une lecture-mémoire avec la syntaxe adéquate : «!» au lieu de «%».
    Pour cela, il faut utiliser l'instruction ouvrante «SetLocal EnableDelayedExpansion» [ou appeler l'interpréteur de commande avec le paramètre «/v» au lancement d'une nouvelle instance de l'interpréteur de commandes] et l'instruction fermante «EndLocal» une fois que l'usage des expansions retardées des variables d'environnements n'est plus nécessaire.
    «EndLocal» est d'ailleurs appelé tacitement à la fin du script même si l'utilisateur ne l'a pas demandé, mais son écriture est fortement recommandée pour un codage propre.

    Notre script initial s'écrit maintenant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    setlocal enabledelayedexpansion
    set var=aaa
    (set var=ccc
    echo !var!)
    echo %var%
    endlocal
    Nouvelle instruction, nouvelle analyse par le préprocesseur de commande.
    Le préprocesseur de commande lit la première instruction [setlocal enabledelayedexpansion].
    Ne reconnait rien de particulier.
    Transmet son exécution
    [conséquence: le Shell exécute la commande interne «setlocal enabledelayedexpansion», il positionne un nouvel environnement pour une allocation dynamique des variables et prend maintenant en charge une nouvelle syntaxe].

    Nouvelle instruction, nouvelle analyse par le préprocesseur de commande.
    Le préprocesseur de commande lit la deuxième instruction [ligne 2], qu'il délimite par le passage à la ligne.
    Ne reconnait rien de particulier.
    Transmet son exécution.
    [conséquence: le Shell exécute la commande interne «set var=aaa» et la variable «var» contient maintenant la chaine "aaa"]

    Nouvelle instruction, nouvelle analyse par le préprocesseur de commande.
    Le préprocesseur de commande lit la troisième instruction.
    Il reconnait un bloc d'instruction [(...)], l'analyse en une seule passe par décomposition en simple instruction.
    Lis la première instruction décomposée (ligne 3: set var=ccc), ne reconnait rien de particulier, passe à la suivante.
    Lis la seconde instruction décomposée (ligne 4: echo !var!), ne reconnait rien de particulier.
    Fin de l'interprétation du bloc, transmission des instructions pour exécution.
    Transmission de [set var=ccc].
    [conséquence: le Shell exécute la commande «set var=ccc» et la variable «var» contient maintenant la chaine "ccc"].
    Transmission de [echo !var!].
    [conséquence: Le Shell reconnait une variable [!], étend cette dernière à la valeur qui lui correspond à cet instant de l'analyse syntaxique,
    comme à cet instant «var» vaut "ccc", la seconde instruction est traduite (interpréter) de cette manière [echo ccc],
    le Shell exécute alors la commande «echo ccc» et affiche la chaine "ccc"].

    Nouvelle instruction, nouvelle analyse par le préprocesseur de commande.
    Le préprocesseur de commande lit la quatrième instruction (ligne 5), qu'il délimite par le passage à la ligne.
    Reconnait une variable [%], étend cette dernière à la valeur qui lui correspond à cet instant de l'analyse,
    comme à cet instant var vaut "ccc", la seconde instruction est traduite (interpréter) de cette manière [echo ccc], ainsi, se qui se trouve maintenant en mémoire ce n'est plus ce que nous avions écrit au départ [echo %var%] mais bien [echo ccc].
    Transmission de [echo ccc] pour exécution.
    [conséquence: le Shell exécute la commande «echo ccc» et affiche la chaine "ccc"].

    Ectectera.
    Ectectera.

    Enfin, à la fois pour résumer et simplifier, on dira que les variables à "expansion immédiate" sont étendues (phase pendant laquelle la représentation textuelle de la variable est remplacée par la valeur qui lui correspond en mémoire) à la lecture (en faisant tacitement allusion au préprocesseur de commande) et que les variables à expansion retardée, avec la syntaxe adéquate, son étendue à l'exécution (en faisant allusion bien sûr au processeur de commande).

    Voilà, j'espère que maintenant avec tout ça, le principe de l'expansion retardée des variables d'environnement est une bonne fois pour toute entériné.

    Enjoy;

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    381
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 381
    Points : 231
    Points
    231
    Par défaut
    C'est parfaitement clair !

    Merci beaucoup

  4. #4
    Candidat au Club
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2013
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Argentine

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Septembre 2013
    Messages : 1
    Points : 4
    Points
    4
    Par défaut
    Merci encore. Je viens de lire l'article et tout est bien expliqué.
    Alex

  5. #5
    Membre expert
    Avatar de sachadee
    Homme Profil pro
    AMI DU BAT
    Inscrit en
    Janvier 2013
    Messages
    1 478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Brésil

    Informations professionnelles :
    Activité : AMI DU BAT
    Secteur : Distribution

    Informations forums :
    Inscription : Janvier 2013
    Messages : 1 478
    Points : 3 768
    Points
    3 768
    Par défaut
    +1
    ________________________________
    Un p'tit coup de pouce ça fait toujours plaisir, pensez-y !
    ________________________________

  6. #6
    Nouveau Candidat au Club
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Juin 2015
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juin 2015
    Messages : 1
    Points : 1
    Points
    1
    Par défaut Réponse à maxim_um pour l'epansion retardée de l'interpréteur de commande
    Merci maxim_um pour ses explications ! Ça se lit comme du petit lait, super bien écrit et clair

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

Discussions similaires

  1. pointeurs (explications)
    Par isidore dans le forum C
    Réponses: 4
    Dernier message: 18/04/2003, 10h41
  2. Explication procédure stockée
    Par underworld dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 09/09/2002, 10h51
  3. Recherche code d'un fifo,ou explication
    Par don-diego dans le forum C
    Réponses: 8
    Dernier message: 25/07/2002, 10h26
  4. DirectX 6, un peu en retard ... :\
    Par multani dans le forum DirectX
    Réponses: 3
    Dernier message: 28/05/2002, 19h19
  5. recherches des cours ou des explications sur les algorithmes
    Par Marcus2211 dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 19/05/2002, 22h18

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