I regularly have to rename hundreds of files across a subfolder structure. I have been creating a batch file consisting of all my rename commands, and manually pasting this into each subfolder to execute one subfolder at a time. I'd like to revise the batch script so that it executes against all subfolders in one fell swoop, run from the parent directory just once.
My renaming is very manual, and so I need to create a discrete entry for each file. For example, here are three lines:
REN STWP01_00669087* BCBSRI-01849351*
REN BCBSRI-01849357* 2011-12-19_BCBSRI-01849357*
REN STWP01_00669094* BCBSRI-01849369*
I've experimented with the FOR /R command, including trying a separate batch file that calls my renaming batch file (via the CALL command). No luck.
I have to assume that this is simple, but I'm a batch novice, so any help would be appreciated.
Thanks!
#Magoo,
Thanks so much for your response. Your approach is going to be far more efficient than my own so far.
A couple of questions. Please bear with me as I am a total novice with batch commands.
Here's what I did: I saved your code to a .BAT file ("RRename.bat"), modified my filenames as per your instructions and saved those to a text file ("Filenames.txt"), and then run this command from the command line: {RRename.bat Filenames.txt}.
The resulting command windows confirm correct renaming. And so I removed the ECHO and PAUSE commands and re-ran. No luck. Just a bunch of Command windows confirming the directory.
Ideally I'd love to save this as a .BAT file and simply drop this in the top-level directory, together with the data file that contains the old names and new names of the files. And so, a double-click of "RRename.bat" will parse the content of "Filenames.txt" and work its way through all subfolders, renaming wherever matches are encountered. Boom.
To that end:
1. How do I make it so {SET "sourcedir=} indicates the current directory (i.e. the directory in which the batch file is located)? This way I wouldn't ever need to change this variable. (I should note that I am running this script on a network location, which requires me to map the drive, resulting in a different drive letter every time.)
2. How do I hard-code the name of the data file into the script itself? My goal is an easily replicated process minimizing user input (save for the content of the data file).
3. How do I stop the individual command windows from appearing? I'll be renaming thousands of files at a time and don't want to see thousands fo corresponding command windows.
Thank you!
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: read parameters
SET "filename1=%~1"
SET "filename2=%~2"
IF DEFINED filename2 GOTO name
IF NOT DEFINED filename1 GOTO :EOF
:: 1 parameter - must be filename
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
GOTO :eof
:: we have 2 parameters so rename pattern 1 to pattern 2
:name
FOR /r "%sourcedir%" %%a IN ("%filename1%*") DO CALL :process "%%a"
PAUSE
GOTO :EOF
:: Process the filenames and actually do the rename
:process
:: Name of file to be changed - name and extension of %1
SET "changeme=%~nx1"
:: REPLACE up-to-from-pattern with nothing = remainder of name/extension
CALL SET "endpart=%%changeme:*%filename1%=%%"
:: and RENAME...
ECHO(REN "%~1" "%filename2%%endpart%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
Revised data file
STWP01_00669087 BCBSRI-01849351
BCBSRI-01849357 2011-12-19_BCBSRI-01849357
STWP01_00669094 BCBSRI-01849369
Aimed at processing the above file, renaming files starting (column1 entries) to start (column2 entries.)
Method:
Run the batch as
batchname filename
This will execute the batch, processing filename
How:
having set the directory name to start processing from, set filename1&2 to the values of the parameters supplied.
If only 1 is supplied, it is the filename, so process it line-by-line and START a new process /min minimised "with the window name in the first set of quotes" and execute this same batch with the data from each line of the file in turn, then finish by going to :eof (end-of-file - built-in to CMD)
The sub-processes all have 2 parameters (eg BCBSRI-01849357 2011-12-19_BCBSRI-01849357) so processing passes to :name. This runs a for /r loop, from the specified source directory, with the name specified from the first column+* and executes :process passing the filenames found as parameter 1.
:process sets changeme to the filename in question, calculates endpart by removing the string filename1 from changeme which will deliver the er, end part.
Then simply rename the supplied filename to the replacement name+that endpart calculated.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
The PAUSE is just to allow the proposed changes to be seen. Once the process has been verified, change the PAUSE to EXIT.
AAMOI, running
*batchname* STWP01_00669094 BCBSRI-01849369
for instance, would execute the recursive-rename from STWP01_00669094* to BCBSRI-01849369*
Sadly, "No luck" is meaningless.
I have made a minor, but significant change to the instructions. The PAUSE should be changed to an EXIT after testing.
After testing, the ECHO(... line should become
REN "%~1" "%filename2%%endpart%"
which actually executes the rename. If you've just deleted the line, it would explain the no-visible-result.
Having restored the original code and verified against a small representative dummy subtree, change the echo(... line and test again. The filenames should change. If not, something is dreadfully wrong. Needless to say, this works perfectly happily for me...
Then try again with the PAUSE changed to EXIT. This time, the windows generated will appear on the taskbar and then disappear when the rename for that line of the input file has finished. This will happen once for BCBSRI-01849357 rentwo for instance - not once for each individual file rename occurring.
To hard-code the filename, remove the line
IF NOT DEFINED filename1 GOTO :EOF
and replace
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
with
FOR /f "usebackqdelims=" %%a IN ("YOURFILENAMEHERE") DO START /min "ren %%a" "%~f0" %%a
For the "run from here" command, change
SET "sourcedir=U:\sourcedir"
to
SET "sourcedir=."
. means "the current directory"
If you place thisbatchfilename.bat into any directory on your PATH then you can run the routine simply by executing thisbatchfilename.
You can display your path by typing
path
at the prompt. PATH is the sequence of directories searched by windows to find an executable if it isn't found in the current directory. To chane path, google "change path windows" - experienced batchers create a separate directory on the path for batch files. Sometimes, they name the directory "Belfry".
Related
I'm using the following batch code to convert all files in a certain directory if the target file doesn't already exist however I'm stuck at getting this to run through every submap and file within that (and keep the output relative with that submap)
So I currently use this:
for %%f in (input/textures/*.*) do ( IF NOT EXIST "ouput/textures/%%~nf.dds" (
"bin/ThempImageParser.exe" "input/textures/%%f" "ouput/textures/%%~nf.dds"
)
)
This works perfectly for a single folder (as was intended), it takes all the files in that specific folder, and passes them as arguments to my executable, which then outputs the file on the path of the second argument.
However this also contains a flaw (this is an additional problem though..) as it does not work if the output -folder- does not exist, so if possible I'd also want it to create the folder if need be.
I've found some batch documentation (I really don't have much experience with Batch) showing me a command called FORFILES and the /R parameter, however I couldn't adjust this so it'd keep the relative paths for the output too, it'd require string manipulation and I have no clue on how to do that.
So the result I'm after is something like this, it takes any file deeper than "input/textures/ for example:
input/textures/some/very/deep/submap/why/does/it/go/on/myfile.anyExtension
it should then take that file (and relative path) and basically change "input" with "output" and replace the file extension with .dds like this:
ouput/textures/some/very/deep/submap/why/does/it/go/on/myfile.dds
and pass those two strings to my executable.
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir\wherever\something"
FOR /f "delims=" %%a IN ('xcopy /y /L /s "%sourcedir%\*"') DO (
SET "destfile=%%a"
SET "destfile=!destfile:*%sourcedir%=%destdir%!"
IF /i "%%a" neq "!destfile!" (
FOR %%m IN ("!destfile!") DO IF NOT EXIST "%%~dpm%%~na.dds" (
ECHO MD "%%~dpm"
ECHO "bin\ThempImageParser.exe" "%%a" "%%~dpm%%~na.dds"
)
)
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
First, perform an xcopy with the /L option to list-only the individual fullnames of files that would be copied by the xcopy.
Assign each name found from %%a to destfile, then remove all characters before the source-directoryname from that filename, and replace that string with the destination directoryname.
This will yield the destination name for the file (with the original extension). The only exception will be the very last output line, which is a count-of-files report. Since this line will not contain the source directoryname, the replacement will not take place, so %%a will be the same as !destfile! - so we eliminate that.
Now assign the destination filename to a metavariable so we can select its various parts, and if the filename made from the destination drive and pathname, the name part of the original file and .dds does not exist, then make the destination directoryname and execute the imageparser, providing the desired output filename.
Note that these last two are ECHOed instead of being executed for testing purposes. Remove the ECHOes to actually perform the command.
Note that / is a switch-indicator, \ is a directory-separator.
Note that MD will report an error if the directory already exists. Append 2>nul to the end of the md command to suppress that error message.
I already tried the answer given by Mofi in my last question regarding this topic. But I changed the base name and it does not seem to work by now. If you want to see the previous question:How do I increment a filename in batch? What is wrong with this new code? It does not make a new file it just overwrites the previous made file.
:MainProcessNew
cd /D "%USERPROFILE%\Desktop"
for /F %%G in (*.json) do (
set "FileName=%%G"
set "BaseName=Device"
set "FileNumber=0"
)
:FileNameLoop
set /A FileNumber+=1
if exist "%BaseName%%FileNumber%.json" (
goto FileNameLoop
)
echo.>"%USERPROFILE%\Desktop\%BaseName%%FileNumber%.json"
I'm quite sure the batch code in question is not complete or reduced to a script code not really suitable to reproduce the problem because with Device.json existing in folder Desktop and no other Device*.json file existing, the empty line is first written to Device1.json. A file with name Device.json is never overwritten by the batch code in question because variable FileNumber has always at least the value 1.
Well, the FOR option /F is most likely wrong here as I suppose the FOR loop should search for *.json files as done without /F. Using a wildcard pattern like *.json together with option/F results in error message:
The system cannot find the file *.json.
Run in a command prompt window for /? or help for for help on syntax of this command.
It is completely unclear what is the purpose of the FOR loop because the FileName variable is not used at all. This variable should perhaps hold the name of last found *.json if there was any *.json file found at all. But that also does not make sense if not further used anywhere.
It is also unclear why BaseName and FileNumber are defined inside the loop and not outside.
In the complete batch code the label FileNameLoop is perhaps the beginning of a subroutine. But in the reduced batch code in question there is no call :FileNameLoop "%%G" which I would expect in this case.
So the question is hard to answer as it is unclear what is really the problem with posted batch code.
:MainProcessNew
cd /D "%USERPROFILE%\Desktop"
rem Useless FOR loop without /F commented out.
rem for %%G in (*.json) do set "FileName=%%G"
set "BaseName=Device"
set "FileNumber="
rem Skip searching for files with a file number
rem after Device if there is no Device.json file.
if not exist "%BaseName%.json" goto CreateFile
rem Otherwise keep Device.json as is and search for Device1.json,
rem Device2.json, ... until a Device*.json file with current number
rem is not found and use this number for next Device*.json file.
set "FileNumber=0"
:FileNameLoop
set /A FileNumber+=1
if exist "%BaseName%%FileNumber%.json" goto FileNameLoop
:CreateFile
rem Note: FileNumber is replaced in the line below by an empty
rem string if there is no Device.json in desktop folder.
echo.>"%USERPROFILE%\Desktop\%BaseName%%FileNumber%.json"
Hint: For debugging a batch file
comment out or remove all #echo off or echo off in the batch file or change off to on,
open a command prompt window,
enter "Path to batch file\BachFileName.bat" and press RETURN.
Now it can be seen in the command prompt window each line executed by Windows command processor after preprocessing each line and each command block which means after replacing all %VariableName% by the current value of the variable in current line or entire command block.
And error messages can be also seen as the command prompt window remains open after processing batch file stopped, except it contains the command exit without option /B which always terminates the current command process.
Basic Question:
How can I take a recently created folder (done in the same batch), then do stuff in the folder, then rename the folder with a timestamp and repeat with a new file generating a new timestamp?
My Batch Code Outline and Expected vs. Actual Results:
I am creating a batch file that will loop through the steps below, for all files in a directory.
Step 1 (working): go through file in a directory and extract data, this data will then be outputted into a created folder that has to be named "output" (which is created during this step).
Step 2 (working): I have to go into this "output" folder and "do stuff" with the data (I already have a script that goes into this new filepath of the "output" and "does stuff")
Step 3 (not working): rename the folder "output" to "output_TimeStamp" (This is where my problem is, my loop takes the timestamp#1 of the first folder created, and tries to name all folders timestamp#1)
Step 4 (semi working): Go back to Step 1 to work on the next file (Loop till all files are finished in directory)
My Code (well at least one of my attempts)
::Loop to perform tasks on files in current directory
for /R %%f in (*.mp4) do (
::Extracts data from file and leaves it in a created output folder
start "" /w Sample.exe --clip "%%f" --verbose 2 --outDir output
::This goes in created output folder and does stuff to data
start "" /w C:\Users\user\Documents\winPython\WinPython-64bit-3.4.4.2\python-3.4.4.amd64\python.exe "%CD%\DoStuff.py" "%CD%\output\folder\folder2\Do.file"
::This is supposed to rename the folder with a time stamp
rename output output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%
::This is what my research came to, which increments a number in the timestamp but doesnt work
set N=0
set FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%
:loop
set /a N+=1
set FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%
if exist %FILENAME% goto :loop
echo You can safely use this name %FILENAME% to create a new file
)
My Research
I have tried numerous things and used the links How do I increment a folder name using Windows batch?
and cmd line rename file with date and time.
I feel like this should be a lot easier then writing out this question.
Not checking the logic of your script, I fixed the following issues I already mentioned in my comment:
goto :Label breaks the context of a parenthesised block of code, your for loop constitutes such a block; so execution continues at :Label but in a way as it would not be in a block any more. You can work around that by placing the code portion containing goto and :Label in a subroutine and call it by the call command; this hides the current block context from goto.
:: comments might lead to unexpected behaviour when being used within loops or other parenthesised blocks of code; they actually constitute invalid labels. You should use rem instead.
Enable delayed expansion and use it for variables date, time and FILENAME, because otherwise they always expand to the values present when the entire loop is parsed.
So here is the modified code:
setlocal EnableDelayedExpansion
rem // Loop to perform tasks on files in current directory
for /R %%f in (*.mp4) do (
rem // Extracts data from file and leaves it in a created output folder
start "" /w "Sample.exe" --clip "%%f" --verbose 2 --outDir output
rem // This goes in created output folder and does stuff to data
start "" /w "C:\Users\user\Documents\winPython\WinPython-64bit-3.4.4.2\python-3.4.4.amd64\python.exe" "%CD%\DoStuff.py" "%CD%\output\folder\folder2\Do.file"
rem // This is supposed to rename the folder with a time stamp
rename "output" "output-!date:~4,2!-!date:~7,2!-!date:~10,4!_at_!time:~0,2!!time:~3,2!"
call :SUB
echo You can safely use this name !FILENAME! to create a new file
)
endlocal
exit /B
:SUB
rem // This is what my research came to, which increments a number in the timestamp but doesnt work
set /a N=0
set "FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%"
:loop
set /a N+=1
set "FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%"
if exist "%FILENAME%" goto :loop
exit /B
I am very new to coding and bulk processes but i am looking for a command line SPECIFICALLY for windows command prompt and i am wondering if such a thing exists. So I have a folder containing 111 subfolders, with each subfolder containing between 20 and 40 png image files. Each subfolder is named 001-111 accordingly and the png files are ordered how i want them, however i am looking for a command line that would be able to quickly and efficiently name all the pngs in the folders to the name of the folder followed by the png number in brackets
e.g. for folder 037, i would want the png's to be renamed to: 037(1), 037(2), 037(3) etc...
I am hoping for the best although i am unsure such a code may not be possible or be simply done.
Also if you come up with a code that achieves this process, it would be great if you could reply with the simple command line that i could use rather than a full explanation because i am new to coding and far from fluent with the language or terms or how things work. I know this same process can be achieved by going select all>rename (ctrl a>f2) and renaming to the folder name however i need to use this process frequently and dont want to have to open each folder, i would rather have a command line for cmd that would do it swiftly
Thank you and a simple answer would be greatly appreciated
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "parentdir=u:\parent"
FOR /l %%a IN (1001,1,1111) DO (
SET dir=%%a&SET "dir=!dir:~1!"
FOR /f "delims=" %%i IN ('dir /a-d /b "%parentdir%\!dir!\*.png" 2^>nul') DO (
ECHO REN "%parentdir%\!dir!\%%~nxi" "!dir!(%%~ni)%%~xi"
)
)
GOTO :EOF
Test results:
Starting directory :
u:\parent\001\1.png
u:\parent\037\1.png
u:\parent\037\2.png
u:\parent\111\999 with spaces in name.png
Script response
REN "u:\parent\001\1.png" "001(1).png"
REN "u:\parent\037\1.png" "037(1).png"
REN "u:\parent\037\2.png" "037(2).png"
REN "u:\parent\111\999 with spaces in name.png" "111(999 with spaces in name).png"
Obviously, you'd need to replace the value assigned to parentdir with your actual target directory name.
The script will report the renames it proposes to do. To actually invoke the rename remove the ECHO keyword.
I would create a batch file like so:
renamepng.bat:
cd %%1
if ERRORLEVEL 1 goto end
for %f in *.png do mv "%f" "%%1(%f).png"
cd ..
:end
This will attempt to cd to the directory name provided on the command line, abort if that fails, then rename all the .png files and return to the previous directory
then call it like so:
for %d in ??? do call renamepng.bat %d
which will loop through all 3-character file and directory names in the current directory, can call the batch file on each one. Using call instead of just the batch file name causes execution to return to the loop when the batch finishes.
I'm trying to create a batch file that can have other files dropped onto it. Specifically, I'm using ffmpeg to edit audio files produced by a handheld voice recorder. The problem is when using filenames with ampersands (&). Even when quoting the input, anything after the & is dropped off, but only when files are dropped onto it; if the filename input is typed on the command line, the script works fine. Before the cmd window closes, I briefly see the rest of the filename with an error saying it is not recognized as a valid command.
Here's 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's what appears on the command line, after dropping "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 it matters: I'm using the Windows 8 Developer Preview. Is this causing my problem? Does the same error occur on Windows 7 or earlier?
There is a long-standing bug in Windows drag and drop functionality regarding file paths that contain & or ^ but don't contain a <space>.
If a file path contains at least one <space>, then Windows automatically encloses the path in quotes so that it gets parsed properly. Windows should do the same thing if the file path contains & or ^, but it does not.
If you create the following simple batch file and drag files onto it, you can see the problem.
#echo off
setlocal enableDelayedExpansion
echo cmd=!cmdcmdline!
echo %%1="%~1"
pause
exit
The !cmdcmdline! variable contains the actual command that launched the batch file.
The batch file prints out the command line and the first parameter.
If you drag and drop a file named "a.txt" you get
cmd=cmd /c ""C:\test\drag.bat" C:\test\a.txt"
%1=C:\test\a.txt
Press any key to continue . . .
If you disregard the quotes around the entire command you see that there are no quotes around the file argument. There are no special characters, so there is no problem.
Now drag and drop "a 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 the space in the name and encloses the file in quotes. Again there is no problem.
Now drag and drop "a&b.txt" and you get
cmd=cmd /c ""C:\test\drag.bat" C:\test\a&b.txt"
%1=C:\test\a
Press any key to continue . . .
Windows doesn't find a space in the name, so it does not enclose it in quotes. Big problem! Windows passes "C:\test\a" to the batch file and treats "b.txt" as a second file to be executed after the batch file completes. The hard EXIT command in the batch file prevents any split filenames from executing after the batch. Of course b.txt could never execute. But if the file were named "a&b.bat" and "b.bat" existed, then that could be trouble if the hard EXIT were not in the batch file.
It is possible to drag multiple files onto a batch file, and each one should be passed as a parameter.
The !cmdcmdline! is the only way to reliably access drag and drop arguments. But that will not work if files are passed as normal arguments in a normal call to the batch file.
Below is a batch file that can detect if it was called using drag and drop versus a normal call. (It is not bullet proof, 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 echos the file name, but you can substitute whatever processing you want.) If the batch was called using drag and drop then it will do 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 designed to handle one file. I can't tell if you need to make modifications to the calls to support multiple files. I modified the above batch to only process the first argument, and substituted your process into the argument processing loop. This is untested, 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
)
I admire dbenham's batch programming skills in silent awe.
I tried his solution and stumbled upon two problems that I present here as I don't have enough reputation to comment:
There seems to be an extra space in front of the last quotation mark on line 15 of his batch template. I suppose it should read:
set "args=!cmd2:~0,-1!"
Someone with not-so-stellar batch programming knowledge could have serious problems finding this, like me. I tried but was unable to edit dbenham's post because of the stupid "Edits must be at least 6 characters" limitation.
The solution is generally not suitable for files/folders containing , (comma) or ; (semicolon) in their full path.
It can be modified to work in case there is only one file/folder dropped onto a batch file by enclosing args in quotes on line 20:
for %%F in ("!args!") do (
When more than one file/folder is dropped onto a batch file, I am afraid no general workaround of the Windows bug is possible that could cope with comma/semicolon in file path. The SendTo mechanism of Windows obviously has the very same deficiency (bug), so can't be used to work around the drag-and-drop bug. It is thus up to Microsoft to finally fix this bug.