Script batch “Droplet” – noms de fichiers contenant des esperluettes

J’essaie de créer un fichier de commandes pouvant contenir d’autres fichiers. Plus précisément, j’utilise ffmpeg pour éditer des fichiers audio produits par un enregistreur vocal portable. Le problème est lorsque vous utilisez des noms de fichiers avec des esperluettes (&). Même en citant l’entrée, tout ce qui est après le & est supprimé, mais uniquement lorsque des fichiers y sont déposés; Si le nom du fichier est saisi sur la ligne de commande, le script fonctionne correctement. Avant la fermeture de la fenêtre cmd , je vois brièvement le rest du nom de fichier avec une erreur indiquant qu’il n’est pas reconnu comme une commande valide.

Voici mon script:

 rem Changer de lecteur et répertoire du fichier d'entrée
 % ~ d1
 cd% ~ p1

 rem ffmpeg: mixez sur un canal, doublez le volume
 % HOMEDRIVE %% HOMEPATH% \ ffmpeg.exe -i "% ~ nx1" -ac 1 -vol 1024 "% ~ n1 fixe% ~ x1"

 pause

Voici ce qui apparaît sur la ligne de commande, après avoir "ch17&18.mp3" :

 C: \ Users \ computergeeksjw \ Desktop> C: \ Utilisateurs \ computergeeksjw \ ffmpeg.exe -i "ch17" -ac 1 -vol 1024 "ch17 corrigé"
 [...]
 ch17: Pas un tel fichier ou répertoire

Au cas où cela importerait: J’utilise l’aperçu du développeur Windows 8. Est-ce que cela cause mon problème? La même erreur se produit-elle sous Windows 7 ou antérieur?

Il existe un bogue de longue date dans la fonctionnalité de glisser-déposer de Windows concernant les chemins de fichiers contenant & ou ^ mais ne contenant pas de .

Si un chemin de fichier contient au moins un , Windows inclut automatiquement le chemin entre guillemets afin qu’il soit analysé correctement. Windows devrait faire la même chose si le chemin du fichier contient & ou ^ , mais ce n’est pas le cas.

Si vous créez le fichier de commandes simple suivant et faites glisser des fichiers dessus, vous pouvez voir le problème.

 @echo off setlocal enableDelayedExpansion echo cmd=!cmdcmdline! echo %%1="%~1" pause exit 

Le! Cmdcmdline! La variable contient la commande proprement dite qui a lancé le fichier de commandes. Le fichier de commandes affiche la ligne de commande et le premier paramètre.

Si vous faites glisser un fichier nommé “a.txt”, vous obtenez

 cmd=cmd /c ""C:\test\drag.bat" C:\test\a.txt" %1=C:\test\a.txt Press any key to continue . . . 

Si vous ignorez les guillemets autour de la commande entière, vous voyez qu’il n’y a pas de guillemets autour de l’argument de fichier. Il n’y a pas de caractères spéciaux, donc il n’y a pas de problème.

Maintenant, faites glisser et déposez “un b.txt” et vous obtenez

 cmd=cmd /c ""C:\test\drag.bat" "C:\test\a b.txt"" %1="C:\test\a b.txt" Press any key to continue . . . 

Vous pouvez voir comment Windows détecte l’espace dans le nom et place le fichier entre guillemets. Encore une fois, il n’y a pas de problème.

Maintenant, faites glisser et déposez “a & b.txt” et vous obtenez

 cmd=cmd /c ""C:\test\drag.bat" C:\test\a&b.txt" %1=C:\test\a Press any key to continue . . . 

Windows ne trouve pas d’espace dans le nom, il ne le met donc pas entre guillemets. Gros problème! Windows transmet “C: \ test \ a” au fichier de commandes et traite “b.txt” comme un second fichier à exécuter après la fin du fichier de commandes. La commande EXIT dans le fichier de commandes empêche l’exécution des noms de fichiers après le traitement par lots. Bien sûr, b.txt ne pourra jamais s’exécuter. Mais si le fichier était nommé “a & b.bat” et que “b.bat” existait, cela pourrait poser problème si EXIT dur n’était pas dans le fichier de commandes.

Il est possible de faire glisser plusieurs fichiers dans un fichier de commandes, et chacun doit être passé en paramètre.

Le! Cmdcmdline! est le seul moyen d’accéder de manière fiable aux arguments de glisser-déposer. Mais cela ne fonctionnera pas si les fichiers sont passés en argument normal dans un appel normal au fichier de commandes.

Vous trouverez ci-dessous un fichier de commandes qui peut détecter s’il a été appelé par glisser-déposer par rapport à un appel normal. (Ce n’est pas une preuve par balle, mais je pense que cela devrait fonctionner dans la plupart des situations). Il traitera chaque argument de fichier, un à la fois, quel que soit le type d’appel. (Le processus fait simplement écho au nom du fichier, mais vous pouvez le remplacer par le traitement de votre choix.) Si le lot a été appelé par glisser-déposer, il effectuera une sortie difficile pour se protéger contre les noms de fichiers fractionnés.

 @echo off setlocal disableDelayedExpansion :: :: first assume normal call, get args from %* set args=%* set "dragDrop=" :: :: Now check if drag&drop situation by looking for %0 in !cmdcmdline! :: if found then set drag&drop flag and get args from !cmdcmdline! setlocal enableDelayedExpansion set "cmd=!cmdcmdline!" set "cmd2=!cmd:*%~f0=!" if "!cmd2!" neq "!cmd!" ( set dragDrop=1 set "args=!cmd2:~0,-1! " set "args=!args:* =!" ) :: :: Process the args for %%F in (!args!) do ( if "!!"=="" endlocal & set "dragDrop=%dragDrop%" rem ------------------------------------------------ rem - Your file processing starts here. rem - Each file will be processed one at a time rem - The file path will be in %%F rem - echo Process file "%%~F" rem - rem - Your file processing ends here rem ------------------------------------------------- ) :: :: If drag&drop then must do a hard exit to prevent unwanted execution :: of any split drag&drop filename argument if defined dragDrop ( pause exit ) 

Il semble que votre lot existant ne soit conçu que pour gérer un seul fichier. Je ne peux pas dire si vous devez apporter des modifications aux appels pour prendre en charge plusieurs fichiers. J’ai modifié le lot ci-dessus pour ne traiter que le premier argument et substitué votre processus à la boucle de traitement des arguments. Ce n’est pas testé, mais je pense que cela devrait fonctionner pour vous.

 @echo off setlocal disableDelayedExpansion :: :: first assume normal call, get args from %* set args=%* set "dragDrop=" :: :: Now check if drag&drop situation by looking for %0 in !cmdcmdline! :: if found then set drag&drop flag and get args from !cmdcmdline! setlocal enableDelayedExpansion set "cmd=!cmdcmdline!" set "cmd2=!cmd:*%~f0=!" if "!cmd2!" neq "!cmd!" ( set dragDrop=1 set "args=!cmd2:~0,-1! " set "args=!args:* =!" ) :: :: Process the first argument only for %%F in (!args!) do ( if "!!"=="" endlocal & set "dragDrop=%dragDrop%" rem ------------------------------------------------ rem - Your file processing starts here. rem - Use %%F wherever you would normally use %1 rem rem Change to drive and directory of input file %%~dF cd %%~pF rem ffmpeg: mix to one channel, double the volume %HOMEDRIVE%%HOMEPATH%\ffmpeg.exe -i "%%~nxF" -ac 1 -vol 1024 "%%~nF fixed%%~xF" rem rem - Your file processing ends here rem ------------------------------------------------- goto :continue ) :continue if defined dragDrop ( pause exit ) 

J’admire les compétences en programmation par lots de dbenham dans la crainte silencieuse. J’ai essayé sa solution et suis tombé sur deux problèmes que je présente ici car je n’ai pas assez de réputation pour commenter:

  1. Il semble y avoir un espace supplémentaire devant le dernier guillemet sur la ligne 15 de son modèle de lot. Je suppose qu’il devrait lire:

      set "args=!cmd2:~0,-1!" 

    Quelqu’un avec une connaissance de la programmation par lots pas si stellaire pourrait avoir de sérieux problèmes pour trouver cela, comme moi. J’ai essayé mais je n’ai pas pu éditer le message de dbenham à cause de la stupidité “Les modifications doivent comporter au moins 6 caractères”.

  2. La solution ne convient généralement pas aux fichiers / dossiers contenant , (virgule) ou ; (point-virgule) dans leur chemin complet. Il peut être modifié pour fonctionner dans le cas où un seul fichier / dossier est déposé sur un fichier de commandes en plaçant args entre guillemets sur la ligne 20:

     for %%F in ("!args!") do ( 

    Lorsque plusieurs fichiers / dossiers sont déposés sur un fichier de commandes, je crains qu’aucune solution de contournement générale du bogue Windows ne soit possible avec une virgule / point-virgule dans le chemin du fichier. Le mécanisme SendTo de Windows a évidemment le même défaut (bug), et ne peut donc pas être utilisé pour contourner le bug du glisser-déposer. Il appartient donc à Microsoft de résoudre ce problème.