I have a file of which the contents are as follows which are in consecutive lines,
VERSION=7.0.2
BUILD=03bbabbd5c0f
PRODUCT=splunk
PLATFORM=Windows-AMD64
From this I only want the VERSION. I tried using the following command:
FOR /F "usebackq tokens=1 eol=P" %G IN ("C:\ProgramFiles\SplunkUniversalForwarder\etc\splunk.version") DO echo %G
Involved eol=P because it doesn't bring out the last two lines, but I don't want the second line too. Can anyone help? Actually the main goal is to get only the version number not even the "VERSION=".
Simpler:
FOR /F "tokens=2 delims==" %G IN ('findstr "VERSION" "C:\ProgramFiles\SplunkUniversalForwarder\etc\splunk.version"') DO echo %G
Use findstr command to select the desired line, that may be at any position in the file...
Along the lines of my comment.
At the Command Prompt:
For /F "UseBackQ EOL=P Tokens=1* Delims==" %A In ("%ProgramFiles%\SplunkUniversalForwarder\etc\splunk.version") Do #Echo %B
As a batch file:
For /F "UseBackQ EOL=P Tokens=1* Delims==" %%A In ("%ProgramFiles%\SplunkUniversalForwarder\etc\splunk.version") Do #Echo %%B
In a batch file use this code:
#echo off
for /F "usebackq tokens=1* delims==" %%A in ("C:\Program Files\SplunkUniversalForwarder\etc\splunk.version") do if /I "%%~A" == "VERSION" if not "%%~B" == "" set "Version=%%~B" & goto HaveVersion
echo Error: Could not find VERSION= with a version string in file:
echo C:\Program Files\SplunkUniversalForwarder\etc\splunk.version
pause
goto :EOF
:HaveVersion
echo Version is: %Version%
pause
Please note the space in Program Files.
The command FOR with the used option /F reads the specified file line by line with skipping empty lines and lines starting with ; which is the default for eol (end of line) option.
The option usebackq is required to get the full qualified file name enclosed in double quotes interpreted as file name and not as string to process. The double quotes " around full qualified file name are required because of space character.
delims== redefines the delimiters for splitting the lines into substrings (tokens) from default space and horizontal tab to equal sign. So FOR splits now the lines using only = as delimiter character for the strings.
tokens=1* means that the first equal sign delimited substring should be assigned to loop variable A. And the rest of the line after the first equal sign(s) should be assigned without any further splitting to next loop variable according to ASCII table which is in this case the loop variable B. Now it should be also clear why loop variables are case-sensitive while environment variables are not case-sensitive. It makes a difference on what is the next loop variable if the specified loop variable is A or a.
On each loop run first a case-insensitive string comparison is made to check if the version string is at beginning of current line. If this first condition is true a second IF condition is used to verify that there is really a version string right to the equal sign in the file. If this second condition is also true the version string is assigned to environment variable Version and the loop is exited by continuing the batch file processing with a jump to the line below label HaveVersion. So the other lines in file are not further processed by FOR.
In general it is better to reference the environment variable ProgramFiles with %ProgramFiles% instead of using C:\Program Files as the standard program files directory for 64-bit applications on Windows x64 respectively for 32-bit applications on Windows x86 can be on any drive with any folder name. But the Windows WOW64 Implementation Details must be taken into account if the batch file should work on 32-bit Windows, on 64-bit Windows in 64-bit environment and on 64-bit Windows in 32-bit environment. See also Wikipedia article about Windows Environment Variables.
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.
echo /?
for /?
goto /?
if /?
pause /?
set /?
See also Single line with multiple commands using Windows batch file for an explanation of operator &.
Related
this is my first time creating a batch file and I'm relatively new to this coding language. I wish to create a batch file that scans through all DLL files within a folder to retrieve and write the version info into a new text file. The nested for-loop functioned well but I couldn't get the WMIC command to execute, can you help?
Upon double-clicking to run the batch file, this is what I will get:
Description = Invalid query
'WMIC DataFile Where "Name='mydirecotry\myfile.dll'" Get Version'
Node - myPCname
Here are the codes I wrote:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%I in (D:\mydirecotry\*.dll*) DO (
SET Location='WMIC DataFile Where "Name='%%I'" Get Version'
ECHO !Location!
For /F "Tokens=1* Delims=" %%A In (!Location!) Do ( <---Where i think it went wrong
For /F "Tokens=*" %%B In ("%%~A") Do Set "pver=%%B"
ECHO %%I - %pver% >> test.txt
)
)
pause
Thanks in advance.
As the majority of my earlier comment is already within a supplied answer, I have decided to provide the alternative method I used within that comment, as an answer.
For the task you've laid out in your question, you should be able to have a single line batch-file, with no set or for commands, just containing:
#"%__AppDir__%wbem\WMIC.exe" /Output:"test.txt" DataFile Where "Drive='D:' And Path='\\mydirecotry\\' And Extension='dll'" Get Name, Version
The DLL version information collection task can be done with following batch file:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
(for %%I in ("D:\V4-x64\Exe\Bin\*.dll*") do (
set "FileName=%%I"
set "FileName=!FileName:\=\\!"
for /F "skip=1" %%J In ('%SystemRoot%\System32\wbem\wmic.exe DATAFILE where Name^="!FileName!" GET Version 2^>nul') do if defined FileName set "FileName=" & echo %%I - %%J
)) >test.txt
endlocal
Each backslash in full qualified file name must be escaped with one more backslash. That is a special WMIC requirement on using DATAFILE on which file name must be always specified with full path.
The command line specified within ' is executed by FOR with running in background %ComSpec% /c and the command line in ' appended as additional arguments. So executed is in background with Windows installed to C:\Windows the command line:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\wbem\wmic.exe DATAFILE where Name="D:\\V4-x64\\Exe\\Bin\\FileName.dll" GET Version 2>nul
The equal sign must be escaped with ^ to be interpreted as literal character and not as argument separator as otherwise = would be replaced by a space character before running cmd.exe in background which would make the arguments invalid for wmic.exe.
The redirection operator > must be escaped also with ^ to be interpreted as literal character on parsing the FOR command line before executing FOR at all.
WMIC outputs the data Unicode encoded with UTF-16 Little Endian encoding with byte order mark (BOM). FOR has problems to parse the Unicode output correct as expecting an ASCII/ANSI/OEM character encoding, i.e. one byte per character without null bytes. The byte sequence with the hexadecimal values 0D 00 0A 00 being UTF-16 LE encoded the line ending carriage return + line-feed is interpreted as 0D 0D 0A by FOR (respectively cmd.exe). So there is an erroneous carriage return at end of the real line ending removed by FOR before processing further the remaining string.
For that reason the WMIC output is interpreted with skipping the first line containing just the string Version and so processed is first the second line with the version string by removing leading and trailing spaces.
The full qualified file name of the current DLL file and its version is output with command ECHO. Additionally the environment variable FileName is deleted to make sure that no further line output by WMIC including an empty line interpreted by FOR as a line with just a carriage return results in running once again command ECHO with loop variable J having assigned a carriage return.
The batch file does not work for DLL files with an exclamation mark in file name, but that should not matter here as I have never seen a DLL with ! in file name.
Another solution provided by Compo in a comment is much faster and works also for DLL file names with ! in file name.
#echo off
%SystemRoot%\System32\wbem\wmic.exe DATAFILE Where "Drive='D:' And Path='\\V4-x64\\Exe\\Bin\\' And Extension='dll'" GET Name,Version >"%TEMP%\%~n0.tmp"
%SystemRoot%\System32\more.com +1 "%TEMP%\%~n0.tmp" >test.txt
del "%TEMP%\%~n0.tmp"
WMIC searches itself for *.dll files in the directory D:\V4-x64\Exe\Bin and outputs full name of the DLL file and its version written into a temporary file being UTF-16 LE encoded with BOM. This temporary file is next processed with MORE with skipping the header line and the empty lines at bottom and the output is written into test.txt being a non-Unicode file. The temporary file is deleted finally.
For completeness:
#%SystemRoot%\System32\wbem\wmic.exe DATAFILE Where "Drive='D:' And Path='\\V4-x64\\Exe\\Bin\\' And Extension='dll'" GET Name,Version | %SystemRoot%\System32\more.com +1 >test.txt
This command line produces a non-Unicode encoded text file test.txt on which every line with a DLL file name and version is terminated with two carriage returns and one line-feed and with two empty lines at bottom with just one carriage return plus line-feed. So it is not recommended to use this single line variant.
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 %~n0 ... file name of argument 0, i.e. the batch file name without file extension.
del /?
echo /?
endlocal /?
for /?
if /?
more /?
set /?
setlocal /?
wmic /?
wmic datafile /?
wmic datafile get /?
See also single line with multiple commands using Windows batch file for an explanation of the operator & and the Microsoft documentation about Using command redirection operators.
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.
EDIT: The value of %%a is
<MachineEntry uuid="{awae1979-6512-4br8-acg5-3oe94f56712de}" src="C:\Users\John\VirtualBox VMs\VM\VM.vbox"/>
I'm trying to grab the folder of a VMWare Virtual Machine. Since the user can change where the folder is located, the path is never the exact same. I've created a script that grabs the path for any VMs on the system but the issue then is that I have a path that ends in a file. In the code I've provided I tried to use the existing findstr loop to remove the slash and trailing characters but it only removes the C:\. Here is an example of my current code:
(FOR /F "delims=" %%a in ('findstr /I /L "src" VirtualBox.xml') DO (
SET "line=%%a"
REM This is the term that the script searches for
SET "line=!line:*src=!"
REM This removes the excess characters from the grabbed string
SET "line=!line:~2,-3!"
SET "line=!line:*\=!"
FOR /F "delims=<" %%b in ("!line!") DO ECHO !line!
))
PAUSE
In my example code it delivers Users\John\VirtualBox VMs\VM\VM.vbox. It ends up only cutting the C:\ off. I want it to instead cut the last \ off giving me C:\Users\John\VirtualBox VMs\VM. Any ideas on how I could get the code to iterate from the end of the line instead of from the start? Any advice or suggestions are welcome!
Would it not be easier to use the doublequotes as delimiters?
#For /F Tokens^=4Delims^=^" %%A In ('Find "src="^<"VirtualBox.xml"')Do #Echo(%%A
or if you only wanted the path, not the full string:
#For /F Tokens^=4Delims^=^" %%A In ('Find "src="^<"VirtualBox.xml"')Do #Echo(%%~dpA
[Edit /]
Here's an alternative, batch-file solution based upon the location provided by LotPings, in their comment below, and leveraging powershell:
#For /F "Delims=" %%A In ('PowerShell -NoProfile -Command^
"[XML]$VBoxes = Get-Content "$Env:UserProfile\.VirtualBox\VirtualBox.xml";"
"$VBoxes.VirtualBox.Global.MachineRegistry.MachineEntry |"
"ForEach-Object {Split-Path -Path "$_.src" -Resolve}" 2^>Nul'
)Do #Echo(%%A
This is a single line command, changed to multiline to improve readability. In it I have used Split-Path along with -Resolve so that only results which are currently available are output.
You could use = and / as delimiters to extract the quoted path and then let the ~ modifiers do their job:
for /F "tokens=3 delims==/" %%L in ('findstr /I "src=" "VirtualBox.xml"') do (
echo Full path: %%~L
echo File name: %%~nxL
echo Dir. path: %%~dpL
)
My solution is using in batch file:
#echo off
for /F "tokens=3 delims=/=" %%I in ('%SystemRoot%\System32\findstr.exe /I /L "src" VirtualBox.xml') do set "VirtualBoxPath=%%~dpI"
echo Virtual box path is: "%VirtualBoxPath%"
FOR with option /F starts in this case a new command process in background with %ComSpec% /c and the command line between the two ' inside the round brackets appended resulting with Windows installed into C:\Windows in executing:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\findstr.exe /I /L "src" VirtualBox.xml
FINDSTR searches case-insensitive for lines containing the literal string src in file VirtualBox.xml in current directory which can be also a directory different to the directory containing the batch file. In this case FINDSTR outputs to handle STDOUT of background command process the line:
<MachineEntry uuid="{awae1979-6512-4br8-acg5-3oe94f56712de}" src="C:\Users\John\VirtualBox VMs\VM\VM.vbox"/>
FOR captures everything output to handle STDOUT of started command process and processes the captured text line by line after started cmd.exe terminated itself.
FOR ignores always empty lines on processing the captured output.
FOR splits up by default each line into substrings using normal space and horizontal tab as delimiters and assigns just first space/tab delimited string to specified loop variable I. The line splitting behavior is modified for this task by specifying tokens=3 delims=/= on FOR command line after option /F.
The string delimiters are now the forward slash / and the equal sign = which means the XML line to process is split up into:
<MachineEntry uuid
"{awae1979-6512-4br8-acg5-3oe94f56712de}" src
"C:\Users\John\VirtualBox VMs\VM\VM.vbox"
>
Only the third string is of interest and is assigned to loop variable I because of tokens=3.
So loop variable I gets assigned the string:
"C:\Users\John\VirtualBox VMs\VM\VM.vbox"
FOR ignores by default also all lines starting with a semicolon because of eol=; is the default for end of line option. The end of line option must not be changed here because the XML line to process never starts with ;.
In real FOR first splits up the line using the specified or the default string delimiters and then checks if the first substring starts with the specified or default end of line character. So FOR would also ignore in this case a line starting for example with =/; because of = and / would be removed during line splitting and then first substring starts with ; although the line itself starts with = and for that reason the line is completely ignored although assigned to loop variable I should be the third and not the first substring.
The string assigned to loop variable I is referenced with %%~dp using three modifiers:
The first one is ~ which results in removing surrounding " from string assigned to loop variable I.
The second one is d which means drive letter with colon.
The third one is p which means path without drive letter and colon.
The path is everything up to last backslash with including the last backslash.
Therefore the string C:\Users\John\VirtualBox VMs\VM\ is assigned to environment variable VirtualBoxPath.
In general it does not matter on applying the modifiers on string assigned to a loop variable if this string really references an existing file or folder. The modifiers are applied on the string itself if no file/folder can be found by FOR with the string assigned to referenced loop variable in file system. Of course the file/folder referenced by the string assigned to loop variable must exist if being incomplete like just a file name without file extension is assigned to loop variable and wanted is drive, path or file extension. In this case FOR accesses file system to get full qualified file name of incomplete file name string and tries its best to get the wanted data.
In a windows batch file, I would like to rename files containing a 4-digit year (ex: "1999") in the filename by simply wrapping the year string in parentheses. Example:
home video 1998.avi
home vid 1987.mov
home_video (2002).avi
would become
home video (1998).avi
home vid (1987).mov
home_video (2002).avi
Notice that if it's already wrapped in parentheses, I'd prefer not to double them up.
So far, I have only been able to match the file names containing a year string with the following code:
#echo off
REM Match file names with 4-digit year
setlocal enableDelayedExpansion
for /f "tokens=1* delims=" %%A in (
'dir /B "*"^|findstr "[1-2][0-9][0-9][0-9]" '
) do #echo %%A
pause
REM Now what?
So I can output a list of matching file names, but from there I do not know how to target the grouped characters that findstr matched in order to parse the full file name into the 3 chunks I believe I would need: the substring preceding the matched group, the group itself, and the substring following the group.
Is this possible in a batch file?
I use since more than 20 years Total Commander (shareware) for file/folder renaming tasks which makes it possible with its built-in multi-rename tool to easily rename files and folders with just a few clicks on which the results can be viewed before really running the multi-rename and which even supports undo after having done the multi-rename. Well, in real I use Total Commander for nearly all file management tasks.
But it was interesting to develop the code for this very special file renaming task with all the limitations Windows command processor has because of not being designed for such tasks.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /R /C:"19[89][0123456789]" /C:"20[012][0123456789]"') do call :RenameFile "%%I"
endlocal
goto :EOF
:RenameFile
set "FileName=%~n1"
setlocal EnableDelayedExpansion
set "Year=1980"
:YearLoop
set "NewName=!FileName:%Year%=(%Year%)!"
if "!NewName!" == "!FileName!" (
if %Year% == 2029 goto ExitSub
set /A Year+=1
goto YearLoop
)
if "!FileName:(%Year%)=!" == "!FileName!" ren "%~1" "!NewName!%~x1"
:ExitSub
endlocal
goto :EOF
FOR executes the following command line with using a separate command process started in background with cmd.exe /C:
dir /A-D-H /B 2>nul | C:\Windows\System32\findstr.exe /R /C:"19[89][0123456789]" /C:"20[012][0123456789]"
DIR outputs with the used options all names of non-hidden files in current directory with just file name + extension and without file path. An error message output in case of current directory does not contain any non-hidden file is suppressed by redirecting it from handle STDERR to device NUL with 2>nul.
The file names output by DIR are redirected with | to handle STDIN of command FINDSTR which searches case-sensitive with two regular expression interpreted search strings for four digits in range 1980 to 1999 or in range 2000 to 2029. There is no check made if a match of a four digit number is part of a larger number like 12000 or 19975. And there is no check made if there are already round brackets around the four digit number.
FINDSTR interprets also ¹, ², ³ as digit on using [0-9] which is the reason for using [0123456789] to really match only any of those 10 digit characters. Please read for more details about FINDSTR the articles SS64 - FINDSTR and What are the undocumented features and limitations of the Windows FINDSTR command?
FINDSTR outputs all file names containing four digits in range 1980 to 2029 to handle STDOUT of background command process.
Please read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters 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 those lines and processes them line by line. The default for option eol= (end of line) is a semicolon and so FOR would ignore all file names starting with a semicolon. For that reason eol=| is specified because a vertical bar cannot be used in a file name and so all captured file names are processed by FOR.
FOR would split up each file name on spaces/tabs by default and assigns only the first substring (token) to specified loop variable I. This splitting behavior is disabled by using delims= which defines an empty list of delimiters. tokens=* is not the same as this results in removing leading spaces from the file names. File names can start with one or more spaces although this is very unusual.
A file name can contain also exclamation marks ! which must be also taken into account on using delayed environment variable expansion. Each file name is passed to a subroutine for further processing it.
A loop is used to replace all occurrences of year assigned to loop variable Year by the year in round brackets until new file name is different to current file name because the substitution was indeed positive for searched string. for /L %%J in (1980,1,2029) do ... was not used as this loop can't be exited once having found the right year in file name.
After having found the year in file name it is checked if this year is not already embedded in parentheses to avoid renaming a file with name home vid (1987).mov to home vid ((1987)).mov. So for example home video 1998.avi is renamed finally to home video (1998).avi.
A file name containing two numbers with four or more digits is also not processed correct as this code can't find out what is the year in such a file name.
This batch code is not really fast, but it should work with the listed limitations.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
findstr /?
goto /?
if /?
ren /?
set /?
setlocal /?
See also Where does GOTO :EOF return to?
PS: File names with ( or ) in name make processing them with a batch file very often more difficult as in this case the file name must be always enclosed in double quotes like for file names containing a space character because of ( and ) have also a special meaning for Windows command processor cmd.exe as it can be seen on code above. See also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
I have a configuration file which I need for my bash script which has a layout:
A=C:/Example1/A
B=C:/Example2/B
C=C:/Example3/C
I want to use the same configuration file for my windows batch file. I need to convert the above file into variables which I have done using:
for /f "delims=" %%x in (test.txt) do (set "%%x")
How do I go about converting this file into variables while also converting all the forward slashes into backslashes?
Thanks!
add after your for line,
for /f "delims==" %%x in (q888.txt) do call set "%%x=%%%%x:/=\%%"
or, as a replacement for your existing for,
for /f "tokens=1*delims==" %%x in (q888.txt) do set "%%x=%%y"&call set "%%x=%%%%x:/=\%%"
(I used a file called q888.txt for testing)
The first smply executes a substitution, using a parsing trick. The second combines the set and substitution into one cascaded command by tokenising on = into %%x and %%y
This could be done with the following batch code:
#echo off
if not exist "test.txt" goto :EOF
setlocal EnableDelayedExpansion
for /F "usebackq tokens=1* delims==" %%I in ("test.txt") do (
if not "%%~J" == "" (
set "Value=%%~J"
set "Value=!Value:/=\!"
set "_%~n0_%%~I=!Value!"
)
)
echo The variables set from file are:
echo/
set "_%~n0_"
echo/
pause
endlocal
The batch file first checks if the file to process exists in current directory at all. The batch file processing is exited with a jump to predefined label EOF (end of file, requires enabled extensions which are enabled by default) in case of the file test.txt does not exist at all.
Next the file is read line by line with skipping empty lines and lines starting with a semicolon by command FOR which splits each line up into two strings.
The first string left of first equal sign is assigned to loop variable I. Everything right of first equal sign is assigned next loop variable J according to ASCII table.
The IF condition in the loop checks if a value is also defined for a variable. The value is assigned to an environment variable on which a string substitution is executed using delayed expansion to replace all / by \.
Then the modified value is assigned to an environment variable with a name starting with _, the name of the batch file, one more underscore and the string assigned to loop variable I read from the file.
For demonstration the variables with their values are finally output before the local variables are discarded on execution of last command ENDLOCAL.
I strongly recommend not assigning the values read from the file directly to environment variables whose name is completely also read from the file as this makes the batch file easy to manipulate by just modifying the contents of the text file. For example path=C:\Temp in text file would otherwise result in set "Path=C:\Temp" and from this point of batch file execution the running Windows command process would not find anymore any standard executable in directories defined by default in environment variable PATH like %SystemRoot%\System32.
A second variant which incorporates answer posted by Magoo with above batch code:
#echo off
if not exist "test.txt" goto :EOF
setlocal DisableDelayedExpansion
for /F "usebackq tokens=1* delims==" %%I in ("test.txt") do if not "%%~J" == "" set "_%~n0_%%~I=%%~J" & call set "_%~n0_%%~I=%%_%~n0_%%~I:/=\%%"
echo The variables set from file are:
echo/
set "_%~n0_"
echo/
pause
endlocal
The advantage of this variant is that delayed expansion is not needed for this solution which makes it possible to correct process also lines from file containing 1 or more exclamation marks on which first variant fails. And it is also a little bit faster, not noticeable faster for a human, but nevertheless a bit faster.
In both batch code blocks _%~n0_ can be replaced by (nearly) anything including also nothing although that is not recommended. Using just an underscore would be also possible as there are no environment variables defined by default by Windows which start with an underscore.
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 %~n0 (name of argument 0 - the batch file name - without path and without file extension).
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
set /?
setlocal /?
The simplest solution is to let the ~f FOR variable modifier put the full path in canonical form (including conversion of forward slashes to back slashes). I use the DELIMS and TOKENS options to split each line into the variable name and path so that I can apply the ~f to the path. It is important to use tokens=1* instead of tokens=1,2 just in case the path includes a = character.
for /f "delims== tokens=1*" %%A in (test.txt) do (set "%%A=%%~fB")
Note, however, that this strategy only works if your "test.txt" already contains full, absolute paths. If the file contains relative paths, then the ~f modifier will add drive and or folder values from the current directory to turn the relative path into an absolute path.