How to escape in windows command shell? - windows

I've a file in c:\Program Files directory named tmp.txt
For each line in tmp.txt, I would like to execute a command.
I am trying to use command prompt for loop but it is not able to find the tmp.txt. Note that I've to run this command out of c:\Program Files directory.
Here is how I am trying:
C:\>for /F %i in ("c:\Program Files\tmp.txt") do echo "%i"
the output is:
C:\>echo "c:\Program"
"c:\Program"
which means that for is considering "c:\Program" as parameter and passing it to do
If I put file in c:\, and run for loop as-
C:\>for /F %i in (c:\tmp.txt) do echo "%i"
it works just fine
So my question is- how do I pass full path to for loop so that for consider it as file

Use the usebackq option to for /f:
for /f "usebackq" %i in (...)
This changes the semantics of the various quote characters, as the help states as well:
usebackq - specifies that the new semantics are in force,
where a back quoted string is executed as a
command and a single quoted string is a
literal string command and allows the use of
double quotes to quote file names in
file-set.

Related

Get result of command with quoted arguments within single quote command evaluation in Windows batch

How do you pass quoted arguments to an executable in a single-quoted evaluation such as FOR /F "delims=" %%i IN ('"executable path" arg1 "arg2 which contains spaces"') do ...?
As per many of the answers here, I'm trying to use the output of a console app in a Windows batch script, using the single quotes to get the console to evaluate it.
However, I need to quote some of the arguments I want to pass to that executable, which also needs to be quoted, as the path contains spaces as well.
But when I do that, the quoting around the executable path breaks.
Here is the would-be line:
FOR /F "delims=" %%i IN ('"!PathToExe!" action^=sanitize string^="!album!"') DO set "falbum=%%i"
(Both !PathToExe! and !album! contain spaces. It seems like I need to escape the equal signs here, hence the circumflexes. Delayed expansion is on)
The above results in "Outcome A": Quoting is broken for the exe path
'<Part of path to exe>' is not recognized as an internal or external command, operable program or batch file.
I've tried different combinations of different quote usages and escapings, but haven't found a way to make it work.
Here are some attempts and their results:
FOR /F "delims=" %%i IN ('"!PathToExe!" action^=sanitize string^=!album!') DO set "falbum=%%i")
No quotes around !album! results in "Outcome B": As expected, only the first word gets passed along with string=, all the other words get scattered as individual arguments.
FOR /F "delims=" %%i IN ('"!PathToExe!" action^=sanitize string^=^"!album!^"') DO set "falbum=%%i")
FOR /F "delims=" %%i IN ('^"!PathToExe!^" action^=sanitize string^=^"!album!^"') DO set "falbum=%%i")
Trying to escape the quotes for the string= argument or both exe path and string arg: Outcome A (still breaks the quoting for the exe path, gives:)
'<Part of path to exe>' is not recognized as an internal or external command, operable program or batch file.
FOR /F "delims=" %%i IN ('"!PathToExe!" action^=sanitize string^=^'!album!^'') DO set "falbum=%%i")
Using escaped single quotes: Outcome B again
FOR /F "delims=" %%i IN ('"!PathToExe!" action^=sanitize string^='"!album!") DO set "falbum=%%i")
Ending the single quote before the string= value and simply having it in quotes after that seems to result in everything being taken as a single first argument (command/path):
The system cannot find the file '"<path to exe>" action=sanitize string='"<Album var with spaces and whatnot>".
It does not matter whether the quotes are part of the variables or literally typed in the IN ('...') line.
Simple testing:
You could test this behavior if you copied %SystemRoot%\System32\cmd.exe to a directory with spaces, e.g. C:\folder with spaces\ and pasted the following script there:
#echo off
setlocal EnableDelayedExpansion
set PathToExe="%~dp0cmd.exe"
REM Toggle the next line to compare between quoted path with spaces and path without quotes or spaces:
REM set PathToExe=cmd.exe
set string=%~dp0
FOR /F "delims=" %%i IN ('!PathToExe! /C CD C:\windows\system32 ^& dir !string!') DO set "fstring=%%i"
echo !fstring!
pause
This should illustrate the challenge of having two quoted sections in one statement.
If the !string! variable remains unquoted, you'll get "The system cannot find the file specified.".
If the quotes of the !PathToExe! variable break, you'll see something like "'C:\folder' is not recognized as an internal or external command, operable program or batch file.".
The for /F command, when used to capture and parse command output, actually uses cmd /C to execute that command, which handles quotation marks in a particular way. From its help (cmd /?):
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.
This means that:
for /F "delims=" %%I in ('"executable path" arg1 "arg2 which contains spaces"') do (…)
actually tries to execute the command line:
cmd /C "executable path" arg1 "arg2 which contains spaces"
leading to:
executable path" arg1 "arg2 which contains spaces
which is obviously invalid syntax.
To overcome this issue provide an additional pair of quotes:
for /F "delims=" %%I in ('^""executable path" arg1 "arg2 which contains spaces"^"') do (…)
It is a good idea to escape these additional quotes (by ^"), so there is no need to alter any potential escape sequences.

Batch script - usage of FOR /F with spaces in command and usebackq

Pretty new to batch scripting. I need the output of a command to be assigned to a variable. I got to know about the FOR /F command. My command has spaces and also double quotes. So I tried using the usebackq option. But it fails with the error on the space.
for /F "usebackq delims=" %%G IN (`"C:\Program Files (x86)\ABC\DEF\XYZ.exe" Get /a "P1=D2" /b "Q1=D3" /c "D5"`) DO (
SET var=%%G
)
ECHO %var%
pause
The failure :
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
As per other questions on StackOverflow, I did everything right - usebackq for the usage of back tick, demlims= to ignore spaces as a delimiter. I am not sure why it is still failing. Any help is appreciated!
A command line within ' or within ` on using usebackq is executed by FOR with option /F respectively the Windows command process cmd.exe processing the batch file in background with %ComSpec% /c and the command line appended as additional arguments.
ComSpec is a Windows environment variable predefined with %SystemRoot%\System32\cmd.exe as system environment variable.
The help output on running cmd /? in a command prompt window explains how the strings are interpreted by Windows command processor after option /C (execute command line and close) or option /K (execute command line and keep running).
The usage of backquotes is not needed here as the command line to execute does not contain itself one or more arguments with '. So it is possible to enclose the command line in '.
It must be taken into account for the command line to execute by FOR, how the Windows command interpreter parses a batch script, i.e. how the FOR command line looks after parsing and processing it by cmd.exe processing the batch file which can be seen on debugging the batch file and how the command line is passed as argument string(s) to the second cmd.exe instance executed in background.
There are at least following two solutions.
for /F "delims=" %%G in ('^""%ProgramFiles(x86)%\ABC\DEF\XYZ.exe" Get /a "P1=D2" /b "Q1=D3" /c "D5"^"') do set "var=%%G"
for /F "delims=" %%G in ('""C:\Program Files (x86^)\ABC\DEF\XYZ.exe" Get /a "P1^=D2" /b "Q1^=D3" /c "D5""') do set "var=%%G"
The first solution was suggested by Compo and is the easier one. The entire command line is enclosed in " which cmd.exe started in background removes before processing and executing the remaining command line. The additionally added " at beginning and at end of the command line are escaped with ^ for cmd.exe processing the batch file to be interpreted as literal character and not as beginning/end of an argument string. So the argument strings between the two escaped " are interpreted normally by cmd.exe processing the FOR command line.
The second solution is more difficult to understand. The entire command line is also enclosed in " for cmd.exe started in background. But the double quotes are not escaped for cmd.exe processing the batch file and for that reason this cmd.exe instance interprets the command line now as list of following argument strings:
""
C:\Program
Files
(x86
)
\ABC\DEF\XYZ.exe
" Get /a "
P1=D2
" /b "
Q1=D3
" /c "
D5
""
The closing round bracket after (x86 is not interpreted as literal character in this case because of being outside a double quoted argument string for cmd.exe processing the entire FOR command line. For that reason it must be escaped with ^ to be interpreted as literal character and not as end of set placed at wrong position before ' marking end of the command line to execute.
The two equal signs are both outside a double quoted argument string for cmd.exe processing the batch file. Therefore cmd.exe would interpret them as argument separator and would replace each = by a space character. That is not wanted here because of the arguments should be passed as P1=D2 and Q1=D3 to the executable and not as P1 D2 and Q1 D3. So both equal signs must be escaped with ^ to be interpreted as literal character and not as argument separator by cmd.exe processing the batch file.
Both solutions result in running in background with Windows installed into C:\Windows and 32-bit program files into C:\Program Files (x86):
C:\Windows\System32\cmd.exe /c ""C:\Program Files (x86)\ABC\DEF\XYZ.exe" Get /a "P1=D2" /b "Q1=D3" /c "D5""
The background command process removes the first and last " and then runs
"C:\Program Files (x86)\ABC\DEF\XYZ.exe" Get /a "P1=D2" /b "Q1=D3" /c "D5"
It is possible to create in directory C:\Program Files (x86)\ABC\DEF a batch file XYZ.bat containing just the command line #echo %0 %* and replace .exe by .bat in the batch file containing the FOR command line to see if the batch file is executed at all and with which parameters passed to the batch file. This helps to understand what is going on with removing #echo off and running the batch file with the FOR command line from within a command prompt window.
The posted line fails because of the additional quotes around parameters after the program name. One workaround is to embed the entire command line inside another pair of outer quotes (and escape reserved characters like () to ^(^) because they now fall outside a quoted string).
for /F "usebackq delims=" %%G IN (`""C:\Program Files ^(x86^)\ABC\DEF\XYZ.exe" Get /a "P1=D2" /b "Q1=D3" /c "D5""`) do (...)
As noted in a comment, usebackq is not actually needed in this case, and the same syntax will work if using regular quotes, instead.
There is no need to use backticks or usebackq here, simply use single quoted string and double quote your paths.
#echo off
for /F "delims=" %%G IN ('"C:\Program Files (x86)\ABC\DEF\XYZ.exe" Get /a "P1=D2" /b "Q1=D3" /c "D5"') do set "var=%%G"
Also note that the parenthesized do code block to set the variable is not needed, in fact, as far as I can see, you do not even need the variable as you could get away by just using the metavariable.
#echo off
set "prog=C:\Program Files (x86)\ABC\DEF\XYZ.exe"
for /F "delims=" %%G IN ('"%prog%" Get /a "P1=D2" /b "Q1=D3" /c "D5"') do echo %%G

including special char (&) from "for delims"-result in batch file

I have a script that reads thru file and sets variable as it finds it.
#echo off
Setlocal EnableDelayedExpansion
for /f "tokens=*" %%V in ('findstr /I /C:title= "%~1"') do set title=%%V
echo %title%
In the txt file there is "title=variable & speed".
And the script only returns:
title=variable
'SPEED' is not recognized as an internal or external command,
operable program or batch file.
As it should return whole line.
This is the part, I have not found the solution yet. It should change the "&" to "-", as in finally this script renames files.
First, don't enable delayed expansion because of not needed here. It can result in findstr does not find the file to open if the batch file is called with a file name without or with a path containing one or more exclamation marks.
Second, the FOR option "tokens=*" results in removing leading spaces/tabs from a line output by FINDSTR not starting with a semicolon and if there is something left assign the rest of the line to specified loop variable. This is okay if this behavior is wanted here. Otherwise it would be better to use "delims=" which defines an empty list of delimiters resulting in assigning the entire line not starting with a semicolon to the specified loop variable. Not double quoted argument string delims^=^ eol^= defines an empty list of delimiters and no end of line character to get assigned also a line starting with a semicolon to the loop variable. The two equal signs and the space character must be escaped with caret character ^ to be interpreted as literal characters and not as argument separators.
Third, an ampersand outside a double quoted argument string is interpreted as operator to unconditionally execute the command after & after executing the command before &. For details see Single line with multiple commands using Windows batch file. For that reason it is recommended to enclose the argument string of command SET in double quotes as explained in detail on answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line?
So I suggest using following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LoadedTitle="
for /F "tokens=*" %%V in ('%SystemRoot%\System32\findstr.exe /I /C:"title=" "%~1" 2^>nul') do set "LoadedTitle=%%V"
if defined LoadedTitle (
setlocal EnableDelayedExpansion
echo !LoadedTitle!
endlocal
)
endlocal
Read this answer for details about the commands SETLOCAL and ENDLOCAL.
Please note that /C:"title=" is used instead of /C:title= on FINDSTR command line as otherwise FINDSTR would in this special case search just for title. The reason is that the command line within the round brackets is executed in a separate command process started by FOR with cmd.exe /C in background and the equal sign not enclosed in a double quoted string and not escaped with ^ would be removed by current command process because of being interpreted as separator. In a command prompt window it is possible to use the FINDSTR command line with /C:title= without double quotes, but not here on this FOR command line in batch file.
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 findstr command line with using a separate command process started in background.
Temporary enabling delayed expansion just for output of the line with loaded title string is required because of usage of only echo %LoadedTitle% would be modified before execution to echo title=variable & speed and the ampersand is again not interpreted as literal character to output by ECHO, but as operator to run speed after execution of echo title=variable .
I recommend to read
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
How to debug a batch file?
A batch file writer must always take into account what is finally executed by Windows command processor after parsing a command line one or more times as this can be different than what is written in batch file on using environment variable references with syntax %variable%.
I Found the correct "formula" to script
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=*" %%V in ('%SystemRoot%\System32\findstr.exe /I /C:"title=" "%~1"
2^>nul') do set "title=%%V"
set title=!title:^&=-!
echo "!title!"
endlocal
As it does now that what I wanted, it returns:
"title=variable - SPEED"
It is not as you suggested but it does the job.

cmd - Remove last \ from the searched folder

I need folder's full path given it has .txt file
Currently I am using the following command
D:\Testfolder\_dn>for /r %i in (*.txt) do #echo %~dpi
And getting the following output
D:\Testfolder\_dn\2nd\
D:\Testfolder\_dn\3rd\
D:\Testfolder\_dn\4th\
D:\Testfolder\_dn\5th\
D:\Testfolder\_dn\first\
But I want the output like following
D:\Testfolder\_dn\2nd
D:\Testfolder\_dn\3rd
D:\Testfolder\_dn\4th
D:\Testfolder\_dn\5th
D:\Testfolder\_dn\first
I tried remove last characters string batch
for /r %i in (*.txt) do #echo %~dpi:~0,-1%
But it is not working.
How can I remove the last \ from the search result?
The sub-string expansion syntax works on normal environment variables only, but not on for variable references. To apply that syntax you need to assign the value to a variable first:
for /R %i in ("*.txt") do #(set "VAR=%~dpi" & echo/!VAR:~^,-1!)
But since you are editing a variable value within a block of code (loop), you need to enable and to apply delayed variable expansion. This can be established by opening the command line instance by cmd /V:ON or cmd /V. However, this can still cause trouble when a path contains !-symbols.
An alternative and better solution is to avoid string manipulation and delayed variable expansion by appending . to the paths (meaning the current directory) and using another for loop to resolve the paths by the ~f modifier of the variable reference, like this:
for /R %i in ("*.txt") do #for %j in ("%~dpi.") do #echo/%~fj
The "" avoid problems with paths containing SPACEs or other token separators (,, ;, =,...).
Be sure to enable delayed expansion so that the P variable gets reevaluated in the loop.
SETLOCAL ENABLEDELAYEDEXPANSION
for /r %%i in (*.txt) do (
SET "P=%%~dpi"
echo !P:~0,-1!
)

Batch script to print last line of many files in a folder matching a file name pattern

I have multiple text files in a folder. I would like to print the last line of each file found in the folder matching the file name pattern *.Config and redirect those lines to a new text file.
The below script works for 1 text file. But at the moment I do a for loop for multiple text files and %lastline% prints always the same value.
#echo off & setlocal EnableDelayedExpansion
for /f %%i in ('dir /b *.Config') do (
for /r "delims==" %%a in (%%i) do (
set lastline=%%a
echo %lastline% is the last line of %%i >> vResult.txt
)
)
Use this batch code to get written into file vResult.txt in current directory the last line of each *.config file in current directory:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
del vResult.txt 2>nul
for %%I in (*.config) do (
for /F "usebackq delims=" %%L in ("%%I") do set "LastLine=%%L"
echo !LastLine! is the last line of %%I>>vResult.txt
)
endlocal
First command extensions as needed for the second FOR command line and delayed environment variable expansion as needed for echoing the last line of each file are enabled with second command line. Command extensions are enabled by default, but not delayed expansion.
Then a perhaps already existing vResult.txt is deleted with suppressing the error message output by command DEL to handle STDERR by redirecting it to device NUL in case of the file to delete does not exist.
The outer FOR searches for *.config files in current directory with hidden attribute not set. There is no need to use command DIR here except it would be necessary to find also hidden *.config files.
The inner FOR processes the lines of each *.config file found by the outer FOR loop. It is in general advisable to enclose the file name in double quotes and use usebackq to interpret the double quoted file name as file name and not as string to process in case of a *.config file contains a space character. delims= disables splitting each line up on spaces/tabs to get entire line.
As the environment variable LastLine is defined/modified within a command block, it is necessary to use delayed expansion as enabled at beginning with referencing the environment variable with exclamation marks instead of percent signs.
All environment variables referenced with percent signs of entire command block to execute on each iteration of outer FOR are replaced already by Windows command interpreter with current value of referenced environment variable before executing outer FOR command. This resulted on your batch code with ECHO command line additionally in wrong loop in replacing %lastline% by nothing before the outer FOR is executed the first time.
For debugging a batch file and to see what Windows command interpreter really executes after preprocessing the command lines and the command blocks, remove or comment out #echo off or change that line to #echo on, open a command prompt window, and run the batch file from within this console window by entering its name with full path enclosed in double quotes after changing the current directory to the directory on which the batch file should work.
A space character left of redirection operator >> is also output by command ECHO and therefore also written to the text file as trailing space which is the reason why the space character left of >> is removed here. The space character right of >> would be ignored, but is here also removed.
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.
del /?
echo /?
endlocal /?
for /?
setlocal /?
set /?
And see also the Microsoft article Using command redirection operators.

Resources