What am I missing?
#echo off
rem - processfiles.bat - Processes all text files in the "source" folder:
rem - Runs %executable% with each text file as parameter
rem - Move the text file to the target folder after processing
set SourceFolder=C:\Users\Chris\Desktop\Yield_Files
set TargetFolder=C:\Users\Chris\Desktop\YieldCleanFiles
set fin=default.set
if not exist "%TargetFolder%" md "%TargetFolder%"
echo Processing text files in %SourceFolder%:
for %%f in (%SourceFolder%\*.txt) do call "C:\Program Files\Yield_Editor\yieldeditor.exe/csvin="(%SourceFolder%\*.txt)"/auto="y"/hide/fin=%fin%"%%f
pause
I have to have the file name I am working on each time I call the .exe
when ran it says it cannot find the file specified, but I am not sure which one it is talking about.
The line
for %%f in (%SourceFolder%\*.txt) do call "C:\Program Files\Yield_Editor\yieldeditor.exe/csvin="(%SourceFolder%\*.txt)"/auto="y"/hide/fin=%fin%"%%f
must be written most likely
for %%f in ("%SourceFolder%\*.txt") do "C:\Program Files\Yield_Editor\yieldeditor.exe" /csvin="%SourceFolder%\*.txt" /auto=y /hide /fin=%fin% "%%~f"
or
for %%f in ("%SourceFolder%\*.txt") do "%ProgramFiles%\Yield_Editor\yieldeditor.exe" "/csvin=%SourceFolder%\*.txt" /auto=y /hide /fin=%fin% "%%~f"
which means your executable must be run with syntax
"Path to Application\Application.exe" "/First Option" /Option2 /Option3 /Option4 "File Name As Fifth Parameter"
File name of application with file extension and path must be in quotes if there is at least 1 space or another character with a special meaning which are output in a command prompt window on last help page displayed when running in command prompt window cmd /?. This is argument 0 for the application.
The first parameter/option is argument 1 for the application. It must be in quotes if the parameter string contains a space or another special character. Options not containing a space must not be quoted, but can be nevertheless also quoted.
Some applications support quoting inside an option with a variable string. An example for such a syntax would be /csvin="%SourceFolder%\*.txt".
Read the help/documentation of the application for details on using it from command line. Most Windows console applications can be executed from within a command prompt window with just /? as parameter for printing command line help to console.
It can be seen here why argument strings with spaces must be enclosed in quotes. The space is the separator for the arguments.
Related
I have a file Staffel artikelen met € bedragen.xlsx and I'd like to copy it with new file name StaffelArtikelen.xlsx into a different directory. I think the Euro sign in file name creates an error.
How can I avoid this error:
Source files cannot be renamed on the source location.
I tried:
set dest1=\\serverb02\DATA\Databeheer\StaffelArtikelen.xlsx
set source=\\serverb02\DATA\Uitwisseling
pushd "%source%"
for /f "tokens=*" %%G in ('dir Staffel artikelen met *.xlsx /b /a-d /od') do SET "newest=%%G"
copy "%newest%" "%dest1%"
The result is a 1 kB file (should be +20 kB) in destination location. I expect StaffelArtikelen.xlsx.
Files in source directory:
DS-Staffel artikelen met%korting variable.xlsx
Staffel artikelen met%korting variable.xlsx
Staffel artikelen met € bedragen variable.xlsx
The latest modified file should be copied with StaffelArtikelen.xlsx as destination file name.
This batch file could be used for this task, too.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "dest1=\\serverb02\DATA\Databeheer\StaffelArtikelen.xlsx"
set "source=\\serverb02\DATA\Uitwisseling"
for /F "eol=| delims=" %%I in ('dir "%source%\*Staffel artikelen met*.xlsx" /A-D-H /B /O-D 2^>nul') do (
copy /Y /B "%source%\%%I" "%dest1%" >nul
goto CopyDone
)
:CopyDone
rem Other commands can be inserted here.
endlocal
A file/folder name must be enclosed in " if it contains a space or one of these characters &()[]{}^=;!'+,`~ as explained by help of Windows command processor output on running in a command prompt window cmd /? on last help page. Other argument strings like variable=value of command set should be also enclosed in double quotes to get all characters inside quoted argument string interpreted as literal characters.
Command FOR with option /F and a command line specified between ' results in starting one more command process in background with %ComSpec% /c and the specified command line. So executed by FOR is with Windows being installed into C:\Windows:
C:\Windows\System32\cmd.exe /c dir "\\serverb02\DATA\Uitwisseling\*Staffel artikelen met*.xlsx" /A-D-H /B /O-D 2>nul
DIR searches
in directory \\serverb02\DATA\Uitwisseling
just for non-hidden files because of option /A-D-H (attribute not directory and not hidden)
matching the wildcard pattern *Staffel artikelen met*.xlsx
and outputs in bare format because of option /B just the names of the files without file path
ordered reverse by last modification date (default) because of option /O-D which means latest/newest modified file first and oldest file last.
The file names are output to handle STDOUT (standard output) of background command process. This output is captured by FOR respectively the command process running the batch file.
It is possible that no directory entry matches the specified search criteria resulting in printing an error message by DIR to handle STDERR (standard error) which is redirected by FOR to STDERR of command process running the batch file. This error message can be suppressed by redirecting it to device NUL by started cmd.exe running in background.
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 with option /F processes the captured standard output of started command process line by line after started cmd.exe terminated itself as follows:
Empty lines are always ignored by FOR, but empty lines do not occur here.
A line is split up by default into substrings using normal space and horizontal tab as delimiters and just first space/tab separated string is assigned to specified loop variable I. This line splitting behavior is not wanted in this case as the file names contain definitely spaces and the entire file name should be assigned to loop variable I and not just the file name part up to first space. For that reason delims= defines an empty list of delimiters to turn off line splitting completely.
Next FOR checks if first substring, i.e. entire file name in this case, starts with default end of line character ; which is a valid character for first character of a file name. For that reason eol=| redefines end of line character to vertical bar which no file name can contain according to Microsoft documentation page Naming Files, Paths, and Namespaces.
BTW: Using just option tokens=* results in running the line splitting, but everything after leading spaces/tabs is assigned to specified loop variable even if there are spaces after first non-space character. It is very unusual that a file name starts with one or more spaces, but it is possible. tokens=* would assign the file name without the leading spaces to loop variable I and further processing fails because of file with missing spaces at beginning of file name is not found or in worst case a different file is processed having same file name as file to process after the removed leading spaces.
So on first iteration of the loop the file name of newest file found by DIR is assigned to the loop variable I without path. This file is copied as binary file to destination directory with new file name StaffelArtikelen.xlsx and overwriting in destination directory a file existing there already with same file name without prompting user.
Note: The file copy can fail if
source file data cannot be read because of missing file reading permissions or
source file is currently opened by an application which prevents reading the file data while opened by the application or
destination file cannot be written because of missing file writing permissions or
destination file exists already and is currently opened by an application which prevents overwriting the file data while opened by the application.
The provided batch file contains no check on file copy being successful or failed.
The command GOTO is used to exit the loop after newest file is copied to ignore all other file names output by DIR and continue batch file processing below the line with label CopyDone.
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 /?
for /?
goto /?
rem /?
set /?
setlocal /?
When dealing with file names containing spaces, you have to double quote them.
Otherwise dir will treat the space separated parts as individual items to list
Staffel
artikelen
met
*.xlsx
As there are presumably no files without extension Staffel, artikelen, met only *.xlsx will return the newest of ALL present excel files and
as you are not showing the source file in your batch it is copied to the new name without being noticed.
This should do as expected:
set dest1=\\serverb02\DATA\Databeheer\StaffelArtikelen.xlsx
set source=\\serverb02\DATA\Uitwisseling
pushd "%source%"
set "newest="
for /f "tokens=*" %%G in ('dir "Staffel artikelen met *.xlsx" /b /a-d /od') do SET "newest=%%G"
if defined newest (
copy "%newest%" "%dest1%"
) else (
Echo no file found matching "Staffel artikelen met *.xlsx"
)
I have the following batch file to make git diff invoke spreadsheet compare UI in windows. So I'm trying to pass the git diff's 2nd (old file) and 5th (new file) arguments to spreadsheet compare in order to make it compare the file using git diff.
So now, this batch file only successfully handles files with NO spaces in the file names, it CANNOT handle files with spaces in the file names.
What code should I add to this script to make this batch code handles file with spaces:
#ECHO OFF
set path2=%5
set path2=%path2:/=\%
ECHO %2 > tmp.txt
dir %path2% /B /S >> tmp.txt
C:/"Program Files"/"Microsoft Office"/root/vfs/ProgramFilesX86/"Microsoft Office"/Office16/DCF/SPREADSHEETCOMPARE.EXE tmp.txt
It currently throw errors like this:
Unhandled Exception: System.ArgumentException: Illegal characters in path.
at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
at System.IO.Path.GetFileName(String path)
at ProdianceExcelCompare.Form1.StatusReady()
at ProdianceExcelCompare.Form1.Init()
at ProdianceExcelCompare.Form1..ctor(String instructionFile)
at ProdianceExcelCompare.Program.Main(String[] args)
fatal: external diff died, stopping at London comparison.xlsx
See the following answers on Stack Overflow:
How to set environment variables with spaces?
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
They explain the recommended syntax set "VariableName=variable value" to define an environment variable and the reasons recommending this syntax.
Why does ECHO command print some extra trailing space into the file?
It explains why the space character left to redirection operator > on an ECHO command line is also written into the file as trailing space and how to avoid this safely on variable text written into the file.
See also Microsoft documentation about Using command redirection operators.
On other command lines than ECHO a space left to > is usually no problem.
It is in general wrong to use multiple times " within an argument string like a file or folder path. There should be just one " at beginning and one " at end. This is explained by help of Windows command processor output on last help page on running in a command prompt window cmd /?.
The Microsoft documentation about Naming Files, Paths, and Namespaces explains that the directory separator on Windows is \ and not / and therefore / should not be used in batch files on Windows in file/folder paths.
The help output on running in a command prompt window call /? explains how the arguments of a batch file can be referenced with which modifiers.
The code rewritten according to information posted above and on the referenced pages:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "path2=%~5"
set "path2=%path2:/=\%"
>"tmp.txt" echo %2
dir "%path2%" /B /S >>"tmp.txt" 2>nul
"%ProgramFiles%\Microsoft Office\root\vfs\ProgramFilesX86\Microsoft Office\Office16\DCF\SPREADSHEETCOMPARE.EXE" "tmp.txt"
endlocal
The first line in tmp.txt contains the second argument as passed to the batch file, i.e. without or with surrounding double quotes.
The following code is necessary to write the second argument safely always without " into file tmp.txt even on second argument passed to the batch file is "Hello & welcome!":
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "path2=%~5"
set "path2=%path2:/=\%"
set "Argument2=%~2"
setlocal EnableDelayedExpansion
echo !Argument2!>"tmp.txt"
endlocal
dir "%path2%" /B /S >>"tmp.txt" 2>nul
"%ProgramFiles%\Microsoft Office\root\vfs\ProgramFilesX86\Microsoft Office\Office16\DCF\SPREADSHEETCOMPARE.EXE" "tmp.txt"
endlocal
>tmp.txt echo %~2 cannot be used as not working for something like "Hello & welcome!". Windows command processor would interpret the first string separated by normal space, horizontal tab, comma, equal sign, or no-break space (in OEM code pages) delimited string after & as command or application to execute as described by single line with multiple commands using Windows batch file.
"tmp.txt" could be written everywhere in both batch files also with just tmp.txt. But it is never wrong to enclose the complete file/folder argument string in double quotes even on not being really necessary because of the string does not contain a space or one of these characters &()[]{}^=;!'+,`~. So it is good practice to always enclose a complete file/folder argument string in double quotes. For example running a replace on both batch files searching for tmp.txt and using as replace string %TEMP%\%~n0.tmp would result in using instead of tmp.txt in current directory a temporary file with name of batch file as file name and file extension .tmp in directory for temporary files independent on what is the name of the batch file and what is the path of the directory for temporary files.
The last suggestion is reading this answer for details about the commands SETLOCAL and ENDLOCAL.
The temporary file should be also deleted finally before reaching an exit point for batch file execution.
You can use quotes as below:
It treats the string in quotes as a title of the new command window. So, you may do the following:
start "" "yourpath"
Found it in the below link :
https://ccm.net/forum/affich-16973-open-a-file-with-spaces-from-batch-file
There's something I'm not understanding about the way that the batch for command works, when used with /f and a command.
I was trying to loop on the output of an executable. The executable turned out to not exist, which is fine.
But instead of getting the expected error about a bad path, the script seemed to spontaneously incorrectly tokenize the string. This led me down a rabbit hole of thinking that I had formatted the for loop and/or used quotes or back-ticks incorrectly.
Placing an executable at the location fixes the issue, making it appear like the path string tokenizing is dependent on the existence of an actual file at that path.
Why does the following batch file
#echo off
for /f "usebackq delims=" %%i in ( `"C:\Program Files\BOGUS_PATH\FAKE.exe"`) do (
rem
)
output 'C:\Program' is not recognized as an internal or external command, operable program or batch file. instead of The system cannot find the path specified. ?
First off, a pet peeve of mine, the usebackq is not needed. The simpler and functionally equivalent command is
#echo off
for /f "delims=" %%i in ('"C:\Program Files\BOGUS_PATH\FAKE.exe"') do (
rem
)
The only time usebackq is required is when you are trying to read a file, and the file path contains token delimiters like space.
for /f "usebackq" %%A in ('"some file.txt"')
All other situations can use the "normal" forms
for /f %%A in (fileName) do...
for /f %%A in ("string") do...
for /f %%A in ('someCommand') do...
Now to answer your actual question :-)
The presence or absence of the file does not actually alter the parsing.
First you need to understand the possible error messages when you try to execute a non-existent program from the command line. There are three possibilities:
1) If the "command" includes a colon anywhere other than the 2nd position, then you may get the following
c:\test\>abc:fake.exe
The filename, directory name, or volume label syntax is incorrect.
c:\test\>abc:\test\fake.exe
The filename, directory name, or volume label syntax is incorrect.
Or sometimes you get the next possible error. The rules as to which message you get are not obvious to me, and I don't feel like it is important enough to figure out the exact rules.
2) If the "command" includes a backslash that indicates a path, and the path is not valid, then you get
c:\test\>c:bogus\fake.exe
The system cannot find the path specified.
c:\test\>\bogus\fake.exe
The system cannot find the path specified.
c:\test\>abc:bogus\fake.exe
The system cannot find the path specified.
3) If the executable file cannot be found, and either the "command" does not include path info or the provided path is valid, then you get
C:\test\>fake.exe
'fake.exe' is not recognized as an internal or external command,
operable program or batch file.
C:\test>c:\test\fake.exe
'c:\test\fake.exe' is not recognized as an internal or external command,
operable program or batch file.
The last conundrum is why "C:\Program Files\BOGUS_PATH\FAKE.exe" gives error 3) instead of 2)
If you execute from the command line with quotes then you get the expected result:
C:\test>"C:\Program Files\BOGUS_PATH\FAKE.exe"
The system cannot find the path specified.
If you execute from the command line without quotes, then you get this expected result:
C:\test>C:\Program Files\BOGUS_PATH\FAKE.exe
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
Your script includes quotes, so you would expect the former, but you get the latter.
There are two parts to understanding this.
First off, FOR /F executes the command by executing
C:\WINDOWS\system32\cmd.exe /c "C:\Program Files\BOGUS_PATH\FAKE.exe"
You might think your "command" is quoted, but cmd.exe plays games with quotes, which is the 2nd part to the explanation. The cmd.exe quote rules are described in the help:
c:\test\>help cmd
Starts a new instance of the Windows command interpreter
...
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
Most of the conditions under 1. are met except for the last one - the string does not point to a valid executable. So the 2. rules are used, thus the command becomes
C:\Program Files\BOGUS_PATH\FAKE.exe
And now the result makes perfect sense - the "command" breaks at the space.
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
If you are trying to execute a command with FOR /F, and the command must be enclosed within quotes, then you must put an extra set of quotes around the entire command.
for /f "delims=" %%A in ('""c:\some path\file.exe" "arg 1" arg2"') do ...
But the above will have problems if there are poison characters in the path or quoted argument, because the extra set of quotes mess up the quoting. You could escape the poison characters like
for /f "delims=" %%A in ('""c:\some path\file.exe" "this^&that" arg2"') do ...
But I find that awkward because the command you use on the command line does not match the command you use in a FOR /F. I prefer to escape the extra set of enclosing quotes so that whatever works on the command line also works within FOR /F.
for /f "delims=" %%A in ('^""c:\some path\file.exe" "this&that" arg2^"') do ...
FOR is an internal command of Windows command processor cmd.exe. The usage of command FOR with option /F and a command to execute results in starting with %ComSpec% /c and the specified command line as further parameter(s) one more command process running in background.
Everything output to handle STDOUT is captured by current command process executing the batch file with command FOR and is processed by FOR after termination of additionally started cmd.exe line by line.
The output to handle STDERR of additionally started command process is redirected to handle STDERR of current command process resulting in getting it displayed in console window in case of STDERR is neither in additionally started command process nor in current command process redirected to a different handle, file or device NUL.
This can be seen on using free Sysinternals (Microsoft) tool Process Monitor.
Download the ZIP file containing Process Monitor.
Extract the ZIP file into any local directory.
Start Procmon.exe with Run as administrator which is required to run Process Monitor.
In Process Monitor Filter dialog displayed first Add the entry Process Name is cmd.exe and close the dialog with button OK.
Toggle off the last five options on toolbar with the exception of the filling cabinet symbol with the tooltip Show File System Activity.
Press Ctrl+X to clear the list.
Run the batch file.
Switch back to Process Monitor and press Ctrl+E to stop capturing.
Make sure to see among the column Process Name also the column PID. Right click on list header and left click on context menu item Select Columns... if the PID column is not already displayed, check Process ID and close dialog window with OK. I recommend to move the column PID using drag and drop of column header PID right to column Process Name.
Scroll up to begin of list of captured file system activities and look for the line where a cmd.exe with a different process identifier is displayed than the first one. That second cmd.exe with different PID is the Windows command processor instance started by FOR.
Right click on this second cmd.exe, left click in context menu on Properties, select tab Process and look on Command Line showing:
C:\Windows\system32\cmd.exe /c "C:\Program Files\BOGUS_PATH\FAKE.exe"
Okay. So we know now what FOR respectively cmd.exe does on running a command to capture the output of this command. Close the properties window.
Look on list and it can be seen which file system accesses are made by second cmd.exe to find the specified executable which does not exist in specified and existing directory. It searches next for C:\Program Files\BOGUS_PATH\FAKE.exe.* also with no such file as result.
The first argument string is split up now on argument separators to which belongs the space character among some others like comma or equal sign. So the first argument string C:\Program Files\BOGUS_PATH\FAKE.exe is argument separated once more resulting in interpreting C:\Program now as first argument string.
cmd.exe checks if the directory path being now just C:\ is existing which is true like it was before for directory path C:\Program Files\BOGUS_PATH.
Next is checked if there is a file matching the pattern C:\Program.* which returns no such file. Then cmd.exe checks if there is the file C:\Program which again results in no such file.
Now second cmd.exe gives up finding a file which could be executed and outputs the error message.
It is indeed confusing on having specified the executable with full path enclosed in double quotes as required because of the space to get not output
'C:\Program Files\BOGUS_PATH\FAKE.exe' is not recognized as an internal or external command
operable program or batch file.
This would be expected, but output is the error message:
'C:\Program' is not recognized as an internal or external command
operable program or batch file.
I think, parsing the first argument string once again using the argument separators on not finding a file to execute is done for example if a user runs cmd.exe with the command (argument)
"C:\Programs\MyApplication.exe C:\Temp\FileToProcess.txt"
instead of
"C:\Programs\MyApplication.exe" "C:\Temp\FileToProcess.txt"
So the question is:
Is this automatic argument parsing good or not good?
Well, this is difficult to answer. It is at least somehow documented in help of cmd.exe as described by dbenham in his answer.
What you probably need to do is to enclose your executable command in doublequotes:
#Echo Off
For /F Delims^=^ EOL^= %%A In ('""C:\Program Files\BOGUS_PATH\FAKE.exe""'
)Do Echo(%%A
Pause
Where the inner set of doublequotes is to protect the known space in the path, and the outer set to protect the inner ones when the content is passed to the cmd.exe instance, that the parenthesised command is run in.
Then to prevent any error message from the command, you should redirect the STDERR output to NUL:
#Echo Off
For /F Delims^=^ EOL^= %%A In ('""C:\Program Files\BOGUS_PATH\FAKE.exe" 2>Nul"'
)Do Echo(%%A
Pause
If the executable does not exist, this will supress the error message, The system cannot find the path specified. and the Do command will not run, (as there is no STDOUT to process).
So I am creating a batch file to store a selected folder's path into a text file to refer to it later on using another batch script...
I created registry entries to include a right-click context menu for any folder which triggers this specific batch file.
Basically When you right click a folder and click "Send to Script" it is supposed to copy the whole path / location of the right clicked folder.
To do so I am using the following command:
SET TargetDir=%~1
I also tried using %1and I also tried using the following code with delimiters
FOR /f "delims=;" %%a in ("%~1") do (
#echo %%a
)
The problem is that CMD is automatically trimming everything after the first space and since this is a path I am copying, I want to keep all the spaces and the path as is
Ex. If I use the command on a Folder such as "C:/folder/subfolder" the copyng is done correctly But If I use the command on a Folder such as "C:/folder/sub folder" the copying is done incorrectly and will only give me "C:/folder/sub" removing all the rest found after the first space detected.
Registry Entries
[HKEY_CLASSES_ROOT\Directory\shell\send-to-script]
"MUIVerb"="Send To Script"
"SubCommands"="sendscript"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\sendscript]
#="Send To Script"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\sendscript\command]
#="C:\\scripts\\pathtotext.bat %1"
Thanks for your help
You should change your registry key [...\sendscript\command] to
#="C:\\temp\\blob.bat \"%1\""
And in your batch you should use
REM The DisableDelayedExpansion is for preserve "!" in path names
setlocal DisableDelayedExpansion
SET "TargetDir=%~1"
setlocal EnableDelayedExpansion
(
echo target is !TargetDir!
) > C:\scripts\target.log
if "reseverdFolder" == "!TargetDir!" echo This folder is reserved
The batch file is not correct registered. For example look with regedit on value of registry key:
HKEY_CLASSES_ROOT\rtffile\shell\open\command
The value is displayed as:
"%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE" "%1"
So there are two arguments which are both enclosed in double quotes:
Argument 0 is the application to run with full path, file name and file extension enclosed in double quotes as path contains a space character.
Argument 1 is the name of the RTF file referenced with %1 passed by Windows Explorer with full path, file name and file extension which of course can also contain a space or any of the characters &()[]{}^=;!'+,`~ which require also enclosing entire argument in double quotes as output by cmd.exe on running it in a command prompt window with cmd /? on last help page.
So you need in your *.reg file used to import into Windows registry:
#="\"C:\\scripts\\pathtotext.bat\" \"%1\""
This string value is displayed in registry editor as:
"C:\scripts\pathtotext.bat" "%1"
And then you can use %1 or %~1 in your batch file as explained by help of command CALL output on running in a command prompt window on execution of call /?.
SET "TargetDir=%*"
Since the parameter apparently being supplied is C:\folder\sub folder then %~1 selects only the first supplied parameter of what cmd sees as two parameters.
%* means "the whole tail"
and echoing %* should show you exactly what cmd is seeing.
I'm trying to understand a pretty complex (to me it is anyhow) batch file that somebody else has written.
The person who wrote it is no longer around so it's down to me to try and figure out what the hell is going on.
To start with, I need to understand what this line is doing: -
for /f "skip=4 tokens=1,5" %%A in ('dir /tc /o-d %i%\PICKS*') do (
Can anybody help?
1. How to get help on Windows commands?
Help on any Windows standard command can be get by running the command with parameter /? from within a command prompt window.
Try it out with executing in a command prompt window dir /? and for /?.
Or run this long command line with multiple commands:
dir /? >"%UserProfile%\Desktop\Help_on_DIR_FOR.txt" & for /? >>"%UserProfile%\Desktop\Help_on_DIR_FOR.txt" & %SystemRoot%\Notepad.exe "%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
This is like running the three command lines:
dir /? >"%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
for /? >>"%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
%SystemRoot%\Notepad.exe "%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
It runs command DIR to output its help with redirecting the help into file Help_on_DIR_FOR.txt on the desktop. Next it runs command FOR to output its help with appending it to file Help_on_DIR_FOR.txt. And last Windows Notepad is started to display the file Help_on_DIR_FOR.txt.
See the Microsoft documentation about Using command redirection operators for the details on the redirection operators > and >>.
See the Wikipedia article about Windows Environment Variables for the details on the predefined environment variables UserProfile and SystemRoot.
Other sources for help on Windows commands are:
Microsoft's command-line reference
SS64.com - A-Z index of the Windows CMD command line
2. What to know about text encoding of batch files?
The text in a batch file is encoded using one byte per character. That means on using Windows Notepad to create/edit a batch file it is necessary to make sure to save the batch file using ANSI encoding.
Text files using a text encoding with only one byte per character are limited to 256 characters. So there must be a table to define which byte value represents which character. There are many such tables as the mankind on this planet uses more than 256 characters. For text encoding the used code page defines the table used for character encoding.
In a Windows command prompt window (console) is used by default an OEM (Original Equipment Manufacturer) code page while in Windows GUI applications like Windows Notepad is used by default a Windows (ANSI) code page.
Which code page is used depends on Windows region setting as defined for the used account. For example in North American countries code page 437 is used for console and Windows-1252 by Windows GUI applications while in Western European countries code page 850 is used for console and also Windows-1252 by Windows GUI applications.
Run in a command prompt window the command chcp to get displayed which code page is used on console according to region (country) set for your account.
The knowledge about code pages is important on writing a batch file using a Windows GUI editor which contains text with characters with a code value greater 127 decimal because for the upper half of the 256 characters the used code page defines which value represents which character.
It can be easily seen that all non ASCII characters of the help are displayed wrong in Notepad in case of not using English Windows and looking on help output for DIR and FOR to console redirected into a text file viewed with Windows Notepad because of the text file was not created using the Windows (ANSI) code page as expected by Notepad.
3. What does the command line ask for?
Let us analyze the command line
for /f "skip=4 tokens=1,5" %%A in ('dir /tc /o-d %i%\PICKS*') do (
by executing following commented batch file:
#echo off
rem Setup a local environment for the commands below.
setlocal EnableExtensions DisableDelayedExpansion
rem Define the directory to process.
set "i=%TEMP%\UnderstandingDirFor"
rem Create this directory temporarily.
md "%i%" 2>nul
rem Create a file with PICKS at beginning of file name in the directory.
echo PICKS_SampleFile.tmp>"%i%\PICKS Sample File.tmp"
rem Copy the batch file into directory with PICKS in file name.
copy "%~0" "%i%\PICKS_ExampleFile.bat" >nul
rem Copy another file into directory with PICKS in file name.
copy "%SystemRoot%\System32\cmd.exe" "%i%\PICKS cmd.exe" >nul
rem Copy a file into directory with a file name not starting with PICKS.
copy "%SystemRoot%\System32\sort.exe" "%i%" >nul
cls
echo First output is from:
echo/
echo dir "%%i%%\PICKS*"
echo/
dir "%i%\PICKS*"
echo/
pause
echo/
echo ===============================================================================
echo/
echo Second output is from:
echo/
echo dir /tc /o-d "%%i%%\PICKS*"
echo/
dir /tc /o-d "%i%\PICKS*"
echo/
pause
echo/
echo ===============================================================================
echo/
echo Third output is from:
echo/
echo for /f "skip=4 tokens=1,5" %%%%I in ('dir /tc /o-d "%%i%%\PICKS*"')
echo/
for /f "skip=4 tokens=1,5" %%I in ('dir /tc /o-d "%i%\PICKS*"') do (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable J = %%J
)
echo/
pause
echo/
echo ===============================================================================
echo/
echo Fourth output is from:
echo/
echo for /F "skip=5 tokens=1,4*" %%%%I in ('dir /A-D /O-D /TC "%%i%%\PICKS *" 2^^^>nul')
echo/
for /F "skip=5 tokens=1,4*" %%I in ('dir /A-D /O-D /TC "%i%\PICKS *" 2^>nul') do (
if /I "%%J" == "PICKS" (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable K = %%K
)
)
echo/
pause
rem Delete the created directory with the four files.
rd /S /Q "%i%"
rem Purge the local environment and restore initial environment.
endlocal
Note: The batch file uses "%i%\PICKS*" instead of %i%\PICKS* to work also for directory paths containing a space character or one of these characters &()[]{}^=;!'+,`~. The path of the directory for temporary directories and files referenced with %TEMP% included in %i% could contain a space character which requires the usage of double quotes.
The first output into console window is from:
dir "%i%\PICKS*"
It can be seen on output that the files (or directories) matching the wildcard pattern PICKS* are listed with last modification date at beginning and in order as used internally by the file system.
The file sort.exe is not output because it's file name does not start with PICKS.
What DIR outputs exactly depends on language of Windows. But the first five lines are the header and the last three lines are the summary in language of Windows.
The second output into console window is from:
dir /tc /o-d "%i%\PICKS*"
The output of the list is now in reverse order by date/time because of option /o-d using creation date because of the option /tc.
By default the file/directory with the oldest date is output first and the file/directory with the newest date is output last on using /od. The order is reverse with /o-d which means output is first the newest and last the oldest file/directory.
It can be seen on comparing the two outputs that the creation date of all copied files is newer than their last modification date.
The reason is that the creation date is the date a file/directory was created in current directory and NOT when the file itself was created the first time anywhere.
For that reason the creation date is most often not really useful and using the creation date might be also not useful here. But this can't be determined without knowing what the entire batch file is written for.
The third output into console window is from:
for /f "skip=4 tokens=1,5" %%I in ('dir /tc /o-d "%i%\PICKS*"') do (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable J = %%J
)
FOR starts a new command process in background using cmd.exe with option /C for an automatic close for executing the specified command line dir /tc /o-d "%i%\PICKS*". To be more precise there is executed by FOR:
%ComSpec% /c dir /tc /o-d "C:\Users\UserName\AppData\Local\Temp\PICKS*"
ComSpec is a predefined environment variable with full qualified file name of Windows command processor which is usually C:\Windows\System32\cmd.exe.
The output of the entire command process to handle STDOUT is captured by FOR and processed line by line after background command process finished and closed itself.
for /F ignores by default all empty lines. The other lines are by default first split up into substrings using normal space and horizontal tab as string delimiters which means no substring contains any space/tab. If the first space/tab delimited substring starts now with a semicolon being the default end of line character, the line is not further processed by FOR and so also ignored like an empty line. Then the first substring is assigned to specified loop variable and the single command or the command block is executed next.
Command FOR is instructed with option skip=4 to skip the first four lines of captured output and start processing the output with line five which is the last empty line of header of DIR output.
Command FOR is instructed with option tokens=1,5 to not assign only first space/tab delimited string to specified loop variable I, but also the fifth space/tab delimited string to the loop variable being the next one after I in ASCII table if there is a fifth substring (= token) at all which is loop variable J.
This substrings/tokens to loop variable assignments feature is the reason why loop variables are case-sensitive while environment variables are not case-sensitive.
Command FOR ignores a line if the first specified token as specified with tokens= cannot be determined from a line. But it does not ignore lines on which not all strings of interest can be found in line as it can be seen on third output.
What fifth space/tab delimited string assigned to loop variable J should be in original batch code depends on what the directory referenced with %i% contains - files or directories - and how the file/directory names starting case-insensitive with PICKS really look like.
For the example I assumed PICKS* matches files with file name starting with PICKS (note the space at end) and more characters not containing a space character.
By looking on third output it can be seen that the names of the three PICKS* are not assigned all to loop variable J as expected and the summary lines are also processed.
I suppose this does not happen in original batch file because of file/directory names do not contain another space character after PICKS and the FOR loop is exited with command GOTO after processing first file/directory name.
The fourth output into console window is from:
for /F "skip=5 tokens=1,4*" %%I in ('dir /A-D /O-D /TC "%i%\PICKS *" 2^>nul') do (
if /I "%%J" == "PICKS" (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable K = %%K
)
)
This is an improved version suitable to output only the creation date and the name of the files in temporary directory matching the wildcard pattern.
The DIR option /A-D is added to ignore subdirectories and get just a list of files of which file name is matched by the wildcard pattern.
The wildcard pattern is modified to PICKS * which results in command DIR not outputting anymore a line for file PICKS_ExampleFile.bat because of the underscore instead of the space in the file name.
On DIR command line 2^>nul is appended for suppressing a perhaps output error message. Command DIR outputs an error message to handle STDERR not processed by FOR and therefore written to console when it can't find a file matching the wildcard pattern in the specified directory or when the directory does not exist at all. This error message is redirected to device NUL to suppress it. The redirection operator > must be escaped here with caret character ^ to be interpreted first as literal character on parsing FOR command line by Windows command interpreter, but as redirection operator on execution of DIR command line in background command process.
The skip option of command FOR is modified to skip=5 to skip the first 5 lines, i.e. the entire header block of command DIR.
The tokens option of command FOR is modified to tokens=1,4*. This results on lines containing a file name matching the wildcard pattern PICKS * in assigning creation date of file to loop variable I, PICKS to loop variable J and everything after the space(s) after PICKS in line without further splitting up on spaces/tabs to loop variable K.
The IF condition compares case-insensitive, because of /I, the string assigned to loop variable J with PICKS and outputs creation date and file name only if the compared strings are equal. This IF condition filters out the summary lines on output.
Because of * in tokens=1,4* the entire file name with the exception of PICKS is output even if the file name contains more space characters like in Sample File.tmp.
4. How to debug a batch file?
Windows command processor outputs by default each command line respectively command block defined with ( ... ) after processing it before execution. The output of the command lines/blocks before execution can be disabled with using #echo off at top of the batch file whereby # prevents already the output of this command line.
But on debugging a batch file it is often necessary to see what is really executed. This can be achieved with either removing #echo off, or changing it to #echo ON or comment this command line out with #rem #echo off or with ::#echo off whereby the last solution changes the command line into an invalid label line.
It is of course also possible to keep #echo off at top of the batch file and use inside the batch file echo on and some lines below #echo off to get displayed on execution just the commands between those two command lines to debug just a specific block of the batch file.
And for debugging the batch file it should not be executed by just double clicking on it in Windows Explorer. This results in starting cmd.exe implicit with option /C for executing the batch file and close the command process and therefore also the console window immediately on exiting the batch file execution.
The closing of the console window is not good for debugging a batch file because the executed commands can't be seen and also the error message output by Windows command processor on detecting a syntax error resulting in an immediate exit of batch file processing is not readable with console window automatically closed.
For that reason debugging a batch file should be done by opening a command prompt window which results in implicit execution of cmd.exe with option /K to keep command process running and its console window open after execution of the batch file which is entered with its file name and if needed with full path in the command prompt window for execution.
Another advantage of running the batch file from within a command prompt window is the possibility to use UP and DOWN keys to re-select a once entered command line or string entered on a user prompt within batch file with set /P from input buffer of command process.
That's a for-loop with the /f option. It loops through a file or command, in this case the command dir /tc /o-d %i%\PICKS*.
Take a look at what dir /tc /o-d word* on its own does (read up on options wtih dir /?), it'll make understanding the loop easier.
Basically, the output of the command is dir (show files in directory) with the timefield showing the creation date and all files sorted by date in descending order. The command applies to the folder %i% (a variable set beforehand). "PICKS*" is the 'mask', i.e. all files starting with "PICKS" are shown. That's what the wildcard (asterisk) means (any characters can follow).
The for-loop now takes that output and assigns it to %%A (temporary iteration variable). It skips the first 4 lines (in the case of dir just header that you don't want to have), and for each following line (the files) picks out the first and the fifth word (delimited by spaces, as space is the default delimiter). %%A is now the date, %%B (the second specified token/fifth word) the second word of the filename (if there is any). Those variables can be used in the do (... part.