Error running with input a parameter in bat script - batch-file

I am trying to convert a number of .doc files into .docx files and I found a solution:
for %F in (*.doc) do "C:\Program Files\Microsoft Office\Office12\wordconv.exe" -oice -nme "%F" "%Fx"
For the detailed info see: Automation: how to automate transforming .doc to .docx?
Now I want to use the absolute path of wordconv.exe as an input parameter.
Then my approach is like this:
The contents of doc2docx.bat:
for %F in (*.doc) do (%1 -oice -nme "%F" "%Fx")
Run doc2docx.bat in console with:
doc2docx.bat "C:\Program Files\Microsoft Office\Office12\wordconv.exe"
I got the result below:
D:\book\work\temp\5. SPEC_NEW>D:\book\doc2docx.bat "C:\Program Files (x86)\Microsoft Office\Office12\Wordconv.exe"
此时不应有 1。
D:\book\work\temp\5. SPEC_NEW>for 1 -oice -nme "Fx")
D:\book\work\temp\5. SPEC_NEW>
The message 此时不应有 1。 means '1' was unexpected at this time.
How could I solve it?
I know little about batch script coding.

I recommend to open a command prompt, run for /? and read the output help from top of first to bottom of last page. There is already written in fourth paragraph that in a batch file the loop variable must be referenced with doubling the percent sign in comparison to usage of command FOR directly on Windows command prompt.
So the solution could be:
for %%F in (*.doc) do %1 -oice -nme "%%F" "%%~nF.docx"
But I can't recommend to use this command line in the batch file because of following reasons.
Reason 1 is explained in detail by this answer by the chapter Issue 7: Usage of letters ADFNPSTXZadfnpstxz as loop variable. It is possible to use these letters for a loop variable, but it is advisable not doing that. There are lots of other ASCII characters with no special meaning which are always safe to use as loop variable.
Reason 2 is that command FOR searches with *.doc in current directory for files with .doc in long or in short file name. So if the directory contains Test1.doc and Test2.docx, FOR runs the converter executable with both file names as it can be seen on running in the command prompt window the command line:
for %I in (*.doc) do #echo Long name: %I - short name: %~snxI
The output for a directory containing Test1.doc and Test2.docx is:
Long name: Test1.doc - short name: TEST1.DOC
Long name: Test2.docx - short name: TEST2~1.DOC
Reason 3 is often problematic on FAT32 and exFAT drives, but is sometimes even a problem on NTFS drives. FOR accesses the file system on processing the files matched by the wildcard pattern after each iteration. There is executed in background _findfirst, _findnext, _findnext, ..., _findclose. The problem is that the directory entries change because of the conversions of the Microsoft Word files as the *.docx files are created in same directory as the processed *.doc files. All file systems return the file names as stored in their table. The difference is that the NTFS master file table is locale specific sorted by name while the table of FAT32 and exFAT is not sorted at all and so changes dramatically with each creation or deletion of a file or folder in a directory. For that reason it could happen that some .doc files are processed more than once and others are skipped and even an endless running loop could be the result. In other words the FOR loop behavior is undefined in this case and so the loop could work by chance, but could also fail.
The solution is using the command line:
for /F "delims=" %%I in ('%SystemRoot%\System32\where.exe *.doc 2^>nul') do %1 -oice -nme "%%I" "%%~dpnI.docx"
Problem 1 is avoided by using I as loop variable. It would be also possible to use # or B or J and lots of other ASCII characters.
FOR with option /F and a set enclosed in ' results in starting in background one more command process with %ComSpec% /c and the command line within ' appended as additional arguments. Therefore it is executed in background with Windows installed into C:\Windows:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\where.exe *.doc 2>nul
WHERE is different to FOR or DIR. It searches only in long file name for files with extension .doc. So problem 2 with matching also files with file extension .docx is avoided by using command WHERE which outputs the found files matching the wildcard pattern with full qualified file name (drive + path + name + extension).
Read 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 where command line with using a separate command process started in background.
Everything written by where to handle STDOUT (standard output) of started background command process is captured by cmd.exe processing the batch file. The captured lines are processed after started cmd.exe terminated itself after where.exe finished. For that reason the problem 3 is avoided as the list of file names with file extension .doc does not change anymore on running the conversion. The file names list is already completely loaded into memory of cmd.exe before starting processing them.
FOR with option /F results by default in ignoring empty lines, splitting up each line into substrings using normal space and horizontal tab as string delimiters, ignoring the line if the first space/tab delimited string starts with a semicolon, and otherwise assigning just first space/tab delimited string to the specified loop variable. This default line processing behavior is not wanted here because of the full qualified file names can contain one or more spaces. The option string "delims=" defines an empty list of delimiters which results in disabling the line splitting behavior completely. where outputs the file names with full path and so no captured line can have a ; at beginning and for that reason the implicit default eol=; can be kept in this case. Otherwise on using a different command line resulting in captured lines being just the names of the files matching a wildcard pattern eol=| or eol=? could be used as neither | nor ? can be used in a file name to avoid that files of which name starts unusually with ; are ignored by FOR.
I suggest to use the following batch file which searches itself for wordconv.exe by using the path stored in Windows registry for winword.exe or excel.exe or powerpnt.exe added by the installer of Office for Application Registration on being started with no file name of an executable.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
goto Main
:GetConvertToolName
for %%# in (winword excel powerpnt) do (
for /F "skip=2 tokens=1,2*" %%I in ('%SystemRoot%\System32\reg.exe query "%~1\%%#.exe" /v Path') do (
if /I "%%I" == "Path" if exist "%%~K\wordconv.exe" for %%L in ("%%~K\wordconv.exe") do set "ConvertTool=%%~fL" & goto :EOF
)
)
goto :EOF
:Main
set "ConvertTool="
if not "%~1" == "" if exist "%~1" if /I "%~x1" == ".exe" set "ConvertTool=%~1"
if not defined ConvertTool call :GetConvertToolName "HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths"
if not defined ConvertTool call :GetConvertToolName "HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths"
if not defined ConvertTool (
echo ERROR: Failed to find the program wordconv.exe.
echo/
echo Please run %~nx0 with full name of wordconv.exe as argument.
echo/
pause
) else for /F "delims=" %%I in ('%SystemRoot%\System32\where.exe *.doc 2^>nul') do "%ConvertTool%" -oice -nme "%%I" "%%~dpnI.docx"
endlocal
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 /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
reg /?
reg query /?
set /?
setlocal /?
where /?

The command line:
for %F in (*.doc) do (%1 -oice -nme "%F" "%Fx")
should read:
for %%F in (*.doc) do ("%~1" -oice -nme "%%F" "%%Fx")
to fixe syntax errors (doubled %-symbols in a batch file as stated in the help of for /?) and to properly handle quotation.
But there is still room for improvement:
The pattern *.doc may even match *.docx files when you have short 8.3 file names enabled on your system (because a file called some long name.docx has got a short name like SOMELO~1.DOC, which dir regards too).
Files may already have been converted, hence a check if there already exists a respective *.docx file might be helpful.
There might no argument be provided.
The following code regards these issues:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Check whether first argument points to an existing file:
if exist "%~1" (
rem // Change into target directory:
pushd "D:\path\to\root\directory" && (
rem /* Instead of a standard `for` loop, use `for /F` over `dir /B` to gain the possibility
rem to apply an additional filter by `findstr` to avoid `*.docx` to match: */
for /F "delims= eol=|" %%F in ('dir /B /A:-D-H-S "*.doc" ^| findstr /I "\.doc$"') do (
rem // Check whether there is already a respective `*.docx` file:
if not exist "%%~nF.docx" (
rem // Attempt to convert the current `*.doc` file to `*.docx`:
"%~1" -oice -nme "%%F" "%%Fx"
)
)
rem // Return from target directory:
popd
)
) else (
rem // Raise an error in case the first argument does not point to an existing file:
>&2 echo "%~1" not found!
exit /B 1
)
endlocal
exit /B

for %%F in (*.doc) do (%1 -oice -nme "%F" "%Fx")
Batch is interpreting %F in (*.doc) do (% as a variable which, unsurprisingly, has no value hence it executes
for 1 -oice -nme "%F" "%Fx")
Which explains your error message as 1 is not expected after for.
The metavariable (F in this case) within a batch must be %%F. Only if run directly from the prompt, is it %F.

Related

What is the reason for error message "N was unexpected at this time"?

File: stack.bat
#echo off
#setlocal enabledelayedexpansion
for %%a in (%*) do (
call set "%%~1=%%~2"
shift
)
ECHO para1 %--para1%
ECHO para2 %--para2%
if "%--para2%"=="" (
echo missing para2
for /f "eol=: delims=" %F in ('dir /b/o:N %--folder%\*.001') do #set "newest=%F"
echo latest %newest%
)
This batch file is called with:
stack.bat --para1 c:\Sample\temp
The execution results in output of the error message:
N was unexpected at this time.
There is no error if the line for /f "eol=: ... is commented out with command REM.
Delayed expansion is already enabled.
What do I need to do to fix the error?
It is not described what the batch file code should do at all. It looks like it should find the newest file by its name containing most likely a date string in name in a directory passed as argument to the batch file and should define an environment variable with name passed also as argument left to the directory path with the file name of newest file.
I suggest following commented batch code for this purpose:
#echo off
rem Remove this line if the environment variables defined by this batch
rem file should still exist after processing of this batch file finished.
setlocal EnableExtensions DisableDelayedExpansion
rem Delete all environment variables of which name starts with --.
for /F "delims=" %%I in ('set -- 2^>nul') do set "%%I"
:ProcessArguments
rem Get current first argument (option) with surrounding " removed.
set "Option=%~1"
rem Is there no more option?
if not defined Option goto EndBatch
rem Remove all double quotes from the option.
set "Option=%Option:"=%"
rem This condition is just for 100% fail safe code. It should be never true.
if not defined Option goto EndBatch
rem Does the option not start with two hyphens?
if not "%Option:~0,2%" == "--" (
echo ERROR: Invalid option: "%Option%"
echo/
goto EndBatch
)
rem Get current second argument (folder path) with surrounding " removed.
set "Folder=%~2"
rem Is there no folder path?
if not defined Folder goto MissingFolder
rem Remove all double quotes from the folder path.
set "Folder=%Folder:"=%"
rem This condition is just for 100% fail safe code. It should be never true.
if not defined Folder goto MissingFolder
rem Replace all / in folder path by \ as many users type folder paths wrong
rem with slashes as on Linux/Mac instead of backslashes as required on Windows.
set "Folder=%Folder:/=\%"
rem Make sure the last character of folder path is a backslash.
if not "%Folder:~-1%" == "\" set "Folder=%Folder%\"
rem Search in specified folder for *.001 files output reverse by name and
rem define the option as environment variable with first output file name
rem assigned with full qualified absolute path even if environment variable
rem Folder referencing a relative path. Then shift the arguments list by
rem two arguments to the left and process the remaining arguments.
for /F "eol=| delims=" %%I in ('dir "%Folder%*.001" /A-D /B /O-N 2^>nul') do (
for %%J in ("%Folder%%%I") do set "%Option%=%%~fJ"
shift
shift
goto ProcessArguments
)
rem It is not possible to define an environment variable with no string.
rem So an error message is output if no file could be found like on
rem wrong folder path or no *.001 file found in the specified folder.
echo ERROR: Could not find a *.001 file for option "%Option%" in folder:
echo "%Folder%"
echo/
goto Endbatch
:MissingFolder
echo ERROR: Missing folder path for option: "%Option%"
echo/
:EndBatch
set "Option="
set "Folder="
echo Options parsed successfully:
echo/
set -- 2>nul
rem Remove this line if the environment variables defined by this batch
rem file should still exist after processing of this batch file finished.
endlocal
This batch file can be started for example with the command line:
stack.bat --para1 c:\Sample\temp "--para2" "C:/Temp/Development & Test!/" --para3 . --para4 "\Program Files\Internet Explorer\" -para5 .."
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 /? ... explains how to reference batch file arguments
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
shift /?
Read 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 with %ComSpec% /c and the command line within ' appended as additional arguments.
I suggest also reading:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
This question with its answers should be read first by every beginner in batch file coding.
Problems checking if string is quoted and adding quotes to string
This answer explains very detailed how to process arguments passed to a batch file.
Syntax error in one of two almost-identical batch scripts: ")" cannot be processed syntactically here
This answer describes common issues made by beginners in batch file coding and how to avoid them.
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
Another extensive answer which describes how to work with environment variables in batch files.
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files
This also very long answer describes in detail how a string comparison is done by Windows command processor and what a batch file writer must take into account on using string comparisons.

How to copy a file with an incremented version number in file name depending on existing files?

I have the batch file below:
FOR /F "delims=|" %%I IN ('DIR "%C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\*.xlsx" /B /O:D') DO SET NewestFile=%%I
FOR /F "delims=" %%a IN ('wmic OS Get localdatetime ^| find "."') DO SET DateTime=%%a
set Yr=%DateTime:~0,4%
set Mon=%DateTime:~4,2%
set Day=%DateTime:~6,2%
setlocal enableDelayedExpansion
set "baseName=InventoryReport%Yr%-%Mon%-%Day% V1.%n%"
set "n=0"
FOR /f "delims=" %%F in (
'DIR /b /ad "%baseName%*"^|findstr /xri "\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport\"%baseName%[0-9]*""'
) do (
set "name=%%F"
set "name=!name:*%baseName%=!"
if !name! gtr !n! set "n=!name!"
)
set /a n+=1
md "%baseName%%n%"
copy "%C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\%NewestFile%" "\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport\%baseName%%n%.xlsx"
cmd /k
I cannot get it to find the greatest version number of previously copied file between V1. and file extension .xlsx in file name and increment it but one. The batch file finds the file V1.1, but overwrites it instead of copying newest file with V1.2 in target file name.
How can I get the previous file version first and increment that number?
The file copying task can be done with following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads"
set "TargetFolder=\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport"
for /F "eol=| delims=" %%I in ('dir "%SourceFolder%\*.xlsx" /A-D /B /O-D 2^>nul') do set "NewestFile=%%I" & goto CheckTarget
echo ERROR: Found no *.xlsx file in the folder:
echo "%SourceFolder%"
exit /B 1
:CheckTarget
if not exist "%TargetFolder%\" md "%TargetFolder%\" 2>nul
if exist "%TargetFolder%\" goto GetDateTime
echo ERROR: Failed to access or create the folder:
echo "%TargetFolder%"
exit /B 2
:GetDateTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "DateTime=%%I"
set "BaseName=InventoryReport%DateTime:~0,4%-%DateTime:~4,2%-%DateTime:~6,2% V1"
set "FileNumber=-1"
setlocal EnableDelayedExpansion
for /F "tokens=2 delims=." %%I in ('dir "!TargetFolder!\!BaseName!.*.xlsx" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /X /C:"!BaseName!\.[0123456789][0123456789]*\.xlsx"') do if %%I GTR !FileNumber! set "FileNumber=%%I"
endlocal & set "FileNumber=%FileNumber%"
set /A FileNumber+=1
copy /B /V "%SourceFolder%\%NewestFile%" "%TargetFolder%\%BaseName%.%FileNumber%.xlsx" >nul || exit /B 3
endlocal
The first FOR loop runs in background one more command process with %ComSpec% /c and the command line between the round brackets appended as additional arguments. So executed is with Windows installed to C:\Windows in background:
C:\Windows\System32\cmd.exe /c dir "C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\*.xlsx" /A-D /B /O-D 2>nul
The background command process executes internal command DIR which
searches in the specified directory
just for file names because of option /A-D (attribute not directory)
matching the wildcard pattern *.xlsx and
outputs them in bare format with just file name + extension because of option /B
ordered reverse by last modification date because of option /O-D which means the file name of newest file is output first and the file name of the oldest file is output last.
It is possible that either the source directory does not exist at all or the source directory does not contain any file matching these criteria. DIR would output in this case an error message to handle STDERR of background command process which would be redirected by the command process processing the batch file to own handle STDERR and so displayed most likely in console window. This error message is not wanted as there is a better one output below the FOR loop if there is not found any file for copying. For that reason the error message is redirected already by background command process to device NUL to suppress it.
Read 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 handle STDOUT of background command process and processes this captured output line by line after the executed background cmd.exe terminated itself.
FOR with option /F ignores always empty lines which do not occur in this case. Every other line would be first split up into substrings using normal space and horizontal tab character as delimiters. The line would be ignored if the first space/tab delimited string starts with default end of line character ; (semicolon). Otherwise just the first space/tab delimited string would be assigned to loop variable I and the command respectively command block would be executed next.
A *.xlsx file name can contain one or more spaces. For that reason the FOR option delims= is used to define an empty list of string delimiters to disable line splitting completely. It is unusual, but nevertheless possible, that a file name starts with a semicolon. Therefore FOR option eol=| is also used to define the vertical bar as end of line character which no file name can contain as described by Microsoft in the documentation about Naming Files, Paths, and Namespaces. So the result is that every file name output by DIR in background command process is assigned one after the other completely to the loop variable I.
The file name of the newest file is output first and so its name is assigned to environment variable NewestFile. Then the first FOR loop is exited with using command GOTO to jump to the first line below label CheckTarget as processing the other file names would be a waste of time and CPU power.
There is a meaningful error message output on no *.xlsx file found to copy and batch file processing is exited with exit code 1 to indicate an error condition to parent process starting this batch file.
Next, with having file name of newest file in source folder, an existence check of target folder is done with creating the target folder if not already existing. A meaningful error message is output if the target folder is still not existing because of other computer or storage device is not running or is not reachable at all or the creation of the target folder failed for whatever reason.
The next two command lines get the current date/time in a region independent format and define the base file name for target file using the current date. For a full description of these two lines see my answer on Time is set incorrectly after midnight.
Then the file number is defined with value -1 and delayed expansion is enabled as required for the number comparison done by the next FOR loop.
The third FOR loop is similar to first FOR loop. There is additionally the output of command DIR redirected to handle STDIN of FINDSTR to be filtered for verification if the file name of found file contains really just one or more digits between the dot after V1 and the dot of the file extension, i.e. this part of the file name is a valid number. It can be assumed that FINDSTR outputs the same lines as output by DIR on target folder not used for something different than the Excel files with the defined pattern for the file name. The two dots in name of each file must be escaped with a backslash in case-insensitive interpreted regular expression search string on which the space character is interpreted as literal character because of using /C: and /R and not as OR expression as on omitting /C:. For 100% safety on processing later only correct file names /X is additionally used to output only file names on which entire file name is matched by the search expression.
This time the FOR loop should not assign the entire file name to loop variable I. There is of interest only the string between the first dot after V1 and the file extension .xlsx. For that reason the FOR option delims=. is used to split the file names on dots and option tokens=2 is used to instruct command FOR to assign the second dot delimited string to loop variable I which is the incremented file number.
A simple integer comparison is done to determine if the file number of current file name is greater than file number assigned currently to environment variable FileNumber in which case this greater file number is assigned to the environment variable FileNumber.
The local environment with enabled delayed expansion is no longer needed after knowing the greatest file number of the existing files if there is one at all. So this environment is destroyed which would mean the environment variable FileNumber would have again the number -1 as assigned to the environment variable in initial environment. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. So to pass the current value of FileNumber in current environment to FileNumber in previous environment the command line with endlocal contains additionally the command set "FileNumber=%FileNumber%" which is processed by cmd.exe, for example, to set "FileNumber=12" before executing the command ENDLOCAL. That simple trick is used to pass the greatest file number value to FileNumber in previous environment.
See also:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Single line with multiple commands using Windows batch file
The greatest file number of an existing file or -1 is incremented by one before copying the newest file in source folder with this number and current date in file name to the target folder with verification that the file data were really correct written on target storage media.
The batch file is exited with exit code 3 in case of file copying failed for whatever reason.
Finally the batch file processing ends with explicitly restoring initial execution environment. The last command ENDLOCAL would be not really necessary because of Windows command processor runs it implicit on exiting processing of this batch file as done for example on execution of one of the three commands exit /B.
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.
copy /?
dir /?
echo /?
endlocal /?
exit /?
findstr /?
for /?
goto /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
PS: The greatest possible file number is 2147483647. But a day has only 86400 seconds and more than 65535 files in one directory would be a real problem, too. So the maximum file number 2147483647 should be never reached if no user renames a file in target folder to exceed that maximum number.

How to add time stamp to log lines in log file from batch file output?

I want to add time stamp to log lines from batch output.
Here is my batch file:
#Echo off
SET LOGFILE=MyLogFile.log
call :Logit >> %LOGFILE%
exit /b 0
:Logit
set "affix=%date%_%time%"
set "affix=%affix::=%"
set "affix=%affix:/=%"
xcopy "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%affix%.xlsx"*
Output of log file:
I:\DF\AB\Data.xlsx
1 File(s) copied
I want output log file looking like this:
I:\DF\AB\Data.xlsx
20180831_124500 : 1 File(s) copied
How could this be achieved?
Some more information:
The asterisk at end of target argument string is required for copying the file without prompt. There would be a prompt asking if target is a file or a directory if * would not be used at end of target file name.
xcopy is used because copied is a file from a network drive to local drive.
The output result is as below after running the batch file:
I:\DF\AB\Data.xlsx
08312018_163959.07 :I:\DF\AB\Data.xlsx
1 File(s) copied
May it be as below?
I:\DF\AB\Data.xlsx
08312018_163959.07 1 File(s) copied
So the region dependent date format is MM/DD/YYY and time format is HH:mm:ss.ms.
You're only XCopying one file, so you know that your last line of output on success will be the language dependent string 1 File(s) copied.As you've already limited the script to using a locale dependent %DATE% and %TIME%, I have assumed that language dependency for this task is fine.
Here therefore is a ridiculous looking example script:
#Echo Off
Set "srcfile=I:\DF\AB\Data.xlsx"
Set "destdir=D:\TL\BACKUP"
Set "logfile=MyLogFile.log"
For %%A In ("%srcfile%") Do Set "dstname=%%~nA" & Set "destext=%%~xA"
For /F "Tokens=1-2 Delims=|" %%A In ('
Echo F^|XCopy "%srcfile%" "|%DATE:/=%_%TIME::=%|" /L 2^>Nul ^&^
Echo F^|Xcopy "%srcfile%" "%destdir%\%dstname%_%DATE:/=%_%TIME::=%%destext%" /Y ^>Nul 2^>^&1
') Do (If Not "%%B"=="" Set "_=%%B"
If Defined _ If /I "%%A"=="%srcfile%" ((
Echo %%A&Call Echo %%_%% 1 File(s^) copied)>"%logfile%"))
You should change nothing other than the values for the variables on lines 2-4.However should you be using an existing logfile, you may wish to change > on the last line to >>
You can use echo| set /p=%affix% to eliminate the newline at echo time as:
#Echo off
SET LOGFILE=MyLogFile.log
call :Logit >> %LOGFILE%
exit /b 0
:Logit
set "affix=%date%_%time%"
set "affix=%affix::=%"
set "affix=%affix:/=%"
echo|set /p=%affix% :
xcopy "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%affix%.xlsx"*
Result:
I:\DF\AB\Data.xlsx
2018-08-31_124900 : 1 file(s) copied.
powershell -command "(New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-
Date)).TotalSeconds">LOG.TXT
Although this is not the format you suggested, this format is called epoch time.
The good thing about this format is that it is always a float value.
LOG.TXT will be the name of the log, make sure you are in the right directory.
I suggest following code producing exactly the initially wanted output in log file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=MyLogFile.log"
del "%LOGFILE%" 2>nul
call :Logit >>"%LOGFILE%"
endlocal
exit /B 0
:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
for /F "tokens=1* delims=:" %%I in ('%SystemRoot%\System32\xcopy.exe "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx*" /C /V /Y 2^>nul') do (
if not "%%J" == "" (
echo %%I:%%J
) else (
echo %FileDate% : %%I
)
)
goto :EOF
The region dependent date and time is reformatted to yyyyMMdd_HHmmss by using string substitutions of the dynamic environment variables DATE and TIME as explained in detail for example by the answer on the question: What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean? For a much slower, but region independent solution, to get date/time in a specific format see for example the answer on: Why does %date% produce a different result in batch file executed as scheduled task?
The current date and time in format yyyyMMdd_HHmmss is assigned to the environment variable FileDate used twice on the next line, once in name of target file and once more in output of last line of reformatted output of command XCOPY.
The XCOPY command line used here is for example:
C:\Windows\System32\xcopy.exe "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_20180831_163959.xlsx*" /C /R /V /Y 2>nul
This command line is executed by FOR in a separate command process started by FOR with cmd.exe /C in background. FOR captures all lines written to handle STDOUT of this command process before processing the captured lines.
XCOPY outputs to handle STDOUT the names of the copied files with full path and as last line a summary information. Errors on file copying are written to handle STDERR which are suppressed by redirecting them to device NUL.
Read also the Microsoft article 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 xcopy command line with using a separate command process started in background.
The asterisk * at end of target file name should be within the double quotes of second argument string and not outside because otherwise cmd.exe respectively xcopy.exe has to correct this wrong syntax.
Please note that the trick with * at end of target file name works here by chance because source and target file have same file extension and the source file name is always shorter than the target file name. Otherwise the command would fail or target file gets an unwanted name being a concatenation of target file name + the characters of source file name after n characters of target file name.
In general there are better methods to avoid a halt on prompt which XCOPY requests in case of a single file is copied with a new file name. The letter to answer the prompt can be output first to STDOUT redirected to handle STDIN of XCOPY command as demonstrated language independent in answer on batch file asks for file or folder.
The captured output of XCOPY is processed by FOR line by line with skipping empty lines and lines starting with a semicolon ; as being the default end of line character of option eol= not used here.
The goal here is to output all lines with a full qualified file output by XCOPY in background command process also in this command process, but output the last line with the summary information different by prepending it with the date/time in wanted format, a space, a colon and one more space.
For that reason the default line splitting behavior on spaces/tabs with assigning only first substring (token) to specified loop variable I is modified here by the options tokens=1* delims=:. FOR splits up a line on colons now.
Only the lines with a full qualified file name starting with a drive letter and a colon contain a colon at all. On such lines the drive letter is assigned to specified loop variable I as specified by tokens=1. The rest of a file name line after first colon is assigned without any further splitting to next loop variable according to ASCII table to loop variable J which is here everything after the colon after drive letter.
The summary information line does not contain a colon. For that reason FOR assigns the entire summary information to loop variable I and J holds an empty string.
The loop variable J is never empty on a line with a file name starting with a drive letter and a colon. This fact is used here to determine if the line from XCOPY should be output as is with inserting the removed colon between drive letter and file path + file name + file extension or output the summary information with date/time at beginning.
Please note that this method works only on copying files from a drive with a drive letter. A different method would be necessary for source files with a UNC path.
In fact copying a single file can be done much easier with command COPY instead of XCOPY even from/to a network drive or when source/target file name is specified with a UNC path. COPY has also the options /V and /Y and even /Z like XCOPY. COPY does not create the target directory structure like XCOPY, but this can be done with command MD before. COPY can't overwrite a read-only file as XCOPY can do on using option /R, but this limitation of COPY is most likely not relevant here. And COPY does not copy a file with hidden attribute set. However, in general copying a single file is nevertheless best done with command COPY instead of XCOPY.
So here is one more solution with using command COPY which is faster than the XCOPY solution as there is no reason for executing the file copy in a separate command process, capture any line, split them and output them concatenated again or modified.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=MyLogFile.log"
md "D:\TL\BACKUP" 2>nul
del "%LOGFILE%" 2>nul
call :Logit >>"%LOGFILE%"
endlocal
exit /B 0
:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
echo I:\DF\AB\Data.xlsx
copy /B /V /Y "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx" >nul 2>nul && echo %FileDate% : 1 File(s) copied|| echo %FileDate% : 0 File(s) copied
goto :EOF
This solution has also the advantage that the line output on success or error can be fully customized. COPY exits with a value greater 0 on an error like source file not available or target file/directory is write-protected currently or permanently.
Example of a better output for a single copied file on success or error (subroutine only):
:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
copy /B /V /Y "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx" >nul 2>nul
if not errorlevel 1 (
echo %FileDate% : Copied successfully I:\DF\AB\Data.xlsx
) else (
echo %FileDate% : Failed to copy file I:\DF\AB\Data.xlsx
)
goto :EOF
It is of course also possible to use the command line
set "FileDate=%DATE:/=%_%TIME::=%"
in the batch file to get the date and time in format MMddyyyy_HHmmss.ms if that is really wanted now. I don't recommend this date/time format as it is not good on alphabetical list of all Data_*.xlsx files in directory D:\TL\BACKUP. The list of files sorted by name is with the date/time format yyyyMMdd_HHmmss automatically also sorted by date/time.
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 /?
del /?
echo /?
endlocal /?
exit /?
for /?
goto /?
if /?
md /?
set /?
setlocal /?
xcopy /?
See also:
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file for an explanation of the operators && and ||.

the system cannot find the path specified when running batch file

I am trying to move files after sorting the files from one folder to another folder but I am always getting this exception "The System cannot find the path specified"
Below is my batch command code:
setlocal EnableDelayedExpansion
set destinationFolder=C:\Test_Actual_Queue
rem Create an array with filenames in right order
for /f "tokens=*" %%f in ('dir /b "C:\Test Print Queue\" ^| sort') do (
echo %%f
move %%f %destinationFolder%
)
pause
I am able to sort and display the file names in console but when I try to move to the destination folder , I am getting the above mentioned exception.
Both the folder paths are correct.
I tried debugging and this is the data I am getting in the console:
C:\TestFoder>setlocal EnableDelayedExpansion
C:\TestFoder>set destinationFolder=C:\Test_Actual_Queue
C:\TestFoder>rem Create an array with filenames in right order
C:\TestFoder>for /F "tokens=*" %f in ('dir /b "C:\Test Print Queue\" | sort') do (
echo %f
move %f C:\Test_Actual_Queue
)
C:\TestFoder>(
echo data1.Print_Job
move data1.Print_Job C:\Test_Actual_Queue
)
data1.Print_Job
The system cannot find the file specified.
C:\TestFoder>(
echo data2.Print_Job
move data2.Print_Job C:\Test_Actual_Queue
)
data2.Print_Job
The system cannot find the file specified.
what am I doing wrong here?
Looking forward to your solutions. Thanks in advance.
The command DIR with the arguments /b and "C:\Test Print Queue\" outputs just the names of all non hidden files and directories in specified directory without path. The current directory on execution of the batch file is C:\TestFoder which is a different directory than C:\Test Print Queue. For that reason the command MOVE cannot find the file/directory to move stored in C:\Test Print Queue specified without path in current directory C:\TestFoder and outputs the error message.
The command DIR would output the file/folder names with full path if additionally option /S is used to search also in subdirectories.
One solution is specifying source path also on MOVE command line:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\Test Print Queue"
set "DestinationFolder=C:\Test_Actual_Queue"
set "CreatedFolder="
if not exist "%DestinationFolder%\" (
md "%DestinationFolder%" 2>nul
if not exist "%DestinationFolder%\" (
echo Error: Failed to create folder "%DestinationFolder%"
goto EndBatch
)
set "CreatedFolder=1"
)
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /ON "%SourceFolder%\*" 2^>nul') do (
echo Moving file "%SourceFolder%\%%I" ...
move "%SourceFolder%\%%I" "%DestinationFolder%\"
)
if defined CreatedFolder rd "%DestinationFolder%" 2>nul
:EndBatch
endlocal
pause
Command extensions are explicitly enabled as required for for /F although enabled by default. Delayed environment variable expansion is explicitly disabled as not needed for this task. Files with one or more exclamation marks in file name could not be successfully processed within the FOR loop if delayed environment variable expansion is enabled explicitly although not enabled by default and not needed here. Read this answer for details about the commands SETLOCAL and ENDLOCAL.
The batch file first creates the destination folder if not already existing with verifying if folder creation was successful.
The command FOR executes the command line
dir /A-D-H /B /ON "C:\Test Print Queue\*" 2>nul
in a background command process started with cmd.exe /C.
Command DIR outputs
just non hidden files because of /A-D-H which means all directory entries not having attribute directory or hidden set
in bare format because of /B which means just the file name with file extension and without file path
sorted by name because of /ON
found in directory C:\Test Print Queue matching the wildcard pattern * (any file).
It is possible that the source directory does not exist at all or does not contain any file matching the criteria. The error message output in these cases by DIR is suppressed by redirecting it from handle STDERR to device NUL.
Read also the Microsoft article 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 with option /F as used here captures everything written to handle STDOUT of background command process and then processes the captured text line by line.
Empty lines are ignored by FOR, but DIR with the used options does not output empty lines at all.
Lines starting with ; would be also ignored by default by FOR. File names can start with a semicolon. For that reason option eol=| is used to change the end of line character from semicolon (default) to a vertical bar which a file name can't contain at all.
FOR would split up each line into substrings (tokens) using the default delimiters space and horizontal tab and would assign to loop variable I just the first space/tab delimited string. This splitting behavior is not wanted here and so option delims= is used to define an empty list of delimiters to disable the line splitting and get assigned to I always the entire file name even on containing one or more spaces. tokens=* could be also used to get entire line (= file name) assigned to I.
For each file output by DIR with name and extension, but without path, the name of the file is output and command MOVE is executed to move the file to destination folder without overwriting a file with same name in that folder because of option /Y is not used here.
Finally the batch file deletes the destination folder if it was created by the batch file and if it is still empty because there was no file to move at all.
Another solution would be changing the current directory to source directory.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
cd /D "C:\Test Print Queue" || goto EndBatch
set "DestinationFolder=C:\Test_Actual_Queue"
set "CreatedFolder="
if not exist "%DestinationFolder%\" (
md "%DestinationFolder%" 2>nul
if not exist "%DestinationFolder%\" (
echo Error: Failed to create folder "%DestinationFolder%"
goto EndBatch
)
set "CreatedFolder=1"
)
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /ON 2^>nul') do (
echo Moving file "%%I" ...
move "%%I" "%DestinationFolder%\"
)
if defined CreatedFolder rd "%DestinationFolder%" 2>nul
:EndBatch
endlocal
pause
If command CD fails to change the current directory to source directory because of not existing, the well known error message is output:
The system cannot find the path specified.
Then the batch file jumps to the label EndBatch to restore previous environment and halt batch file execution until user presses any key.
On successfully changing the current directory the batch file continues and with command ENDLOCAL the initial current directory C:\TestFoder is set again as current directory for the command process executing the batch file.
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.
cd /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
md /?
move /?
pause /?
rd /?
set /?
setlocal /?

Why does batch file for "Send To" to get all file names of a folder with space in path not work?

I need to get the names of all files within a folder to do a compare in Excel. I have this working and set up as a Send To feature. However, if the folder I right click on contains a space, it fails. Here is what I have:
#echo off
for /f "tokens=2 delims=:." %%x in ('chcp') do set cp=%%x
chcp 1252>nul
set dirpath=%1
dir %dirpath% dir /b /a | sort > "%dirpath%\FolderContents.txt"
chcp %cp%>nul
exit
Why are the file names not written to "Selected Folder\FolderContents.txt" if folder path contains 1 or more spaces?
There are several mistakes in those few lines of batch code.
The line with command DIR contains the command twice.
As the last paragraph output after several pages on running cmd /? in a command prompt window explains, a file/folder name (and other parameter strings) must be enclosed in double quotes if there is in name or path a space or one of the following characters: &()[]{}^=;!'+,`~
Therefore first argument of batch file on execution is enclosed in quotes if folder name contains a space. set dirpath=%1 assigns the folder name with the quotes to the environment variable dirpath. This results on next line in ""C:\Folder With Space"\FolderContents.txt" which can't be processed by command processor as expected by you.
A number left to redirection operator > could easily result in getting it interpreted as handle number. Although code page numbers are large enough to be not interpreted as handle number, it is better to have a space left to last redirection operator > in your code as %cp% is replaced by a code page number.
A really working and additionally simplified code for your task is:
#echo off
for /F "tokens=2 delims=:." %%x in ('chcp') do set "cp=%%x"
chcp 1252 >nul
dir "%~f1" /A-D /B /ON >"%~f1\FolderContents.txt"
chcp %cp% >nul
exit
The command DIR outputs just
the names of files because of option /A-D
in bare format because of option /B
ordered by name because of option /ON
and without path because of not using option /S for getting a list with all subdirectories included.
%~f1 is replaced by command processor by name of folder with full path without quotes.
Well, determining active code page of this and only this command process and restoring the code page before exiting entire command process is also not really needed in my point of view and therefore second and last but one line are also not necessary.
This variant of above writes the file names with path into the text file.
#echo off
chcp 1252 >nul
del "%~f1\FolderContents.txt" 2>nul
for /F "delims=" %%I in ('dir "%~f1" /A-D /B /ON 2^>nul') do (
>>"%~f1\FolderContents.txt" echo %~f1\%%I
)
exit
The redirection operator >> is now left to command echo to avoid that a file name ending very unlikely with  1,  2, ... is interpreted as handle number. A separating space can't be used here as this space would be also written into the file as being taken by command echo which would result in a trailing space on each file name in text file.
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 /?
chcp /?
dir /?
echo /?
exit /?
for /?
set /?
See also Microsoft article about Using command redirection operators.

Resources