web-dev-qa-db-fra.com

Comment le Windows Command Interpreter (CMD.EXE) analyse-t-il les scripts?

J'ai rencontré ss64.com , qui fournit une aide précieuse sur la procédure à suivre pour écrire des scripts de traitement par lots que l'interpréteur de commandes Windows exécutera.

Cependant, je suis incapable de trouver une bonne explication de la grammaire des scripts de traitement par lots, de la manière dont les choses se développent ou ne se développent pas, et de la manière de les échapper.

Voici des exemples de questions que je n'ai pas pu résoudre:

  • Comment le système de devis est-il géré? J'ai fait un script TinyPerl
    (foreach $i (@ARGV) { print '*' . $i ; }), l'a compilé et appelé ainsi:
    • my_script.exe "a ""b"" c" → la sortie est *a "b*c
    • my_script.exe """a b c""" → l'affiche *"a*b*c"
  • Comment fonctionne la commande interne echo? Qu'est-ce qui est développé dans cette commande?
  • Pourquoi dois-je utiliser for [...] %%I Dans les scripts de fichiers, mais for [...] %I Dans les sessions interactives?
  • Quels sont les caractères d'échappement et dans quel contexte? Comment échapper à un signe de pourcentage? Par exemple, comment puis-je faire écho littéralement à %PROCESSOR_ARCHITECTURE%? J'ai trouvé que echo.exe %""PROCESSOR_ARCHITECTURE% Fonctionne, existe-t-il une meilleure solution?
  • Comment les paires de % Correspondent-elles? Exemple:
    • set b=a, echo %a %b% c%%a a c%
    • set a =b, echo %a %b% c%bb c%
  • Comment puis-je m'assurer qu'une variable passe à une commande sous forme d'argument unique si cette variable contient des guillemets doubles?
  • Comment les variables sont-elles stockées lors de l’utilisation de la commande set? Par exemple, si je fais set a=a" b Puis echo.%a%, J'obtiens a" b. Si toutefois j'utilise echo.exe De UnxUtils, j'obtiens a b. Comment se fait-il que %a% Se développe différemment?

Merci pour tes lumières.

130
Benoit

Nous avons effectué des expériences pour étudier la grammaire des scripts batch. Nous avons également étudié les différences entre le mode batch et le mode ligne de commande.

Analyseur de ligne de lot:

Voici un bref aperçu des phases du traitement d’une ligne de code dans un fichier de commandes:

Phase 0) Ligne de lecture:

Phase 1) Pourcentage d'expansion:

Phase 1.5) Supprimer <CR>: Supprimer tous les caractères de retour à la ligne (0x0D)

Phase 2) Traiter les caractères spéciaux, créer des jetons et construire un bloc de commande en cache: Il s'agit d'un processus complexe qui est affecté par des éléments tels que des guillemets, des caractères spéciaux , délimiteurs de jetons et échappements de curseur.

Phase 3) Écho de la ou des commandes analysées uniquement si le bloc de commandes n'a pas commencé par @ Et qu'ECHO était activé à la début de l'étape précédente.

Phase 4) Extension de la variable FOR %X: Uniquement si une commande FOR est active et que les commandes suivantes sont en cours de traitement.

Phase 5) Expansion retardée: Uniquement si l'expansion retardée est activée

Phase 5.3) Traitement du tuyau: Uniquement si les commandes sont situées de part et d'autre d'un tuyau

Phase 5.5) Exécuter la redirection:

Phase 6) Traitement des appels/Doublure de carets: Uniquement si le jeton de commande est CALL

Phase 7) Execute: La commande est exécutée.


Voici les détails pour chaque phase:

Notez que les phases décrites ci-dessous ne sont qu'un modèle du fonctionnement de l'analyseur de lot. Les éléments internes réels de cmd.exe peuvent ne pas refléter ces phases. Mais ce modèle est efficace pour prédire le comportement des scripts batch.

Phase 0) Ligne de lecture: Lisez la ligne d'entrée en commençant par le premier <LF>.

  • Lors de la lecture d'une ligne à analyser en tant que commande, <Ctrl-Z> (0x1A) est lu comme suit: <LF> (LineFeed 0x0A)
  • Lorsque GOTO ou CALL lit des lignes lors de la recherche d’une étiquette: <Ctrl-Z> Est traité comme tel - il est not ​​ converti en <LF>

Phase 1) Pourcentage d'expansion:

  • Un double %% Est remplacé par un simple %
  • Expansion des arguments (%*, %1, %2, Etc.)
  • Expansion de %var%, Si var n'existe pas, ne le remplacez par rien
  • La ligne est tronquée au début <LF> Pas dans l'extension %var%
  • Pour une explication complète, lisez la première moitié de ceci à partir de dbenham Même fil: même pourcentage

Phase 1.5) Supprimer <CR>: Supprimer tous les retours chariot (0x0D) de la ligne.

Phase 2) Traiter les caractères spéciaux, créer des jetons et construire un bloc de commande en cache: Il s'agit d'un processus complexe qui est affecté par des éléments tels que des guillemets, des caractères spéciaux , délimiteurs de jetons et échappements de curseur. Ce qui suit est une approximation de ce processus.

Certains concepts sont importants tout au long de cette phase.

  • Un jeton est simplement une chaîne de caractères traitée comme une unité.
  • Les jetons sont séparés par des délimiteurs de jetons. Les délimiteurs de jetons standard sont <space><tab>;,=<0x0B><0x0C> Et <0xFF>
    Les délimiteurs de jetons consécutifs sont traités comme un seul; il n'y a pas de jetons vides entre les délimiteurs de jetons.
  • Il n'y a pas de délimiteurs de jeton dans une chaîne citée. La chaîne entière citée est toujours traitée comme faisant partie d'un seul jeton. Un seul jeton peut consister en une combinaison de chaînes entre guillemets et de caractères non entre guillemets.

Les caractères suivants peuvent avoir une signification particulière dans cette phase, selon le contexte: ^(@&|<><LF><space><tab>;,=<0x0B><0x0C><0xFF>

Regardez chaque personnage de gauche à droite:

  • S'il s'agit d'un signe d'insertion (^), Le caractère suivant est échappé et le signe d'insertion est supprimé. Les caractères échappés perdent toute signification particulière (à l'exception de <LF>).
  • S'il s'agit d'une citation ("), Activez/désactivez l'indicateur de citation. Si l'indicateur de citation est actif, seuls " Et <LF> Sont spéciaux. Tous les autres caractères perdent leur signification particulière jusqu'à ce que la citation suivante active le drapeau de citation. Il n'est pas possible d'échapper à la citation finale. Tous les caractères cités sont toujours dans le même jeton.
  • <LF> Désactive toujours l'indicateur de citation. D'autres comportements varient en fonction du contexte, mais les guillemets ne modifient jamais le comportement de <LF>.
    • Échappé <LF>
      • <LF> Est dépouillé
      • Le caractère suivant est échappé. Si à la fin de la ligne tampon, la ligne suivante est lue et traitée par les phases 1 et 1.5 et ajoutée à la ligne actuelle avant d'échapper au caractère suivant. Si le caractère suivant est <LF>, Il est traité comme un littéral, ce qui signifie que ce processus n'est pas récursif.
    • Unescaped <LF> Pas entre parenthèses
      • <LF> Est supprimé et l'analyse de la ligne en cours est terminée.
      • Tous les caractères restants dans le tampon de ligne sont simplement ignorés.
    • Unescaped <LF> Dans un bloc FOR IN entre parenthèses
      • <LF> Est converti en un <space>
      • Si à la fin de la mémoire tampon de ligne, la ligne suivante est lue et ajoutée à la ligne courante.
    • Unescaped <LF> Dans un bloc de commande entre parenthèses
      • <LF> Est converti en <LF><space>, Et le <space> Est traité comme faisant partie de la ligne suivante du bloc de commandes.
      • Si à la fin de la ligne tampon, la ligne suivante est lue et ajoutée à l’espace.
  • S'il s'agit de l'un des caractères spéciaux &|< Ou >, Divisez la ligne à ce stade afin de gérer les canaux, la concaténation de commandes et redirection.
    • Dans le cas d'un tuyau (|), Chaque côté est une commande séparée (ou un bloc de commandes) qui obtient un traitement spécial en phase 5.3.
    • Dans le cas de la concaténation de commandes &, && Ou ||, Chaque côté de la concaténation est traité comme une commande distincte.
    • Dans le cas d'une redirection <, <<, > Ou >>, La clause de redirection est analysée, temporairement supprimée, puis ajoutée à la fin de la commande en cours. Une clause de redirection comprend un chiffre de descripteur de fichier facultatif, l'opérateur de redirection et le jeton de destination de la redirection.
      • Si le jeton qui précède l'opérateur de redirection est un chiffre unique, ce dernier spécifie le descripteur de fichier à rediriger. Si le jeton de descripteur n'est pas trouvé, la redirection de sortie par défaut est 1 (stdout) et la redirection d'entrée par défaut est 0 (stdin).
  • Si le tout premier jeton de cette commande (avant de déplacer la redirection à la fin) commence par @, Alors le @ A une signification particulière. (@ N'est pas spécial dans aucun autre contexte)
    • La spéciale @ Est supprimée.
    • Si ECHO est activé, cette commande, ainsi que toutes les commandes concaténées suivantes sur cette ligne, sont exclues de l'écho de la phase 3. Si le @ Est avant une ouverture (, Le bloc entier entre parenthèses est exclu de l'écho de la phase 3.
  • Traiter la parenthèse (fournit des instructions composées sur plusieurs lignes):
    • Si l'analyseur ne recherche pas de jeton de commande, ( N'est pas spécial.
    • Si l'analyseur recherche un jeton de commande et trouve (, Lancez une nouvelle instruction composée et incrémentez le compteur de parenthèses.
    • Si le compteur de parenthèses est> 0, ) Termine l'instruction composée et décrémente le compteur de parenthèses.
    • Si la fin de la ligne est atteinte et que le compteur de parenthèses est> 0, la ligne suivante sera ajoutée à l'instruction composée (recommence avec la phase 0)
    • Si le compteur de parenthèses est 0 et que l'analyseur recherche une commande, alors ) Fonctionne comme une instruction REM à condition qu'elle soit immédiatement suivie d'un délimiteur de jeton, d'un caractère spécial, d'une nouvelle ligne, ou fin de fichier
      • Tous les caractères spéciaux perdent leur signification sauf ^ (La concaténation de ligne est possible)
      • Une fois la fin de la ligne logique atteinte, la totalité de la "commande" est ignorée.
  • Chaque commande est analysée dans une série de jetons. Le premier jeton est toujours traité comme un jeton de commande (après que les @ Spéciaux aient été supprimés et que la redirection soit déplacée à la fin).
    • Les délimiteurs de jetons avant le jeton de commande sont supprimés
    • Lors de l'analyse du jeton de commande, ( Fonctionne comme un délimiteur de jeton de commande, en plus des délimiteurs de jetons standard.
    • Le traitement des jetons suivants dépend de la commande.
  • La plupart des commandes concaténent simplement tous les arguments après le jeton de commande en un jeton à un seul argument. Tous les délimiteurs de jeton d'argument sont conservés. Les options d'argument ne sont généralement pas analysées avant la phase 7.
  • Trois commandes reçoivent un traitement spécial - IF, FOR et REM
    • IF est divisé en deux ou trois parties distinctes traitées indépendamment. Une erreur de syntaxe dans la construction IF entraînera une erreur de syntaxe fatale.
      • L’opération de comparaison est la commande réelle qui va jusqu’à la phase 7
        • Toutes les options IF sont entièrement analysées en phase 2.
        • Les délimiteurs de jetons consécutifs se réduisent en un seul espace.
        • Selon l'opérateur de comparaison, un ou deux jetons de valeur sont identifiés.
      • Le bloc de commandes True est l'ensemble des commandes après la condition et est analysé comme tout autre bloc de commandes. Si ELSE doit être utilisé, le bloc True doit être mis entre parenthèses.
      • Le bloc de commandes optionnel False est l'ensemble des commandes après ELSE. De nouveau, ce bloc de commande est analysé normalement.
      • Les blocs de commandes True et False ne sont pas automatiquement transférés dans les phases suivantes. Leur traitement ultérieur est contrôlé par la phase 7.
    • FOR est divisé en deux après le DO. Une erreur de syntaxe dans la construction FOR entraînera une erreur de syntaxe fatale.
      • La partie passant par DO est la commande réelle d'itération FOR qui traverse la phase 7
        • Toutes les options FOR sont entièrement analysées en phase 2.
        • La clause entre parenthèses IN traite <LF> Comme <space>. Une fois la clause IN analysée, tous les jetons sont concaténés pour former un seul jeton.
        • Les délimiteurs de jetons consécutifs non échappés/non cités se réduisent en un seul espace dans la commande FOR via DO.
      • La partie après DO est un bloc de commande qui est analysé normalement. Le traitement ultérieur du bloc de commande DO est contrôlé par l'itération de la phase 7.
    • Le REM détecté en phase 2 est traité de manière dramatique différente de toutes les autres commandes.
      • Un seul jeton d'argument est analysé - l'analyseur ignore les caractères situés après le premier jeton d'argument.
      • La commande REM peut apparaître dans la sortie de phase 3, mais la commande n'est jamais exécutée et le texte de l'argument d'origine est répercuté - les carets d'échappement ne sont pas supprimés, sauf ...
        • S'il n'y a qu'un seul jeton d'argument qui se termine par un ^ Non échappé qui termine la ligne, le jeton d'argument est alors jeté et la ligne suivante est analysée et ajoutée au REM. Cela se répète jusqu'à ce qu'il y ait plus d'un jeton ou que le dernier caractère ne soit pas ^.
  • Si le jeton de commande commence par : Et qu'il s'agit du premier tour de la phase 2 (pas un redémarrage en raison de CALL en phase 6), alors
    • Le jeton est normalement traité comme un Libellé non exécuté.
      • Le reste de la ligne est analysé, cependant ), <, >, & Et | N'ont plus de signification particulière. Le reste de la ligne est considéré comme faisant partie de l’étiquette "commande".
      • Le ^ Continue d'être spécial, ce qui signifie que la continuation de ligne peut être utilisée pour ajouter la ligne suivante à l'étiquette.
      • Une étiquette non exécutée dans un bloc entre parenthèses entraînera une erreur de syntaxe fatale sauf si elle est immédiatement suivie d'une commande ou de étiquette exécutée sur la ligne suivante.
        • ( N'a plus de signification particulière pour la première commande suivant le Libellé non exécuté.
      • La commande est abandonnée une fois l'analyse de l'étiquette terminée. Les phases suivantes n'ont pas lieu pour l'étiquette
    • Il existe trois exceptions qui peuvent entraîner le traitement d'une étiquette trouvée dans la phase 2 comme une étiquette exécutée qui poursuit l'analyse jusqu'à la phase 7.
      • Il y a une redirection qui précède le jeton d'étiquette et il y a une concaténation de commande | Ou &, && Ou || Sur la ligne.
      • Une redirection précède le jeton d'étiquette et la commande se trouve dans un bloc entre parenthèses.
      • Le jeton d'étiquette est la toute première commande sur une ligne d'un bloc entre parenthèses, et la ligne ci-dessus s'est terminée par un Etiquette non-exécutée.
    • Les événements suivants se produisent lorsqu'un Etiquette exécutée est découvert en phase 2
      • L'étiquette, ses arguments et sa redirection sont exclus de toute sortie d'écho en phase 3
      • Toutes les commandes concaténées ultérieures sur la ligne sont entièrement analysées et exécutées.
    • Pour plus d'informations sur les Etiquettes exécutées vs. Etiquettes non exécutées, voir https://www.dostips.com/forum/viewtopic.php?f=3&t= 3803 & p = 55405 # p55405

Phase 3) Écho de la ou des commandes analysées uniquement si le bloc de commandes n'a pas commencé par @ Et qu'ECHO était activé à la début de l'étape précédente.

Phase 4) Extension de la variable FOR %X: Uniquement si une commande FOR est active et que les commandes suivantes sont en cours de traitement.

  • À ce stade, la phase 1 du traitement par lots aura déjà converti une variable FOR telle que %%X En %X. La ligne de commande a différentes règles d'expansion en pourcentage pour la phase 1. C'est la raison pour laquelle les lignes de commande utilisent %X Mais les fichiers de traitement par lots utilisent %%X Pour les variables FOR.
  • Les noms de variables FOR sont sensibles à la casse, mais ~modifiers Ne le sont pas.
  • ~modifiers A priorité sur les noms de variables. Si un caractère suivant ~ Est à la fois un modificateur et un nom de variable FOR valide, et s'il existe un caractère ultérieur qui est un nom de variable FOR actif, il est interprété comme un modificateur.
  • Les noms de variables FOR sont globaux, mais uniquement dans le contexte d'une clause DO. Si une routine est appelée à partir d'une clause FOR DO, les variables FOR ne sont pas développées dans la routine CALLed. Mais si la routine a sa propre commande FOR, alors all les variables FOR actuellement définies sont accessibles aux commandes internes DO.
  • Les noms de variables FOR peuvent être réutilisés dans des FOR imbriqués. La valeur FOR interne a priorité, mais une fois que INNER FOR se ferme, la valeur FOR externe est restaurée.
  • Si ECHO était activé au début de cette phase, la phase 3) est répétée pour afficher les commandes DO analysées une fois les variables FOR développées.

---- À partir de ce moment, chaque commande identifiée lors de la phase 2 est traitée séparément.
---- Les phases 5 à 7 sont terminées pour une commande avant de passer à la suivante.

Phase 5) Expansion retardée: Uniquement si l'expansion retardée est activée, la commande n'est pas dans un bloc entre parenthèses de part et d'autre d'un tuya , et la commande n'est pas un script de lot "naked" (nom de script sans parenthèses, CALL, concaténation de commandes ou canal).

  • Chaque jeton d'une commande est analysé séparément pour une expansion retardée.
    • La plupart des commandes analysent deux ou plusieurs jetons - le jeton de commande, le jeton d'arguments et chaque jeton de destination de redirection.
    • La commande FOR analyse uniquement le jeton de la clause IN.
    • La commande IF analyse uniquement les valeurs de comparaison, une ou deux, en fonction de l'opérateur de comparaison.
  • Pour chaque jeton analysé, vérifiez d’abord s'il contient un !. Sinon, le jeton n'est pas analysé - important pour les caractères ^. Si le jeton contient !, Scannez chaque caractère de gauche à droite:
    • S'il s'agit d'un curseur (^), Le caractère suivant n'a pas de signification particulière, le curseur est supprimé.
    • S'il s'agit d'un point d'exclamation, recherchez le prochain point d'exclamation (les carets ne sont plus observés), développez la valeur de la variable.
      • Les ouvertures consécutives ! Sont réduites en un seul !
      • Tout ! Non apparié restant est supprimé
    • L'expansion de vars à ce stade est "sûre", car les caractères spéciaux ne sont plus détectés (même <CR> Ou <LF>)
    • Pour une explication plus complète, lisez la deuxième moitié de ceci dans dbenham même fil - Phase d'exclamation

Phase 5.3) Traitement du tuyau: Uniquement si les commandes sont situées de part et d'autre d'un tuyau
Chaque côté du tuyau est traité indépendamment et de manière asynchrone.

  • Si la commande est interne à cmd.exe ou s'il s'agit d'un fichier de commandes ou d'un bloc de commande entre parenthèses, elle est exécutée dans un nouveau thread cmd.exe via %comspec% /S /D /c" commandBlock", De sorte que le bloc de commande reçoit un phase restart, mais cette fois en mode ligne de commande.
    • Si un bloc de commande entre parenthèses est bloqué, tous les <LF> Avec une commande avant et après sont convertis en <space>&. Les autres <LF> Sont supprimés.
  • C'est la fin du traitement des commandes de canal.
  • Voir Pourquoi l'expansion retardée échoue-t-elle à l'intérieur d'un bloc de code transmis? pour plus d'informations sur l'analyse et le traitement des canaux

Phase 5.5) Exécuter la redirection: Toute redirection découverte en phase 2 est maintenant exécutée.

Phase 6) Traitement des appels/Doublure de curseur: Uniquement si le jeton de commande est CALL ou si le texte précédant le premier délimiteur de jetons standard est CALL. Si CALL est analysé à partir d'un jeton de commande plus volumineux, la portion inutilisée est ajoutée au jeton arguments avant de continuer.

  • Analysez le jeton d'arguments pour rechercher un /? Sans guillemets. Si vous en trouvez un dans les jetons, annulez la phase 6 et passez à la phase 7, où l’aide HELP for CALL sera imprimée.
  • Supprimez le premier CALL, afin que plusieurs appels puissent être empilés
  • Double tous les carets
  • Redémarrez les phases 1, 1.5 et 2, mais ne passez pas à la phase 3
    • Tous les carets doublés sont réduits à un caret tant qu'ils ne sont pas cités. Mais malheureusement, les carets cités restent doublés.
    • La phase 1 change un peu
      • Les erreurs d'expansion à l'étape 1.2 ou 1.3 annulent l'appel, mais l'erreur n'est pas fatale - le traitement par lots se poursuit.
    • Les tâches de la phase 2 sont légèrement modifiées
      • Toute nouvelle redirection non précisée, non échappée et non détectée lors du premier tour de la phase 2 est détectée, mais elle est supprimée (y compris le nom du fichier) sans effectuer la redirection.
      • Tout curseur inséré non cité et non échappé apparaissant à la fin de la ligne est supprimé sans effectuer la continuation de la ligne.
      • L’appel est annulé sans erreur si l’un des problèmes suivants est détecté
        • Nouvellement apparaissant non cité, non échappé & Ou |
        • Le jeton de commande résultant commence par non cité, non échappé (
        • Le tout premier jeton après l'appel supprimé a commencé par @
      • Si la commande résultante est un IF ou FOR apparemment valide, l'exécution échouera par la suite avec une erreur indiquant que IF ou FOR n'est pas reconnu en tant que commande interne ou externe.
      • Bien entendu, l’APPEL n’est pas annulé lors de ce 2e tour de la phase 2 si le jeton de commande résultant est une étiquette commençant par :.
  • Si le jeton de commande résultant est CALL, redémarrez la phase 6 (répète jusqu'à ce qu'il n'y ait plus d'appels)
  • Si le jeton de commande résultant est un script de traitement par lots ou un libellé:, l'exécution de l'appel est alors entièrement traitée par le reste de la phase 6.
    • Poussez la position actuelle du fichier de script de traitement par lots sur la pile d'appels afin que l'exécution puisse reprendre à partir de la position correcte une fois l'appel terminé.
    • Configurez les jetons d'argument% 0,% 1,% 2, ...% N et% * pour l'appel, en utilisant tous les jetons résultants.
    • Si le jeton de commande est une étiquette commençant par :, Alors
      • Redémarrez la phase 5. Cela peut avoir une incidence sur quoi: l'étiquette est appelée. Mais comme les jetons% 0 etc. ont déjà été configurés, les arguments transmis à la routine CALLed ne seront pas modifiés.
      • Exécutez le libellé GOTO pour positionner le pointeur de fichier au début du sous-programme (ignorez tous les autres jetons pouvant suivre le libellé). Consultez la phase 7 pour connaître les règles de fonctionnement de GOTO.
        • Si le jeton: label est manquant ou si le libellé: label est introuvable, la pile d'appels est immédiatement supprimée pour restaurer la position du fichier enregistré et l'appel est annulé.
        • Si l'étiquette: contient /?, L'aide de GOTO est imprimée au lieu de rechercher l'étiquette:. Le pointeur de fichier ne se déplace pas, de sorte que le code après l'appel soit exécuté deux fois, une fois dans le contexte d'appel, puis à nouveau après le retour de l'appel. Voir Pourquoi CALL imprime le message d'aide GOTO dans ce script? Et pourquoi les commandes suivantes sont exécutées deux fois? pour plus d'informations.
    • Sinon, transférez le contrôle vers le script de lot spécifié.
    • L’exécution de l’appel CALLed: label ou script continue jusqu’à atteindre EXIT/B ou la fin du fichier. La pile CALL est alors supprimée et l’exécution reprend à partir de la position du fichier enregistré.
      La phase 7 n'est pas exécutée pour les scripts CALLed ou les libellés.
  • Sinon, le résultat de la phase 6 tombe dans la phase 7 pour exécution.

Phase 7) Execute: La commande est exécutée.

  • 7.1 - Exécuter une commande interne - Si le jeton de commande est entre guillemets, ignorez cette étape. Sinon, essayez d’analyser une commande interne et d’exécuter.
    • Les tests suivants sont effectués pour déterminer si un jeton de commande non entre guillemets représente une commande interne:
      • Si le jeton de commande correspond exactement à une commande interne, exécutez-la.
      • Sinon, casser le jeton de commande avant la première occurrence de +/[]<space><tab>,; Ou =
        Si le texte précédent est une commande interne, alors rappelez-vous cette commande
        • Si vous êtes en mode ligne de commande ou si la commande provient d'un bloc entre parenthèses, d'un bloc de commande IF vrai ou faux, d'un bloc de commande FOR DO ou de la concaténation de commandes, exécutez la commande interne.
        • Sinon (il doit s'agir d'une commande autonome en mode de traitement par lots), recherchez dans un dossier .COM, .EXE, .BAT ou .CMD le dossier en cours et le chemin PATH dont le nom de base correspond au jeton de commande d'origine .
          • Si le premier fichier correspondant est un fichier .BAT ou .CMD, passez à 7.3.exec et exécutez ce script.
          • Sinon (correspondance non trouvée ou la première correspondance est .EXE ou .COM) exécuter la commande interne mémorisée
      • Sinon, casser le jeton de commande avant la première occurrence de .\ Ou :
        Si le texte précédent n'est pas une commande interne, passez à 7.2.
        Sinon, le texte précédent peut être une commande interne. Rappelez-vous cette commande.
      • Casser le jeton de commande avant la première occurrence de +/[]<space><tab>,; Ou =
        Si le texte précédent est un chemin d'accès à un fichier existant, passez à 7.2.
        Sinon, exécutez la commande interne mémorisée.
    • Si une commande interne est analysée à partir d'un jeton de commande plus volumineux, la partie inutilisée du jeton de commande est incluse dans la liste des arguments.
    • Le simple fait qu'un jeton de commande soit analysé comme une commande interne ne signifie pas qu'il s'exécutera correctement. Chaque commande interne a ses propres règles sur la manière dont les arguments et les options sont analysés et sur la syntaxe autorisée.
    • Toutes les commandes internes afficheront de l'aide au lieu de remplir leur fonction si /? Est détecté. La plupart reconnaissent /? S'il apparaît n'importe où dans les arguments. Toutefois, quelques commandes telles que ECHO et SET n'impriment l'aide que si le premier jeton d'argument commence par /?.
    • SET a une sémantique intéressante:
      • Si une commande SET a un guillemet avant que le nom de la variable et les extensions soient activés
        set "name=content" ignored -> valeur = content
        alors le texte entre le premier signe égal et le dernier guillemet est utilisé comme contenu (le premier guillemet égal et le dernier guillemet sont exclus). Le texte après le dernier devis est ignoré. S'il n'y a pas de guillemet après le signe égal, le reste de la ligne est utilisé comme contenu.
      • Si une commande SET n'a pas de guillemet avant le nom
        set name="content" not ignored -> valeur = "content" not ignored
        alors le reste de la ligne après l'égalité est utilisé comme contenu, y compris les guillemets éventuellement présents.
    • Une comparaison IF est évaluée et, selon que la condition est vraie ou fausse, le bloc de commande dépendant déjà analysé approprié est traité à partir de la phase 5.
    • La clause IN d'une commande FOR est itérée de manière appropriée.
      • S'il s'agit d'un FOR/F qui itère la sortie d'un bloc de commande, alors:
        • La clause IN est exécutée dans un nouveau processus cmd.exe via CMD/C.
        • Le bloc de commandes doit passer par le processus d'analyse complet une seconde fois, mais cette fois dans un contexte de ligne de commande
        • ECHO démarrera et l'activation retardée sera généralement désactivée (en fonction du paramétrage du registre)
        • Toutes les modifications d'environnement apportées par le bloc de commandes de la clause IN seront perdues une fois le processus enfant cmd.exe terminé.
      • Pour chaque itération:
        • Les valeurs de la variable FOR sont définies
        • Le bloc de commande DO déjà analysé est ensuite traité en commençant par la phase 4.
    • GOTO utilise la logique suivante pour localiser le: label
      • L'étiquette est analysée à partir du premier jeton d'argument
      • Le script est analysé pour rechercher l'occurrence suivante de l'étiquette
        • L'analyse commence à partir de la position actuelle du fichier
        • Si la fin du fichier est atteinte, l'analyse retourne au début du fichier et continue jusqu'au point de départ d'origine.
      • L'analyse s'arrête à la première occurrence de l'étiquette trouvée et le pointeur de fichier est défini sur la ligne qui suit immédiatement l'étiquette. L'exécution du script reprend à partir de ce moment. Notez qu'un vrai GOTO réussi annulera immédiatement tout bloc de code analysé, y compris les boucles FOR.
      • Si l'étiquette n'est pas trouvée ou si le jeton d'étiquette est manquant, alors le GOTO échoue, un message d'erreur est imprimé et la pile d'appels est éjectée. Cela fonctionne efficacement comme un EXIT/B, sauf que les commandes déjà analysées dans le bloc de commandes en cours qui suivent le GOTO sont toujours exécutées, mais dans le contexte de l'appelant (le contexte qui existe après EXIT/B)
      • Voir https://www.dostips.com/forum/viewtopic.php?f=3&t=38 pour une description plus précise des règles utilisées pour l'analyse des étiquettes.
    • RENAME et COPY acceptent tous les deux des caractères génériques pour les chemins source et cible. Mais Microsoft fait un travail épouvantable en documentant le fonctionnement des caractères génériques, en particulier pour le chemin cible. Vous trouverez un ensemble utile de règles relatives aux caractères génériques à l'adresse suivante: Comment la commande Windows RENAME interprète-t-elle les caractères génériques?
  • 7.2 - Exécuter le changement de volume - Sinon, si le jeton de commande ne commence pas par un guillemet, comporte exactement deux caractères et que le deuxième caractère est un signe deux-points. puis changez le volume
    • Tous les jetons d'argument sont ignorés
    • Si le volume spécifié par le premier caractère est introuvable, annulez avec une erreur.
    • Un jeton de commande de :: Entraînera toujours une erreur, sauf si SUBST est utilisé pour définir un volume pour ::
      Si vous utilisez SUBST pour définir un volume pour ::, Le volume sera modifié, il ne sera pas traité comme une étiquette.
  • 7.3 - Exécuter une commande externe - Sinon essayez de traiter la commande comme une commande externe.
    • Si vous êtes en mode ligne de commande et que la commande n'est pas citée et qu'elle ne commence pas par une spécification de volume, coupez le jeton de commande à la première occurrence de <space>,; Ou = Et ajoutez le reste au (x) jeton (s) d’argument.
    • Si le deuxième caractère du jeton de commande est un signe deux-points, vérifiez que le volume spécifié par le premier caractère peut être trouvé.
      Si le volume est introuvable, annulez avec une erreur.
    • Si en mode de traitement par lots et que le jeton de commande commence par :, Passez à 7.4
      Notez que si le jeton d'étiquette commence par ::, Cela ne sera pas atteint car l'étape précédente sera abandonnée avec une erreur sauf si SUBST est utilisé pour définir un volume pour ::.
    • Identifiez la commande externe à exécuter.
      • Il s'agit d'un processus complexe pouvant impliquer le volume actuel, le répertoire actuel, la variable PATH, la variable PATHEXT et/ou les associations de fichiers.
      • Si une commande externe valide ne peut pas être identifiée, annulez avec une erreur.
    • Si vous êtes en mode ligne de commande et que le jeton de commande commence par :, Alors passez à 7.4
      Notez que ceci est rarement atteint car l'étape précédente aura été annulée avec une erreur sauf si le jeton de commande commence par ::, Et SUBST est utilisé pour définir un volume pour ::, et le jeton de commande entier est un chemin valide pour une commande externe.
    • 7.3.exec - Exécute la commande externe.
  • 7.4 - Ignorer une étiquette - Ignore la commande et tous ses arguments si le jeton de commande commence par :.
    Les règles de 7.2 et 7.3 peuvent empêcher une étiquette d’atteindre ce point.

Analyseur de ligne de commande:

Fonctionne comme le BatchLine-Parser, sauf:

Phase 1) Pourcentage d'expansion:

  • Aucune extension d'argument %*, %1 Etc.
  • Si var n'est pas défini, alors %var% Reste inchangé.
  • Pas de traitement spécial de %%. Si var = content, alors %%var%% Devient %content%.

Phase 3) Écho de la ou des commandes analysées

  • Ceci n'est pas effectué après la phase 2. Il est uniquement effectué après la phase 4 pour le bloc de commande FOR DO.

Phase 5) Expansion retardée: uniquement si RetardedExpansion est activé

  • Si var n'est pas défini, alors !var! Reste inchangé.

Phase 7) Execute Command

  • Les tentatives CALL ou GOTO a: label génèrent une erreur.
  • Comme indiqué dans la phase 7, une étiquette exécutée peut entraîner une erreur dans différents scénarios.
    • Les étiquettes exécutées par lots ne peuvent provoquer une erreur que si elles commencent par ::
    • Les étiquettes exécutées en ligne de commande entraînent presque toujours une erreur

Analyse de valeurs entières

Il existe de nombreux contextes différents dans lesquels cmd.exe analyse les valeurs entières des chaînes et les règles sont incohérentes:

  • SET /A
  • IF
  • %var:~n,m% (Expansion de sous-chaîne variable)
  • FOR /F "TOKENS=n"
  • FOR /F "SKIP=n"
  • FOR /L %%A in (n1 n2 n3)
  • EXIT [/B] n

Les détails de ces règles peuvent être trouvés à Règles pour la façon dont CMD.EXE analyse les nombres


Pour ceux qui souhaitent améliorer les règles d'analyse de cmd.exe, il existe un sujet de discussion sur le forum DosTips où les problèmes peuvent être signalés et les suggestions formulées.

J'espère que ça aide
Jan Erik (jeb) - Auteur original et découvreur des phases
Dave Benham (dbenham) - Beaucoup de contenu et d'édition supplémentaires

173
jeb

Lors de l'appel d'une commande à partir d'une fenêtre de commande, la syntaxe des arguments de la ligne de commande n'est pas effectuée par cmd.exe (a.k.a. "le shell"). Le plus souvent, la création de jetons est effectuée par le runtime C/C++ des processus nouvellement formés, mais ce n'est pas nécessairement le cas - par exemple, si le nouveau processus n'a pas été écrit en C/C++ ou si le nouveau processus choisit d'ignorer argv et traite la ligne de commande brute pour elle-même (par exemple avec GetCommandLine () ). Au niveau du système d'exploitation, Windows transmet les lignes de commande non considérées comme une chaîne unique aux nouveaux processus. Ceci contraste avec la plupart des * shells nix, où le shell marque les arguments de manière cohérente et prévisible avant de les transmettre au processus nouvellement formé. Tout cela signifie que vous pouvez rencontrer un comportement très différent en ce qui concerne la numérotation des arguments entre les différents programmes de Windows, car chaque programme prend souvent les choses en main.

Si cela ressemble à de l'anarchie, c'est en quelque sorte. Cependant, puisqu'un grand nombre de programmes Windows utilisent le argv du runtime de Microsoft C/C++, il peut être généralement utile de comprendre - comment le MSVCRT tokenize arguments. Voici un extrait:

  • Les arguments sont délimités par des espaces, qui sont soit des espaces, soit des tabulations.
  • Une chaîne entourée de guillemets doubles est interprétée comme un seul argument, quel que soit l'espace blanc qu'il contient. Une chaîne entre guillemets peut être incorporée dans un argument. Notez que le signe d'insertion (^) n'est pas reconnu comme un caractère d'échappement ou un délimiteur.
  • Un guillemet double précédé d'une barre oblique inversée, "" est interprété comme un guillemet double littéral (").
  • Les barres obliques inverses sont interprétées littéralement, sauf si elles précèdent immédiatement un guillemet double.
  • Si un nombre pair de barres obliques inverses est suivi d'un guillemet double, une barre oblique inversée () est placée dans le tableau argv pour chaque paire de barres obliques inverses (\) et le guillemet double (") est interprété comme un séparateur de chaîne.
  • Si un nombre impair de barres obliques inverses est suivi d'un guillemet double, une barre oblique inversée () est placée dans le tableau argv pour chaque paire de barres obliques inverses (\) et le guillemet double est interprété comme une séquence d'échappement par la barre oblique restante, ce qui provoque un double guillemet (") à placer dans argv.

Le "langage par lots" de Microsoft (.bat) ne fait pas exception à cet environnement anarchique et a développé ses propres règles uniques pour la création de jetons et l’échappement. Il semble également que l'invite de commande de cmd.exe effectue un prétraitement de l'argument de ligne de commande (principalement pour la substitution et l'échappement de variable) avant de transmettre l'argument au processus en cours d'exécution. Vous pouvez en savoir plus sur les détails de bas niveau de la langue de traitement par lots et de l'échappement de cmd dans les excellentes réponses de Jeb et Dbenham sur cette page.


Construisons un utilitaire de ligne de commande simple en C et voyons ce qu'il dit à propos de vos cas de test:

int main(int argc, char* argv[]) {
    int i;
    for (i = 0; i < argc; i++) {
        printf("argv[%d][%s]\n", i, argv[i]);
    }
    return 0;
}

(Remarques: argv [0] est toujours le nom de l'exécutable et est omis ci-dessous par souci de brièveté. Testé sous Windows XP SP3. Compilé avec Visual Studio 2005.)

> test.exe "a ""b"" c"
argv[1][a "b" c]

> test.exe """a b c"""
argv[1]["a b c"]

> test.exe "a"" b c
argv[1][a" b c]

Et quelques uns de mes propres tests:

> test.exe a "b" c
argv[1][a]
argv[2][b]
argv[3][c]

> test.exe a "b c" "d e
argv[1][a]
argv[2][b c]
argv[3][d e]

> test.exe a \"b\" c
argv[1][a]
argv[2]["b"]
argv[3][c]
60
Mike Clark

Règles d'expansion en pourcentage

Voici une explication détaillée de la phase 1 dans réponse de jeb (valide pour le mode de traitement par lots et le mode de ligne de commande).

Phase 1) Pourcentage d'expansion En commençant par la gauche, scannez chaque caractère pour % Ou <LF>. Si trouvé alors

  • 1.05 (tronquer la ligne à <LF>)
    • Si le caractère est <LF>, Alors
      • Supprimer (ignorer) le reste de la ligne à partir de <LF>
      • Aller à la phase 1.5 (Strip <CR>)
    • Sinon, le caractère doit être %, Passez donc à 1.1
  • 1.1 (escape %) ignoré si mode de ligne de commande
    • Si le mode de traitement par lots et suivi d'un autre % Alors
      Remplacez %% Par le simple % Et continuez le scan
  • 1.2 (argument développé) ignoré si mode de ligne de commande
    • Sinon si mode batch, alors
      • Si suivi de * Et que les extensions de commande sont activées, alors
        Remplacez %* Par le texte de tous les arguments de la ligne de commande (Remplacez par rien s'il n'y a pas d'argument) et continuez le scan.
      • Sinon si suivi de <digit> Puis
        Remplacez %<digit> Par la valeur de l'argument (remplacez par rien si non défini) et continuez l'analyse.
      • Sinon, suivi de ~ Et les extensions de commande sont activées, puis
        • Si suivi d'une liste optionnelle de modificateurs d'arguments valide, suivie de <digit> Requis, alors
          Remplacez %~[modifiers]<digit> Par une valeur d'argument modifiée (remplacez-la par rien si non défini ou si spécifié, $ PATH: modificateur non défini) et poursuivez l'analyse.
          Remarque: les modificateurs ne sont pas sensibles à la casse et peuvent apparaître plusieurs fois dans n'importe quel ordre, à l'exception de $ PATH: le modificateur ne peut apparaître qu'une seule fois et doit être le dernier modificateur avant le <digit>
        • Sinon, la syntaxe d'argument modifié non valide est levée erreur fatale: toutes les commandes analysées sont abandonnées et le traitement par lots est annulé en mode de traitement par lots !
  • 1.3 (variable de développement)
    • Sinon si les extensions de commande sont désactivées alors
      Regardez la chaîne de caractères suivante, avant le % Ou la fin du tampon, et appelez-les VAR (peut-être une liste vide)
      • Si le caractère suivant est %, Alors
        • Si VAR est défini alors
          Remplacez %VAR% Par la valeur de VAR et continuez le scan
        • Sinon si le mode batch alors
          Supprimez %VAR% Et continuez le scan
        • Sinon goto 1.4
      • Sinon goto 1.4
    • Sinon si les extensions de commande sont activées alors
      Regardez la chaîne de caractères suivante, précédée de %: Ou de la fin de la mémoire tampon, et appelez-les VAR (cette liste peut être vide). Si VAR se sépare avant : Et que le caractère suivant est %, Incluez : Comme dernier caractère de VAR et séparez-le avant %.
      • Si le caractère suivant est %, Alors
        • Si VAR est défini alors
          Remplacez %VAR% Par la valeur de VAR et continuez le scan
        • Sinon si le mode batch alors
          Supprimez %VAR% Et continuez le scan
        • Sinon goto 1.4
      • Sinon si le caractère suivant est :, Alors
        • Si VAR n'est pas défini, alors
          • Si le mode batch, alors
            Supprimez %VAR: Et continuez le scan.
          • Sinon goto 1.4
        • Sinon si le caractère suivant est ~, Alors
          • Si la chaîne de caractères suivante correspond au motif de [integer][,[integer]]%, Alors
            Remplacez %VAR:~[integer][,[integer]]% Par une sous-chaîne de la valeur de VAR (pouvant entraîner une chaîne vide) et continuez l'analyse.
          • Sinon goto 1.4
        • Sinon si suivi de = Ou *= Puis
          La syntaxe de recherche et de remplacement de variable non valide est générée erreur fatale: toutes les commandes analysées sont abandonnées et le traitement par lots est annulé en mode de traitement par lots !
        • Sinon, si la chaîne de caractères suivante correspond au modèle de [*]search=[replace]%, Où la recherche peut inclure tout jeu de caractères sauf =, Et replace peut inclure tout jeu de caractères sauf %, Puis remplacez
          %VAR:[*]search=[replace]% Avec la valeur VAR après avoir effectué une recherche et un remplacement (ce qui peut entraîner une chaîne vide) et poursuivre l'analyse
        • Sinon goto 1.4
  • 1,4 (% bande)
    • Sinon si le mode batch, alors
      Supprimez % Et poursuivez l'analyse en commençant par le caractère suivant après le %
    • Sinon, conservez % Et continuez votre analyse en commençant par le caractère suivant après le %

Ce qui précède aide à expliquer pourquoi ce lot

@echo off
setlocal enableDelayedExpansion
set "1var=varA"
set "~f1var=varB"
call :test "arg1"
exit /b  
::
:test "arg1"
echo %%1var%% = %1var%
echo ^^^!1var^^^! = !1var!
echo --------
echo %%~f1var%% = %~f1var%
echo ^^^!~f1var^^^! = !~f1var!
exit /b

Donne ces résultats:

%1var% = "arg1"var
!1var! = varA
--------
%~f1var% = P:\arg1var
!~f1var! = varB

Note 1 - La phase 1 a lieu avant la reconnaissance des instructions REM. Ceci est très important car cela signifie qu'une remarque peut générer une erreur fatale si sa syntaxe de développement d'argument n'est pas valide ou si la syntaxe de recherche et de remplacement de variable n'est pas valide!

@echo off
rem %~x This generates a fatal argument expansion error
echo this line is never reached

Note 2 - Autre conséquence intéressante des règles d’analyse%: Les variables contenant: dans le nom peuvent être définies, mais ils ne peuvent être développés que si les extensions de commande sont désactivées. Il existe une exception: un nom de variable contenant un simple deux-points à la fin peut être développé lorsque les extensions de commande sont activées. Cependant, vous ne pouvez pas effectuer d'opération de sous-chaîne ou de recherche et remplacement sur des noms de variables se terminant par un signe deux-points. Le fichier de commandes ci-dessous (courtoisie de jeb) illustre ce problème.

@echo off
setlocal
set var=content
set var:=Special
set var::=double colon
set var:~0,2=tricky
set var::~0,2=unfortunate
echo %var%
echo %var:%
echo %var::%
echo %var:~0,2%
echo %var::~0,2%
echo Now with DisableExtensions
setlocal DisableExtensions
echo %var%
echo %var:%
echo %var::%
echo %var:~0,2%
echo %var::~0,2%

Note 3 - Un résultat intéressant de l'ordre des règles d'analyse que jeb énonce dans son message: Lors de l'exécution rechercher et remplacer avec l’extension normale, les caractères spéciaux ne doivent PAS être échappés (bien qu’ils puissent être cités). Mais lors de la recherche et du remplacement avec expansion retardée, les caractères spéciaux DOIVENT être évités (à moins qu’ils ne soient cités).

@echo off
setlocal enableDelayedExpansion
set "var=this & that"
echo %var:&=and%
echo "%var:&=and%"
echo !var:^&=and!
echo "!var:&=and!"

Règles d'expansion retardée

Voici une explication plus détaillée et plus précise de la phase 5 en réponse de jeb (Valable pour le mode de traitement par lots et le mode de ligne de commande)

Phase 5) Expansion retardée

Cette phase est ignorée si l’une des conditions suivantes est remplie:

  • L'expansion retardée est désactivée.
  • La commande se trouve dans un bloc entre parenthèses de part et d'autre d'un tuyau.
  • Le jeton de commande entrant est un script de lot "nu", ce qui signifie qu'il n'est pas associé à CALL, un bloc entre parenthèses, à toute forme de concaténation de commande (&, && Ou ||) Ou un tuyau |.

Le processus d'expansion retardée est appliqué aux jetons indépendamment. Une commande peut avoir plusieurs jetons:

  • Le jeton de commande. Pour la plupart des commandes, le nom de la commande est un jeton. Mais quelques commandes ont des régions spécialisées qui sont considérées comme un JETON pour la phase 5.
    • for ... in(TOKEN) do
    • if defined TOKEN
    • if exists TOKEN
    • if errorlevel TOKEN
    • if cmdextversion TOKEN
    • if TOKEN comparison TOKEN, Où comparaison est l'un de ==, equ, neq, lss, leq, gtr ou geq
  • Le jeton d'arguments
  • Le jeton de destination de la redirection (un par redirection)

Aucune modification n'est apportée aux jetons qui ne contiennent pas !.

Pour chaque jeton qui contient au moins un !, Scannez chaque caractère de gauche à droite pour trouver ^ Ou !. S'il est trouvé,

  • 5.1 (caractère d'échappement) nécessaire pour les littéraux ! Ou ^
    • Si le caractère est un signe ^, Alors
      • Supprimer le ^
      • Scannez le caractère suivant et conservez-le en tant que littéral
      • Continuer le scan
  • 5.2 (variable de développement)
    • Si le caractère est !, Alors
      • Si les extensions de commande sont désactivées,
        Regardez la chaîne de caractères suivante, précédée de ! Ou <LF>, Et appelez-les VAR (peut-être une liste vide)
        • Si le caractère suivant est !, Alors
          • Si VAR est défini, alors
            Remplacez !VAR! Par la valeur de VAR et continuez le scan
          • Sinon si le mode batch alors
            Supprimez !VAR! Et continuez le scan
          • Sinon goto 5.2.1
        • Sinon goto 5.2.1
      • Sinon si les extensions de commande sont activées alors
        Regardez la chaîne de caractères suivante, précédée de !, : Ou <LF>, Et appelez-les VAR (cette liste peut être vide). Si VAR se sépare avant : Et que le caractère suivant est !, Incluez : Comme dernier caractère du VAR et coupez avant !
        • Si le caractère suivant est !, Alors
          • Si VAR existe, alors
            Remplacez !VAR! Par la valeur de VAR et continuez le scan
          • Sinon si le mode batch alors
            Supprimez !VAR! Et continuez le scan
          • Sinon goto 5.2.1
        • Sinon si le caractère suivant est :, Alors
          • Si VAR n'est pas défini, alors
            • Si le mode batch, alors
              Supprimez !VAR: Et continuez le scan
            • Sinon goto 5.2.1
          • Sinon si la chaîne de caractères suivante correspond au motif de
            ~[integer][,[integer]]! Alors
            Remplacez !VAR:~[integer][,[integer]]! Par une sous-chaîne de la valeur de VAR (résultant éventuellement en une chaîne vide) et continuez l'analyse
          • Sinon, si la chaîne de caractères suivante correspond au modèle de [*]search=[replace]!, Où la recherche peut inclure n’importe quel jeu de caractères sauf = Et remplacer peut inclure tout jeu de caractères sauf !,
            Remplacez !VAR:[*]search=[replace]! Par la valeur de VAR après une recherche et un remplacement (ce qui peut entraîner une chaîne vide) et continuez l'analyse
          • Sinon goto 5.2.1
        • Sinon goto 5.2.1
      • 5.2.1
        • Si le mode par lots, supprimez le !
          Sinon, conservez le !
        • Continuez le balayage en commençant par le caractère suivant après le !
44
dbenham

Comme indiqué, les commandes reçoivent la totalité de la chaîne d'arguments dans μSoft land, et il leur appartient de les analyser en arguments séparés pour leur propre utilisation. Il n’ya pas de cohérence à cet égard entre différents programmes et, par conséquent, il n’existe pas un ensemble unique de règles pour décrire ce processus. Vous devez vraiment vérifier chaque cas de coin pour la bibliothèque C que votre programme utilise.

En ce qui concerne le système .bat fichiers vont, voici ce test:

c> type args.cmd
@echo off
echo cmdcmdline:[%cmdcmdline%]
echo 0:[%0]
echo *:[%*]
set allargs=%*
if not defined allargs goto :eof
setlocal
@rem Wot about a Nice for loop?
@rem Then we are in the land of delayedexpansion, !n!, call, etc.
@rem Plays havoc with args like %t%, a"b etc. ugh!
set n=1
:loop
    echo %n%:[%1]
    set /a n+=1
    shift
    set param=%1
    if defined param goto :loop
endlocal

Maintenant, nous pouvons exécuter des tests. Voyez si vous pouvez comprendre ce que μSoft essaie de faire:

C>args a b c
cmdcmdline:[cmd.exe ]
0:[args]
*:[a b c]
1:[a]
2:[b]
3:[c]

Bien jusqu'à présent. (Je laisserai de côté l'inintéressant %cmdcmdline% et %0 à partir de maintenant.)

C>args *.*
*:[*.*]
1:[*.*]

Aucune extension de nom de fichier.

C>args "a b" c
*:["a b" c]
1:["a b"]
2:[c]

Pas de suppression des guillemets, bien que les guillemets empêchent la division des arguments.

c>args ""a b" c
*:[""a b" c]
1:[""a]
2:[b" c]

Les doubles guillemets consécutifs leur font perdre toutes leurs capacités d'analyse particulières. @ L'exemple de Beniot:

C>args "a """ b "" c"""
*:["a """ b "" c"""]
1:["a """]
2:[b]
3:[""]
4:[c"""]

Quiz: Comment passez-vous la valeur de n'importe quel environnement var en tant qu'argument nique (c'est-à-dire, en tant que %1) dans un fichier chauve-souris?

c>set t=a "b c
c>set t
t=a "b c
c>args %t%
1:[a]
2:["b c]
c>args "%t%"
1:["a "b]
2:[c"]
c>Aaaaaargh!

Une analyse syntaxique semble à jamais brisée.

Pour votre divertissement, essayez d’ajouter divers ^, \, ', & (etc.) caractères à ces exemples.

7
bobbogo

Vous avez déjà d'excellentes réponses ci-dessus, mais pour répondre à une partie de votre question:

set a =b, echo %a %b% c% → bb c%

Qu'est-ce qui se passe là-bas est que parce que vous avez un espace avant le =, une variable est créée appelée %a<space>% alors quand vous echo %a % qui est évalué correctement comme b.

La partie restante b% c% est ensuite évalué comme texte brut + une variable non définie % c%, qui devrait être repris comme il se doit, pour moi echo %a %b% c% résultats bb% c%

Je soupçonne que la possibilité d'inclure des espaces dans les noms de variables est davantage un oubli qu'une "fonctionnalité" planifiée

5
ss64

edit: voir réponse acceptée, ce qui suit est faux et explique uniquement comment passer une ligne de commande à TinyPerl.


En ce qui concerne les citations, j’ai le sentiment que le comportement est le suivant:

  • lorsqu'un " est trouvé, la chaîne commence
  • quand survient une chaîne:
    • chaque personnage qui n'est pas un " est globbed
    • lorsqu'un " est trouvé:
      • si elle est suivie de "" _ (donc un triple ") alors un guillemet double est ajouté à la chaîne
      • si elle est suivie de " _ (donc un double "), une citation double est ajoutée à la chaîne et la chaîne se termine
      • si le caractère suivant n'est pas ", la fin de la chaîne
    • lorsque la ligne se termine, la chaîne se termine.

En bref:

"a """ b "" c""" se compose de deux chaînes: a " b " et c"

"a"", "a""" et"a"""" sont tous la même chaîne si à la fin d'une ligne

0
Benoit