I am trying to comment out some useful hints. I followed this Stack Overflow page to apply the most defensive comment - using double quotes. Comment error. But I still get error
This is my simple script: test_comment.cmd
#echo off
:: " %~nI - expands %I to a file name only "
:: " %~xI - expands %I to a file extension only "
for /F "delims=" %i in ("c:\foo\bar baz.txt") do #echo %~nxi
This is the error I got when I run it
>test_comment
The following usage of the path operator in batch-parameter
substitution is invalid: %~nI - expands %I to a file name only "
For valid formats type CALL /? or FOR /?
The syntax of the command is incorrect.
If I removed those two commented lines, error would disappear; so that I know the comment didn't work.
How should I comment out those two lines?
I really don't want to delete them.
I am using windows 10, latest patch, default cmd.exe.
I followed the link in #Stephan's comment and read: Batch Line Parser
Phase 0) Read Line:
Phase 1) Percent Expansion:
Phase 2) Process special characters, tokenize, and build a cached
command block
In short: Comment is parsed in Phase 2, after the Percent Expansion in Phase 1.
Solution: use % to escape the % in the comment, as suggested by #Magoo in the comment.
#echo off
:: " %%~nI - expands %%I to a file name only "
:: " %%~xI - expands %%I to a file extension only "
for /F "delims=" %%I in ("c:\foo\bar baz.txt") do #echo %%~nxI
Then I got the desired result
>test_comment
bar baz.txt
Related
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.
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!
)
I used to program batch files for work but I quit since a long time, now I'm back on the job and there seemed to be a bit of a problem.
I try to edit txt files using CMD commands on a batch file:
e.g. echo hello >> *.txt
the thing is I want to add the text to all the txt files in that directory and I remember the * represented all the files in that directory with the same extension unless it's used as *.* then it includes all the files, but now all it does is just writes this error on cmd:
The filename, directory name, or volume label syntax is incorrect.
Can anyone can give a little help?
You can use a FOR /F loop with a DIR command to iterate the full paths and pass those over to the redirection append >> to echo to the text files accordingly.
Example FOR Loop
Be sure to change the value of the Folder= variable to be the directory you need to append to the files with the ECHO command.
Confirmed working batch script example
#ECHO ON
SET Folder=C:\MyFolder
CD /D "%Folder%"
FOR /F "TOKENS=*" %%A IN ('DIR /B /A-D "%Folder%\*.txt"') DO ECHO HELLO>>%%~fA
PAUSE
EXIT
Further Resources
DIR
FOR /F
In addition, substitution of FOR variable references has been
enhanced. You can now use the following optional syntax:
%~I - expands %I removing any surrounding quotes (")
%~fI - expands %I to a fully qualified path name
%~dI - expands %I to a drive letter only
%~pI - expands %I to a path only
%~nI - expands %I to a file name only
%~xI - expands %I to a file extension only
%~sI - expanded path contains short names only
%~aI - expands %I to file attributes of file
%~tI - expands %I to date/time of file
%~zI - expands %I to size of file
%~$PATH:I - searches the directories listed in the PATH
environment variable and expands %I to the
fully qualified name of the first one found.
If the environment variable name is not
defined or the file is not found by the
search, then this modifier expands to the
empty string
I have the below cmd script that is not yielding what I would expect:
set parent=src\Sandbox
set child=src\Sandbox\SandboxTest
echo %parent%
for /F "tokens=* delims=%parent%\" %%i in ("%child%") do set DIRNAME=%%i
echo %DIRNAME%
%DIRNAME% is getting returned as "Test", but I expect DIRNAME to be 'SandboxTest'.
Anyone have any ideas why this is? Is there an issue with my delims?
Thanks,
Keith
You want to use variable reference syntax.
set parent=src\Sandbox
set child=src\Sandbox\SandboxTest
echo %parent%
for /F %%i in ("%child%") do (
set DIRNAME=%%~ni
)
delims is a array of characters used to split
set parent=aeiou
set child=abcdefghijklmnopqrstuvwxyz
for /F "tokens=1-10 delims=%parent%\" %%a in ("%child%") do (
echo.%%a
echo.%%b
echo.%%c
echo.%%d
echo.%%e
)
It splits on characters not words.
bcd
fgh
jklmn
pqrst
vwxyz
type for /? for the full range of variable reference syntax.
In addition, substitution of FOR variable references has been enhanced.
You can now use the following optional syntax:
%~I - expands %I removing any surrounding quotes (")
%~fI - expands %I to a fully qualified path name
%~dI - expands %I to a drive letter only
%~pI - expands %I to a path only
%~nI - expands %I to a file name only
%~xI - expands %I to a file extension only
%~sI - expanded path contains short names only
%~aI - expands %I to file attributes of file
%~tI - expands %I to date/time of file
%~zI - expands %I to size of file
%~$PATH:I - searches the directories listed in the PATH
environment variable and expands %I to the
fully qualified name of the first one found.
If the environment variable name is not
defined or the file is not found by the
search, then this modifier expands to the
empty string
The modifiers can be combined to get compound results:
%~dpI - expands %I to a drive letter and path only
%~nxI - expands %I to a file name and extension only
%~fsI - expands %I to a full path name with short names only
%~dp$PATH:I - searches the directories listed in the PATH
environment variable for %I and expands to the
drive letter and path of the first one found.
%~ftzaI - expands %I to a DIR like output line
DELIMS does not define a delimeter phrase - it defines a set of delimiter characters. None of the characters in "Test" appear in you DELIMS set, hence the result.
In your particluar cited case, you can get your desired result using:
for %%F in ("%child%") do set DIRNAME=%%~nxF
How do I in a batch script find the full path to application XYZ if it is installed
Clarifications:
The application is not in the PATH
All I have is it's name in this case "ISTool.exe" and I would like to get C:\Program\ISTool\ISTool.exe
You can locate an executable on the path (or other path-like string if necessary):
c:\> for %i in (cmd.exe) do #echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe
c:\> for %i in (python.exe) do #echo. %~$PATH:i
C:\Python25\python.exe
Details can be found at the end of the help text for the "for" command, "for /?" but the summary is:
%~i - expands %i removing any surrounding quotes.
%~fi - expands %i to a fully qualified path name.
%~di - expands %i to a drive letter only.
%~pi - expands %i to a path only.
%~ni - expands %i to a file name only.
%~xi - expands %i to a file extension only.
%~si - expanded path contains short names only.
%~ai - expands %i to file attributes of file.
%~ti - expands %i to date/time of file.
%~zi - expands %i to size of file.
%~$P:i - searches the directories listed in the P environment variable
and expands %i to the fully qualified name of the first one found.
If the environment variable name is not defined or the file is not
found by the search, then this modifier expands to the empty string.
The modifiers can be combined to get compound results:
%~dpi - expands %i to a drive letter and path only.
%~nxi - expands %i to a file name and extension only.
%~fsi - expands %i to a full path name with short names only.
%~dp$P:i - searches the directories listed in the P environment variable
for %i and expands to the drive letter and path of the first
one found.
%~ftzai - expands %i to a DIR like output line.
If your executable isn't on the path (as per your edit), your best bet is to use the bare/subdirectory format of dir which will do it for you. From the root directory:
dir /b /s ISTool.exe
will get you all of the files on that drive with that name. You then just have to parse the output. My own preference would be to use Cygwin's "find /cygdrive -name ISTool.exe" but that's because I already have it installed. You may not want that (or even have that option).
Update:
That dir /b /s command will take a while since it's basically searching the whole disk. If that's a problem you may want to consider periodically creating a cached record of all files on all disks with a cmd file like:
#echo off
setlocal enableextensions enabledelayedexpansion
del c:\files.cache.tmp >nul: 2>nul:
for %%d in (c d e) do (
cd /d %%d:\
dir /b /s >>c:\files.cache.tmp
)
del c:\files.cache >nul: 2>nul:
move c:\files.cache.tmp c:\files.cache
endlocal
You could do this with scheduled tasks either nightly (for an always-on server) or on boot (for a desktop). You could even make the script more intelligent to do it only every couple of days (I have an automated backup script that does a similar thing on the family machines I support). This creates the list in a temporary cache file then overwrites the original one to ensure the time when the file doesn't exist is minimized.
Then you can just use:
findstr \\ISTool.exe c:\files.cache
to locate all your files.
Based on the really helpful answers here I hacked up these two batches which I thought I share here (I know this thread is now 3 years old, but its found as 1st match when googling ...):
1) which.bat:
#echo off
REM emulate the Linux which command
if "%1" == "" (
echo Usage: %~nx0 ^<command[.ext]^>
exit /b
)
setlocal
for %%P in (%PATHEXT%) do (
for %%I in (%1 %1%%P) do (
if exist "%%~$PATH:I" (
echo %%~$PATH:I
exit /b
)
)
)
not perfect because there are allways two tests, but its fast enough so I didnt further bother about; sure its possible to 1st do a separate test with %1 only ...
2) findfile.bat:
#echo off
REM emulate the Linux find command
if "%1" == "" (
echo Usage: %~nx0 ^<startdir^> ^<file^>
exit /b
)
setlocal
for /f "delims=" %%A in ('dir /b /s %1\%2') do set F=%%A
if exist "%F%" echo %F%
This is the closest I got. One drawback is that it works only for one drive per execution, but that could made more flexible. Another is the output, that always contains a // between the path and the filename. But per definition thats a valid path.
#ECHO OFF
SET filename=autoexec.bat
FOR /R C:\ %%a IN (\) DO (
IF EXIST "%%a\%filename%" (
SET fullpath=%%a%filename%
GOTO break
)
)
:break
ECHO %fullpath%
Will deliver: C:\\autoexec.bat
EDIT:
For explanation, the for loop iterates through all directories starting at the given path (C:\) and check if the filename exists in that directory. If so, both variables are concatenated and stored in %fullpath% and the loop is terminated by a jump.
Sometimes this simple solution works, where you check to see if the output matches what you expect. The first line runs the command and grabs the last line of standard output.
FOR /F "tokens=*" %%i in (' "xcopy /? 2> nul" ') do SET xcopyoutput=%%i
if "%xcopyoutput%"=="" echo xcopy not in path.
Alternately, programs like Everything, and UltraSearch (freeware), SwiftSearch can search the MFT (of your NTFS partition) for files (so it can do so very quickly), (but Wikipedia claims this kind of thing can breach your security model by finding things it's not supposed to) -- some of them look like they have some command line parameters, I've not used them, but maybe it could be helpful, if you're resorting to a full drive search.
The answers I got from others worked (but slow or used extra files) and worked for any exe but didn't really suit my needs.
Since I wanted to find a particular exe I went looking in the registry using REG QUERY instead. I found a key that contained the data I wanted to find and extracted that.
The result is fast, has few lines of code but is not very pretty nor reusable.
Short example:
#ECHO off
SETLOCAL
set found=
FOR /F "tokens=1-3 delims= " %%a IN ('REG QUERY "HKEY_CLASSES_ROOT\Applications\ISTool.exe\shell\OpenWithISTool\command"') DO (
set found=%%c
)
for /f "tokens=1-2" %%a in ("%found%") do (
set my_exe=%%a
)
echo %my_exe%
ENDLOCAL
This results in "C:\Program\ISTool\ISTool.exe" (with quotes)
Note: delims= above is followed by a tab-char