Drop folder script - file names containing ampersands

I am trying to create a batch file that may contain other files. In particular, I use ffmpeg to edit audio files created by a manual recorder. The problem is using file names with ampersands (&). Even when quoting input, something after is uploaded, but only when files fall on it; if a file name is entered on the command line, the script works fine. Before closing the cmd window, I briefly see the rest of the file name with an error, saying that it is not recognized as a valid command.

Here is my script:

  rem Change to drive and directory of input file
 % ~ d1
 cd% ~ p1

 rem ffmpeg: mix to one channel, double the volume
 % HOMEDRIVE %% HOMEPATH% \ ffmpeg.exe -i "% ~ nx1" -ac 1 -vol 1024 "% ~ n1 fixed% ~ x1"

 pause

Here, what appears on the command line after removing "ch17&18.mp3" :

  C: \ Users \ computergeeksjw \ Desktop> C: \ Users \ computergeeksjw \ ffmpeg.exe -i "ch17" -ac 1 -vol 1024 "ch17 fixed"
 [...]
 ch17: No such file or directory

In case that matters: I am using Windows 8 Developer Preview. Is this the cause of my problem? Did the same error happen in Windows 7 or earlier?

+7
source share
2 answers

There is a long-standing error in the drag and drop function of Windows regarding file paths containing & or ^ but not containing <space> .

If the file path contains at least one <space> , then Windows automatically encloses the path in quotation marks so that it is parsed correctly. Windows should do the same if the file path contains & or ^ , but it doesnโ€™t.

If you create the following simple batch file and drag and drop files onto it, you may see the problem.

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

The! cmdcmdline! the variable contains the actual command that launched the batch file. The batch file prints the command line and the first parameter.

If you drag a file named "a.txt", you will get

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

If you ignore quotes around the entire command, you see that there are no quotes in the file argument. There are no special characters, so no problem.

Now drag "b.txt" and you get

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

You can see how Windows detects a space in the name and encloses the file in quotation marks. Again no problem.

Now drag "a & b.txt" and you will get

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

Windows does not find a space in the name, so it does not enclose it in quotation marks. A big problem! Windows passes "C: \ test \ a" to the batch file and treats "b.txt" as the second file that will be executed after the batch file completes. The hard EXIT command in the batch file prevents any split file names from being executed after the batch. Of course, b.txt will never be able to execute. But if the file was named "a & b.bat" and "b.bat", then this can be a problem if the hard EXIT was not in the batch file.

You can drag and drop multiple files into a batch file, and each of them should be passed as a parameter.

The! cmdcmdline! is the only way to reliably access drag and drop arguments. But this will not work if the files are transferred as normal arguments during a normal call to a batch file.

The following is a batch file that can determine if it was called using drag and drop compared to a regular call. (This is not bulletproof, but I think it should work in most situations). It will process each file argument, one at a time, regardless of the type of call. (The process simply renames the file name, but you can replace any necessary processing.) If the package is called using drag and drop, then it will make a hard exit to protect against split file names.

 @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 ) 

It looks like your existing batch is only for processing a single file. I canโ€™t say if you need to make changes to calls to support multiple files. I modified the above batch to handle only the first argument, and replaced your process with an argument processing loop. This is not tested, but I think it should work for you.

 @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 ) 
+14
source

I admire dbenham's batch programming skills in quiet fear. I tried its solution and stumbled upon two problems that I present here, as I do not have enough reputation to comment:

  • There seems to be extra space in front of the last quote in line 15 of his batch template. I suppose he should read:

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

    Someone who doesnโ€™t have stellar programming can have serious problems finding this, just like me. I tried, but could not edit the dbenham post due to the silly restriction "Editing must be at least 6 characters."

  • The solution is usually not suitable for files / folders containing , (comma) or ; (semicolon) in their full path. It can be changed to work if only one file fell into a batch file, including arguments in quotation marks on line 20:

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

    When more / less file / folder is dropped into a batch file, I am afraid that there is no workaround for a Windows error that could handle the semicolon / semicolon in the file path. The SendTo mechanism for Windows obviously has the same drawback (error), so it cannot be used to bypass a drag and drop error. Therefore, Microsoft can permanently fix this error.

+1
source

Source: https://habr.com/ru/post/903992/


All Articles