Modification d’un fichier de commandes lors de son exécution

J’exécute un long fichier batch en cours d’exécution. Je me rends compte maintenant que je dois append quelques commandes supplémentaires à la fin du fichier de commandes (pas de modification du contenu existant, juste quelques commandes supplémentaires). Est-il possible de le faire, étant donné que la plupart des fichiers de commandes sont lus de manière incrémentielle et exécutés un par un? Ou est-ce que le système lit tout le contenu du fichier et exécute ensuite le travail?

Je viens de l’essayer, et contre mon intuition, il a pris les nouvelles commandes à la fin (sous Windows XP)

J’ai créé un fichier batch contenant

echo Hello pause echo world 

J’ai couru le fichier, et pendant qu’il était en pause, ajouté

 echo Salute 

Enregistrez-le et appuyez sur Entrée pour continuer la pause, les trois invites ont été répercutées sur la console.

Alors, allez-y!

L’interpréteur de commandes se souvient de la position de la ligne dans le fichier de commandes. Tant que vous modifiez le fichier de commandes après la position de la ligne en cours d’exécution, tout ira bien.

Si vous le modifiez avant, il commencera à faire des choses étranges (répétition des commandes, etc.).

L’exemple de jeb est très amusant, mais cela dépend beaucoup de la longueur du texte ajouté ou supprimé. Je pense que les résultats contre-intuitifs sont ce que cela signifiait quand il a dit “Si vous le modifiez avant, il commencera à faire des choses étranges (répétition des commandes, etc.)”.

J’ai modifié le code de jeb pour montrer comment un code dynamic de longueur variable peut être librement modifié au début d’un fichier de commandes en cours d’exécution tant que le remplissage approprié est en place. Toute la section dynamic est complètement remplacée à chaque itération. Chaque ligne dynamic est préfixée d’un non interférant ; . Cela permet commodément à FOR /F de supprimer le code dynamic à cause de l’ EOL=; implicite EOL=; option.

Au lieu de rechercher un numéro de ligne particulier, je recherche un commentaire spécifique pour localiser le début du code dynamic. C’est plus facile à maintenir.

J’utilise des lignes de signes égaux pour protéger le code de manière inoffensive afin de permettre l’expansion et la contraction. Toute combinaison des caractères suivants peut être utilisée: virgule, point-virgule, égal, espace, tabulation et / ou nouvelle ligne. (Bien sûr, le remplissage ne peut pas commencer par un point-virgule.) Les signes égaux dans les parenthèses permettent l’expansion du code. Les signes égaux après les parenthèses permettent la contraction du code.

Notez que FOR /F supprime les lignes vides. Cette limitation peut être surmontée en utilisant FINDSTR pour préfixer chaque ligne avec le numéro de ligne, puis enlevez le préfixe dans la boucle. Mais le code supplémentaire ralentit les choses, il ne vaut donc pas la peine, sauf si le code dépend de lignes vides.

 @echo off setlocal DisableDelayedExpansion echo The starting filesize is %~z0 :loop echo ---------------------- ::*** Start of dynamic code *** ;set value=1 ::*** End of dynamic code *** echo The current value=%value% :: ::The 2 lines of equal signs amount to 164 bytes, including end of line chars. ::Putting the lines both within and after the parentheses allows for expansion ::or contraction by up to 164 bytes within the dynamic section of code. ( call :changeBatch ============================================================================== ============================================================================== ) ================================================================================ ================================================================================ set /p "quit=Enter Q to quit, anything else to continue: " if /i "%quit%"=="Q" exit /b goto :loop :changeBatch ( for /f "usebackq delims=" %%a in ("%~f0") do ( echo %%a if "%%a"=="::*** Start of dynamic code ***" ( setlocal enableDelayedExpansion set /a newValue=value+1, extra=!random!%%9 echo ;set value=!newValue! for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n endlocal ) ) ) >"%~f0.tmp" :: ::The 2 lines of equal signs amount to 164 bytes, including end of line chars. ::Putting the lines both within and after the parentheses allows for expansion ::or contraction by up to 164 bytes within the dynamic section of code. ( move /y "%~f0.tmp" "%~f0" > nul ============================================================================== ============================================================================== ) ================================================================================ ================================================================================ echo The new filesize is %~z0 exit /b 

Ce qui précède fonctionne, mais les choses sont beaucoup plus simples si le code dynamic est déplacé vers une sous-routine à la fin du fichier. Le code peut se développer et se contracter sans limitation et sans besoin de remplissage. FINDSTR est beaucoup plus rapide que FOR / F pour supprimer la partie dynamic. Les lignes dynamics peuvent être préfixées avec un point-virgule (y compris les étiquettes!). Ensuite, l’option FINDSTR / V est utilisée pour exclure les lignes qui commencent par un point-virgule et le nouveau code dynamic peut simplement être ajouté.

 @echo off setlocal DisableDelayedExpansion echo The starting filesize is %~z0 :loop echo ---------------------- call :dynamicCode1 call :dynamicCode2 echo The current value=%value% call :changeBatch set /p "quit=Enter Q to quit, anything else to continue: " if /i "%quit%"=="Q" exit /b goto :loop :changeBatch ( findstr /v "^;" "%~f0" setlocal enableDelayedExpansion set /a newValue=value+1, extra=!random!%%9 echo ;:dynamicCode1 echo ;set value=!newValue! echo ;exit /b echo ; echo ;:dynamicCode2 for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n echo ;exit /b endlocal ) >"%~f0.tmp" move /y "%~f0.tmp" "%~f0" > nul echo The new filesize is %~z0 exit /b ;:dynamicCode1 ;set value=33 ;exit /b ; ;:dynamicCode2 ;echo extra line 1 ;exit /b 

Presque comme l’a dit rein , cmd.exe se souvient de la position du fichier (pas seulement de la position de la ligne), mais aussi de la position du fichier sur une stack invisible.

Cela signifie que vous pouvez éditer votre fichier pendant qu’il est en cours d’exécution et avant la position du fichier, vous n’avez besoin que de ce que vous faites …

Un petit échantillon d’un lot auto-modifiable
Il change la set value=1000 ligne set value=1000 continu

 @echo off setlocal DisableDelayedExpansion :loop REM **** the next line will be changed set value=1000 rem *** echo ---------------------- echo The current value=%value%  nul echo( ( call :changeBatch rem This should be here and it should be long ) rem ** It is neccessary, that this is also here! goto :loop rem ... :changeBatch set /an=0 set /a newValue=value+1 set /a toggle=value %% 2 set "theNewLine=set value=%newValue%" if %toggle%==0 ( set "theNewLine=%theNewLine% & rem This adds 50 byte to the filesize.........." ) del "%~f0.tmp" 2> nul for /F "usebackq delims=" %%a in ("%~f0") DO ( set /a n+=1 set "line=%%a" setlocal EnableDelayedExpansion if !n!==5 ( (echo !theNewLine!) ) ELSE ( (echo !line!) ) endlocal ) >> "%~f0.tmp" ( rem the copy should be done in a parenthesis block copy "%~f0.tmp" "%~f0" > nul if Armageddon==TheEndOfDays ( echo This can't never be true, or is it? ) ) echo The first line after the replace action.... echo The second line comes always after the first line? echo The current filesize is now %~z0 goto :eof 

Réponse courte: oui, les fichiers batch peuvent se modifier en cours d’exécution. Comme d’autres l’ont déjà confirmé.

Il y a des années et des années, avant Windows 3, l’endroit où je travaillais avait un système de menus interne sous MS-DOS. La façon dont les choses tournaient était assez élégante: en fait, à partir d’un fichier de commandes, le programme principal (écrit en C) a été modifié pour exécuter des scripts. Cette astuce signifiait que le programme de menu lui-même ne prenait pas d’espace mémoire pendant que les sélections étaient en cours. Et cela incluait des choses comme le programme LAN Mail et le programme terminal 3270.

Mais exécuter un fichier de commandes auto-modifiables signifiait que ses scripts pouvaient aussi faire des choses comme les programmes TSR de chargement et, en fait, faire pratiquement tout ce que vous pouviez mettre dans un fichier de commandes. Ce qui l’a rendu très puissant. Seule la commande GOTO ne fonctionnait pas, jusqu’à ce que l’auteur finisse par comprendre comment faire redémarrer le fichier de commandes pour chaque commande.

L’interpréteur de commandes semble se souvenir du décalage d’octet dans chaque fichier de commandes qu’il lit, mais le fichier lui-même n’est pas verrouillé. Il est donc possible de faire des modifications, par exemple avec un éditeur de texte, pendant son exécution.

Si une modification est apscope au fichier après cet emplacement mémorisé, l’interpréteur devrait continuer à exécuter le script maintenant modifié. Cependant, si la modification est effectuée avant ce point et que cette modification modifie la longueur du texte à ce stade (par exemple, vous avez inséré ou supprimé du texte), cet emplacement mémorisé ne fait plus référence au début de la prochaine commande. . Lorsque l’interpréteur essaie de lire la ligne suivante, il choisit plutôt une ligne différente, ou éventuellement une ligne, en fonction de la quantité de texte insérée ou supprimée. Si vous avez de la chance, il ne sera probablement pas en mesure de traiter le mot qui lui arrive, de donner une erreur et de continuer à exécuter à partir de la ligne suivante – mais probablement pas ce que vous voulez.

Cependant, avec la compréhension de ce qui se passe, vous pouvez structurer vos scripts pour réduire les risques. J’ai des scripts qui implémentent un système de menu simple, en affichant un menu, en acceptant les entrées de l’utilisateur en utilisant la commande de choice et en traitant ensuite la sélection. L’astuce consiste à veiller à ce que le point d’attente du script se trouve en haut du fichier, de sorte que toutes les modifications que vous souhaiteriez effectuer interviennent après ce moment et ne présentent donc aucun impact négatif.

Exemple:

 :top call :displayMenu :prompt REM The script will spend most of its time waiting here. choice /C:1234 /N "Enter selection: " if ERRORLEVEL == 4 goto DoOption4 if ERRORLEVEL == 3 goto DoOption3 if ERRORLEVEL == 2 goto DoOption2 goto DoOption1 :displayMenu (many lines to display menu) goto prompt :DoOption1 (many lines to do Option 1) goto top :DoOption2 (many lines to do Option 2) goto top (etc)