Batch Drag and drop files and folders - windows-7

I'm trying to copy multiple files and folders from a drag and drop selection using a solution I think should look something like this:
mkdir newdir
for %%a in ("%*") do (
echo %%a ^ >>new.set
)
for /f "tokens=* delims= " %%b in ('type "new.set"') do (
SET inset=%%b
call :folderchk
if "%diratr%"=="d" robocopy "%%b" "newdir" "*.*" "*.*" /B /E && exit /b
copy /Y %%b newdir
)
exit /b
:folderchk
for /f tokens=* delims= " %%c in ('dir /b %inset%') do (
set atr=%~ac
set diratr=%atr:~0,1%
)
I've tried throwing together code from the following examples but I'm stuck:
http://ss64.com/nt/syntax-dragdrop.html
Drag and drop batch file for multiple files?
Batch Processing of Multiple Files in Multiple Folders

Handling of special characters with Drag&Drop is tricky, as doesn't quote them in a reliable way.
Spaces are not very complicated, as filenames with spaces are automatically quoted.
But there are two special characters, who can produce problems, the exclamation mark and the ampersand.
Names with an ampersand will not automatically quoted, so the batch can be called this way
myBatch.bat Cat&Dog.txt
This produces two problems, first the parameters aren't complete.
In %1 and also in %* is only the text Cat the &Dog.txt part can't be accessed via the normal parameters, but via the cmdcmdline variable.
This should be expanded via delayed expansion, else exclamation marks and carets can be removed from the filenames.
And when the batch ends, it should use the exit command to close the cmd-window, else the &Dog.txt will be executed and produce normally an error.
So reading the filenamelist should look like
#echo off
setlocal ENABLEDELAYEDEXPANSION
rem Take the cmd-line, remove all until the first parameter
set "params=!cmdcmdline:~0,-1!"
set "params=!params:*" =!"
set count=0
rem Split the parameters on spaces but respect the quotes
for %%N IN (!params!) do (
echo %%N
)
pause
REM ** The exit is important, so the cmd.ex doesn't try to execute commands after ampersands
exit
This is also described in the link you refereneced Drag and drop batch file for multiple files?

Related

How to first order files in folder by date and then concatenate their contents into a new file?

I have a folder with four to five text files in it.
My overall aim is the following: Create one big file which has the content of the separate files, but in the right order.
I can use the time-stamp of each file to start with the oldest file up to the youngest.
My process right now looks like this:
Order the files in this folder by date.
Create a temporary file and write the content from the separate files into this file.
Output the temporary file.
In code I do something like this:
set temp_concat=%temp_dir%\temp_concat.log
echo %temp_concat%
echo aiu_logs > %temp_concat%
for /f "delims=" %%? in ('dir /b /o:d %Folder%*') do (
for /f "delims=" %%K in (%Folder%%%?) do (
echo %%K >>%temp_concat%
)
)
The above code seems to work as my temp_concat is very large.
However, this takes much much longer than expected. I have to wait about 40 seconds just to merge three files in my case.
Is there some better way of merging some amount of files, but keep them in the correct order by date?
This batch file uses the suggestion posted by Sqashman to use a FOR loop to create the arguments string for command COPY used to concatenate the file contents into a single file in the order of oldest modified file first and newest modified file last.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Folder=%~dp0"
if not "%~1" == "" set "Folder=%~1"
set "Folder=%Folder:/=\%"
if not "%Folder:~-1%" == "\" set "Folder=%Folder%\"
set "ResultsFile=%Folder%Results.log"
del "%ResultsFile%" 2>nul
set "Arguments="
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /O:D "%Folder%*" 2^>nul') do if not "%%~fI" == "%~f0" set "Arguments=!Arguments! + "%%I""
if defined Arguments (
echo aiu_logs>"%ResultsFile%"
copy /B "%ResultsFile%"%Arguments% "%ResultsFile%" >nul
)
endlocal
The batch file as is does not work if either the folder path or one of the file names contains one or more exclamation marks ! because of an enabled delayed environment variable expansion.
Further the command line length is limited and so this batch file does not work on too many files must be concatenated depending on length of the file path of each file and the length of the file names.
A better solution would be using following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Folder=%~dp0"
if not "%~1" == "" set "Folder=%~1"
set "Folder=%Folder:/=\%"
pushd "%Folder%" 2>nul
if errorlevel 1 goto EndBatch
set "ResultsFile=Results.log"
del "%ResultsFile%" 2>nul
set "Arguments="
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /O:D * 2^>nul') do if not "%%~fI" == "%~f0" call set "Arguments=%%Arguments%% + "%%I""
if defined Arguments (
echo aiu_logs>"%ResultsFile%"
copy /B "%ResultsFile%"%Arguments% "%ResultsFile%" >nul
)
popd
:EndBatch
endlocal
A folder path with one or more exclamation marks is no problem anymore. Also the file names can contain ! because of delayed expansion is not used by this batch file which is a bit slower than the first batch file.
The folder with the files to concatenate is made the current directory by this batch file. For that reason more file names can be specified as arguments on COPY command line in comparison to first batch file because of the file names are specified without path. But the number of file contents which can be merged with this batch file is nevertheless limited by the maximum length of a Windows command line respectively the maximum length of an environment variable value.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
popd /?
pushd /?
set /?
setlocal /?
Read also the Microsoft article about Using command redirection operators for an explanation of > and 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background with %ComSpec% /c and the DIR command line between the two ' appended as further arguments.
The second FOR /F does not contain a command. It contains a filename. I have not tested this, but perhaps:
set temp_concat=%temp_dir%\temp_concat.log
echo %temp_concat%
echo aiu_logs > "%temp_concat%"
for /f "delims=" %%? in ('dir /b /o:d "%Folder%"') do (
if not "%%~f?" == "%~f0" (
type %%? >>"%temp_concat%"
)
)
This will concatenate all files in the "%Folder%" directory. Paths should be quoted in case there are special characters in them.

How do I create folder from file name and move files into folder?

I need a windows batch file to create a folder based on part of a file name (the part before an underscore) and move any files that start with the folder name into the folder.
I'm not familiar with windows batch files. I've googled and tinkered a solution which works except that I cannot substring the file name at the underscore.
(Yes there are a few similar threads but nothing I could use to exactly answer my question)
FWIW my unsuccessful solution:
#ECHO OFF
setlocal enabledelayedexpansion
SETLOCAL
SET "sourcedir=C:\Development\test"
PUSHD %sourcedir%
FOR /f "tokens=1*" %%a IN (
'dir /b /a-d "TTT*_*.*"'
) DO (
ECHO MD NEED FILE NAME BEFORE UNDERSCORE HERE
ECHO MOVE "%%a" .\NEED FILE NAME BEFORE UNDERSCORE HERE\
)
(Ideally I'd remove the leading 'TTT' from files too but if necessary can create the files without this.)
Try this batch file code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\Development\test"
set "DestDir=C:\Development\test"
for /F "eol=| delims=" %%A in ('dir /B /A-D-H "%SourceDir%\TTT*_*" 2^>nul') do (
for /F "eol=| tokens=1 delims=_" %%B in ("%%~nA") do (
md "%DestDir%\%%B" 2>nul
move /Y "%SourceDir%\%%A" "%DestDir%\%%B\"
)
)
endlocal
The first FOR executes in a separate command process started with cmd.exe /C in background the command line:
dir /B /A-D-H "C:\Development\test\TTT*_*" 2>nul
DIR searches in specified directory for
just non-hidden files because of /A-D-H (attribute not directory and not hidden)
matching the wildcard pattern TTT*_* which could be also just *_*
and outputs to handle STDOUT in bare format because of /B just the file names with file extension, but without file path.
The error message output by DIR to handle STDERR if the specified directory does not exist at all or there is no file matching the pattern is suppressed by redirecting it with 2>nul to device NUL.
Read also the Microsoft documentation about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR captures everything written to STDOUT of started command process and processes the captured output line by line.
FOR ignores by default all empty lines (do not occur here) and all lines starting with a semicolon. A file name could begin with a semicolon. For that reason option eol=| is used to redefine end of line character to vertical bar which a file name can't contain, see Microsoft documentation Naming Files, Paths, and Namespaces. In this case on using TTT*_* as wildcard pattern it is not possible that a file name starts with a semicolon, but it would be possible on usage of *_* as wildcard pattern.
FOR would split up also each line into substrings (tokens) using space/tab as delimiters and would assign just the first space/tab separated string to specified loop variable A. This splitting behavior is not wanted here as file names can contain one or more space characters. Therefore the option delims= is used to define an empty list of delimiters which disables line splitting completely and results in assigning entire file name with extension to loop variable A.
The inner FOR processes just the file name (without extension) as string. This time the file name is split up using the underscore as delimiter because of delims=_ with assigning just first underscore delimited string to loop variable B because of tokens=1. Well, tokens=1 is the default on using for /F and so this option string could be removed from code.
So the outer FOR assigns to A for example TTTxy_test & example!.txt and the inner FOR processes TTTxy_test & example! and assigns to B the string TTTxy.
The command MD creates in set destination directory a subdirectory for example with name TTTxy. An error message is output also on directory already existing. This error message is suppressed by redirecting it to device NUL.
Then the file is moved from source to perhaps just created subdirectory in destination directory with overwriting an existing file with same name in target directory of the file.
The inner FOR loop could be optimized away when there are never files starting with an underscore or which have more than one underscore after first part of file name up to first underscore.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\Development\test"
set "DestDir=C:\Development\test"
for /F "eol=| tokens=1* delims=_" %%A in ('dir /B /A-D-H "%SourceDir%\TTT*_*" 2^>nul') do (
md "%DestDir%\%%A" 2>nul
move /Y "%SourceDir%\%%A_%%B" "%DestDir%\%%A\"
)
endlocal
Option tokens=1* results in assigning first underscore delimited part of file name to loop variable A and rest of file name to next loop variable B according to ASCII table without further splitting up on underscores.
But please take into account that the optimized version does not work for file names like
_TTTxy_test & example!.txt ... underscore at beginning (ignored by pattern), or
TTTxy__test & example!.txt ... more than one underscore after first part.
The optimized version can be further optimized to a single command line:
#for /F "eol=| tokens=1* delims=_" %%A in ('dir /B /A-D-H "C:\Development\test\TTT*_*" 2^>nul') do #md "C:\Development\test\%%A" 2>nul & move /Y "C:\Development\test\%%A_%%B" "C:\Development\test\%%A\"
Well, the not optimized version could be also written as even longer single command line:
#for /F "eol=| delims=" %%A in ('dir /B /A-D-H "C:\Development\test\TTT*_*" 2^>nul') do #for /F "eol=| tokens=1 delims=_" %%B in ("%%~nA") do #md "C:\Development\test\%%B" 2>nul & move /Y "C:\Development\test\%%A" "C:\Development\test\%%B\"
See also Single line with multiple commands using Windows batch file for an explanation of operator &.
For additionally removing TTT from file name on moving the file the first batch code is modified with using two additional commands SET and CALL:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\Development\test"
set "DestDir=C:\Development\test"
for /F "eol=| delims=" %%A in ('dir /B /A-D-H "%SourceDir%\TTT*_*" 2^>nul') do (
for /F "eol=| tokens=1 delims=_" %%B in ("%%~nA") do (
md "%DestDir%\%%B" 2>nul
set "FileName=%%A"
call move /Y "%SourceDir%\%%A" "%DestDir%\%%B\%%FileName:~3%%"
)
)
endlocal
The file name is assigned to an environment variable FileName. The value of this environment variable cannot be referenced with just using %FileName% because of all references of environment variable values using percent signs are substituted by Windows command processor in entire command block starting with first ( and ending with matching ) before FOR is executed at all. Delayed expansion is usually used in such cases, but that would result here in file names containing one or more exclamation marks would not be corrected processed by the batch file.
The solution is using %% on both sides of FileName environment variable reference instead of % and force a double parsing of the command line by using command CALL.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
for /?
md /?
move /?
set /?
setlocal /?
It is really very simple:
#echo off
for /f "tokens=1-2 delims=_" %%i in ('dir /b /a-d "TTT*_*"') do (
if not exist "%%i" mkdir "%%i"
move "%%i_%%j" "%%i\%%j"
)
We split by _ into 2 tokens, %%i everything before _ and %%j everything after.
We simply create folder (if it does not exist) then move the file with only the name after the _ into the new folder.
So as an example file TTT123_File1.txt will create a folder called TTT123 and place the file into it but rename it as File1.txt
You might consider using Tcl/Tk. Tcl/Tk is an open source script language. You can call it as a stand-alone or execute it from a windows batch file. You will need to install it first if you don't have it yet.
The following Tcl script does what you want:
cd "C:/Development/test"
# glob is a tcl command to list all functions that match the requirements
set files [glob TTT*_*]
foreach f $files {
# use the underscore as a separator to split f and store the parts in dir and fnew
lassign [split $f "_"] dir fnew
if {![file exist $dir]} {
file mkdir $dir
}
file rename $f [file join $dir $fnew]
}
In my opinion, this is a very readable script, even if you don't know tcl.
You can call this script from a batch file as:
tclsh script.tcl
if you have saved the script as script.tcl

unable to use for loop output to set variable in batch

I'm using a for loop to acces a text file with a bunch of files + their directory formatted like this:
//srv/something/somethingelse/movie.mpg
//srv/something/somethingelse/movie2.mkv
//srv/something/somethingelse/movie3.mpg
//srv/something/somethingelse/movie4.mkv
I have to replace .mpg and .mkv with .xml, and then write that output away to another text file, which I'm trying to do like this:
for /F "tokens=*" %%A in (%~dp0temporary\movies.txt) do (
set string=%%A
set find=.mkv
set replace=.xml
set string=%%string:!find!=!replace!%%
set find=.mpg
set string=%%string:!find!=!replace!%%
echo %string%>>%~dp0temporary\xml.txt
)
The output I want is this:
//srv/something/somethingelse/movie.xml
//srv/something/somethingelse/movie2.xml
//srv/something/somethingelse/movie3.xml
//srv/something/somethingelse/movie4.xml
But what I get is this:
Echo is off.
Echo is off.
Echo is off.
Echo is off.
I have been searching on this for over an hour but I can't find anything that works
Here is the rewritten batch code which produces the expected output from input file.
#echo off
setlocal EnableDelayedExpansion
set "vidLoc=//srv"
set "resultLoc=c:"
del "%~dp0temporary\xml.txt" 2>nul
for /F "usebackq delims=" %%A in ("%~dp0temporary\movies.txt") do (
set "FileNameWithPath=%%A"
set "FileNameWithPath=!FileNameWithPath:.mkv=.xml!"
set "FileNameWithPath=!FileNameWithPath:.mpg=.xml!"
set "FileNameWithPath=!FileNameWithPath:%vidLoc%=%resultLoc%!"
echo !FileNameWithPath!>>"%~dp0temporary\xml.txt"
)
endlocal
All environment variable references enclosed in percent signs are expanded already on parsing entire for block. Just the environment variable references enclosed in exclamation marks are expanded delayed on executing the command. This can be seen on opening a command prompt window and running from there the batch file without #echo off at top or with this line being changed to #echo on.
Executing in a command prompt window set /? results in getting help of this command output on several window pages where usage of delayed expansion for for and if blocks is explained on a simple example.
And running in a command prompt window for /? prints help of command for into the output window.
For just replacing the file extension you could also use:
#echo off
del "%~dp0temporary\xml.txt" 2>nul
for /F "usebackq delims=*" %%A in ("%~dp0temporary\movies.txt") do (
echo %%~dpnA.xml>>"%~dp0temporary\xml.txt"
)
But this faster code changes also all forward slashes / to backslashes \ as the backslash character is the directory separator on Windows.
Mofi is right: move the line with setlocal enabledelayedexpansion out of any code block enclosed in (parentheses).
However, try next approach using Command Line arguments (Parameters) modifier ~:
#ECHO OFF >NUL
#SETLOCAL enableextensions
for /F "tokens=*" %%A in (%~dp0temporary\movies.txt) do (
rem full_path=%%~dpnA
rem extension=%%~xA
echo %%~dpnA.xml
)>%~dp0temporary\xml.txt

For loop copying files in command prompt using a substring to name the destination for each file (with delayed expansion)

I have a directory of files and a seperate directory filled with folders with names that correspond to a substring of the file name. I'm trying to cycle through a list of files and for each one, where there is a folder with the corresponding substring name, deposit a copy of the file into it. Where there is no corresponding folder, I need the file to be ignored. This can't be done in a batch file, it needs to be done in command prompt.
I'm having the problem that the initial response to my command
for /f %I in ('dir /b J:\test\test_audio\en * ^| find ".wav"') do (
set var=I
copy J:\test\test_audio\en\%~I J:\test\test_designed_locations\%var:~0,5%\
)
is The syntax of the command is incorrect, due I think to the order of expansion of the command.
....
C:\Windows\System32>(
set var=Ev164_en.wav
copy J:\test\test_audio\en\Ev164_en.wav J:\test\test_designed_locations\%var:~0,5%\
)
The syntax of the command is incorrect
C:\Windows\System32>(
set var=Ev178_en.wav
copy J:\test\test_audio\en\Ev178_en.wav J:\test\test_designed_locations\%var:~0,5%\
)
The syntax of the command is incorrect
...
This would also explain why running the command a second time creates a situation where the substring from the last file is used in all commands, causing every file to be deposited in the last folder and none in the others.
....
for /f %I in ('dir /b J:\test\test_audio\en * ^| find ".wav"') do (
set var=I
copy J:\test\test_audio\en\157_en.wav J:\test\test_designed_locations\Ev225\
)
1 file(s) copied.
for /f %I in ('dir /b J:\test\test_audio\en * ^| find ".wav"') do (
set var=I
copy J:\test\test_audio\en\158_en.wav J:\test\test_designed_locations\Ev225\
)
1 file(s) copied.
....
I suspect that I need to use delayed expansion to stop environment variables being processed and overwriting each other before use. I've tried adding setLocal enableDelayedExpansion at the start of the command and wrapping the reference to the variable in bangs
setLocal enableDelayedExpansion
for /f %I in ('dir /b J:\test\test_audio\en * ^| find ".wav"') do (
set var=%I
copy J:\test\test_audio\en\%~I J:\test\test_designed_locations\!var:~0,5!\
)
With or without the an escape before the comma, I get
....
C:\Windows\System32>(
set var=Ev164_en.wav
copy J:\test\test_audio\en\Ev164_en.wav J:\test\test_designed_locations\!var:~0,5!\
)
The syntax of the command is incorrect.
....
The variable isn't being processed for its value. I'm starting to wonder if it's actually possible to for-loop through all the files and use a substring from each file name to identify where to put that file, all in one command. Any help much appreciated.
cmd /v /q /c "pushd "j:\test\test_audio\en" && ( for %a in (*.wav) do ( set "I=%~nxa" & if exist "!I:~0,5!\" copy "%a" "!I:~0,5!" ) & popd )"
You can use delayed expansion from command line only if the cmd instance is started with it enabled.
edited to adapt to comments
pushd "j:\test\test_audio\en" && (for %a in (*.wav) do (set "I=%~nxa" & call pushd ^"%I:~0,5^%^" 2>nul &&(copy "%~fa" & popd)) & popd)
Well, it can be done without starting a separate cmd instance to enable delayed expansion, but the needed escaping to use a call to force a second parser evaluation of the line is not very obvious.

how to replace names recursively via windows batch operation

I want to process a batch operation in a big directory. Actually I have the batch script for that process. But here , I have a problem. Some of the directory names, files names contain " " (space character). So in the batch operation this names passed as 2 arguments . and those lines will not work. So Iwant to rename " " with "_" to overcome this problem .
Example:
process /MyDirectory/Ola and Me/Private/TopSecretPictures/
this gives error. the below one works fine
process /MyDirectory/Ola and Me/Private/TopSecretPictures
My aim is: convert | Ola and Me |>> |Ola_And_Me recursively
:)
thanks in advance ..
The following script renames all files and directories recursively, starting at a given directory, converting spaces to underscores.
spaces_to_underscores.bat source:
#echo off
setlocal
for /r "%~1" %%t in (.) do (
for /f "usebackq tokens=*" %%f in (`dir /b/a-d "%%~t" 2^>nul:`) do (
call :proc "%%~f" "%%~t"
)
for /f "usebackq tokens=*" %%d in (`dir /b/ad "%%~t" 2^>nul:`) do (
call :proc "%%~d" "%%~t"
)
)
exit /b 0
:proc
set fn=%~1
if "%fn: =_%"=="%fn%" exit /b 0
set fn=%~2\%fn: =_%
move "%~2\%~1" "%fn%" >nul:
exit /b 0
Usage:
spaces_to_underscores "My Directory"
Given this directory structure
My Directory
Ola and Me
Private
TopSecretPictures
it will rename the folder "Ola and Me" to "Ola_and_Me", and also rename any files such as "Photo 001.jpg" to "Photo_001.jpg". The starting directory "My Directory" will not be renamed.
WARNING: Do not run this script on standard windows directories, such as "C:\Documents and Settings" or "C:\Program Files" or "My Documents" or "Application Data". There is no "undo" functionality here. Make sure you have a backup.
You can do this in a batch file if you use a feature called "delayed exapansion" that isn't on by default. To switch it on, you need to start cmd.exe with the /v switch:
cmd.exe /v
Once this is on, the following batch script will replace all spaces in %%i with underscores, and spit the result out:
for /f "usebackq tokens=*" %%i in (`dir /b`) do (
set S=%%i
set T=!S: =_!
echo !T!
)
***Vauge description...***Excluding the for loop itself, the interesting parts of this are:
String substitution using the %var:str1=str2% syntax
Delayed expansion using !var! instead of %var%
First: delayed expansion... without this, the command interpreter (for whatever reason Microsoft decided to code it as) will evaluate all the parameters first, and then run the script: so this version of the script does NOT work:
for /f "usebackq tokens=*" %%i in (`dir /b`) do (
set S=%%i
set T=%S: =_%
echo %T%
)
With this version the variable 'T' is set to the last value of the for loop before the contents of the (...) block actually execute. Which makes no sense to me. So with delayed execution enabled, we can use the delayed execution variable marks, i.e., !var! rather than %var%. Which gives us the right result.
The other clever bit then is the set T=!S: =_! (which basically says set T to S, replacing every '' ' in S with '_'). Without delayed expansion, this would be written set T=%S: =_%.

Resources