Verifying MD5 hashes in Batch? - batch-file

I am writing a batch file and I need it to verify a file's MD5 hash before continuing on with the script. For example:
#echo off
if MD5 equ 79054025255fb1a26e4bc422aef54eb4 (
echo MD5 identical!
) else (
echo MD5 does not match.
)
Thanks!

The standard Windows utility CERTUTIL can be used to generate MD5 (and other) hashes.
For example:
certutil -hashfile yourFile MD5
However, the output (versions prior to Windows 10) will not be formatted as a single string of contiguous hex digits.
npocmaka used CERTUTIL in his batch utility to generate a single MD5 string.
In a similar manner, I also used CERTUTIL with my utility: HASHSUM.BAT - a batch file emulation of unix utilities like md5sum, shasum, etc.
HASHSUM.BAT does more than compute MD5 for a single file, but it is also convenient to use for what you want.
For example:
echo 79054025255fb1a26e4bc422aef54eb4 yourFileName | hashsum /c /ns
REM - There must be exactly two spaces between the MD5 hash and the filename
Will yield output like
---------- <stdin> ----------
OK: yourFileName
or
---------- <stdin> ----------
*FAILED: yourFileName
Or you can take total control of the output
echo 79054025255fb1a26e4bc422aef54eb4 yourFileName | hashsum /c /q && (
echo MD5 identical!
) || (
echo MD5 does not match.
)
Or, if you really wanted, you could capture the MD5 value and do the comparison yourself:
for /f %%N in ('hashsum /a md5 yourFileName') do set "MD5=%%N"
if %MD5% equ 79054025255fb1a26e4bc422aef54eb4 (
echo MD5 identical!
) else (
echo MD5 does not match.
)
I'm posting the code for the current version 1.4 of HASHSUM.BAT, but I don't promise to keep this code up-to-date. I recommend you get the code from the DOSTIPS site where I originally posted it. There you can also see a more thorough discussion of all the HASHSUM.BAT capabilities, as well as some more examples of usage.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
#goto :Batch
::::HASHNUM.BAT history
::::
:::: v1.4 2016-12-26 - Convert /A value to upper case because some Windows
:::: versions are case sensitive. Also improve JScript file
:::: read performance by reading 1000000 bytes instead of 1.
:::: v1.3 2016-12-17 - Bug fixes: Eliminate unwanted \r\n from temp file by
:::: reading stdin with JScript instead of FINDSTR.
:::: Fix help to ignore history.
:::: v1.2 2016-12-07 - Bug fixes: Exclude FORFILES directories and
:::: correct setlocal/endlocal management in :getOptions
:::: v1.1 2016-12-06 - New /V option, and minor bug fixes.
:::: v1.0 2016-12-05 - Original release
:::
:::HASHSUM [/Option [Value]]... [File]...
:::
::: Print or check file hashes using any of the following standard
::: hash algorithms: MD5, SHA1, SHA256, SHA384, or SHA512.
:::
::: HASHSUM always does a binary read - \r\n is never converted to \n.
:::
::: In the absence of /C, HASHSUM computes the hash for each File, and writes
::: a manifest of the results. Each line of output consists of the hash value,
::: followed by a space and an asterisk, followed by the File name. The default
::: hash alogrithm is sha256. File may include wildcards, but must not contain
::: any path information.
:::
::: If File is not given, then read from standard input and write the hash
::: value only, without the trailing space, asterisk, or file name.
:::
::: Options:
:::
::: /? - Prints this help information to standard output.
:::
::: /?? - Prints paged help using MORE.
:::
::: /V - Prints the HASHNUM.BAT version.
:::
::: /A Algorithm
:::
::: Specifies one of the following hash algorithms:
::: MD5, SHA1, SHA256, SHA384, SHA512
:::
::: /P RootPath
:::
::: Specifies the root path for operations.
::: The default is the current directory.
:::
::: /S - Recurse into all Subdirectories. The relative path from the root
::: is included in the file name output.
::: This option is ignored if used with /C.
:::
::: /I - Include the RootPath in the file name output.
::: This option is ignored if used with /C.
:::
::: /T - Writes a space before each file name, rather than an
::: asterisk. However, files are still read in binary mode.
::: This option is ignored if used with /C.
:::
::: /C - Read hash values and file names from File (the manifest), and verify
::: that local files match. File may include path information with /C.
:::
::: If File is not given, then read hash and file names from standard
::: input. Each line of input must have a hash, followed by two spaces,
::: or a space and an asterisk, followed by a file name.
:::
::: If /A is not specified, then the algorithm is determined by the
::: File extension. If the extension is not a valid algorithm, then
::: the algorithm is derived based on the length of the first hash
::: within File.
:::
::: Returns ERRORLEVEL 1 if any manifest File is not found or is invalid,
::: or if any local file is missing or does not match the hash value in
::: the manifest. If all files are found and match, then returns 0.
:::
::: /NE - (No Errors) Suppresses error messages when using /C.
:::
::: /NM - (No Matches) Suppresses listing of matching files when using /C.
:::
::: /NS - (No Summary) Suppresses summary information when using /C.
:::
::: /Q - (Quiet) Suppresses all output when using /C.
:::
:::HASHSUM.BAT version 1.4 was written by Dave Benham
:::maintained at http://www.dostips.com/forum/viewtopic.php?f=3&t=7592
============= :Batch portion ===========
#echo off
setlocal disableDelayedExpansion
:: Define options
set "options= /A:"" /C: /I: /P:"" /S: /T: /?: /??: /NE: /NM: /NS: /Q: /V: "
:: Set default option values
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
set "/?="
:getOptions
if not "%~1"=="" (
set "test=%~1"
setlocal enableDelayedExpansion
if "!test:~0,1!" neq "/" endlocal & goto :endOptions
set "test=!options:*%~1:=! "
if "!test!"=="!options! " (
endlocal
>&2 echo Invalid option %~1
exit /b 1
) else if "!test:~0,1!"==" " (
endlocal
set "%~1=1"
) else (
endlocal
set "%~1=%~2"
shift /1
)
shift /1
goto :getOptions
)
:endOptions
:: Display paged help
if defined /?? (
(for /f "delims=: tokens=*" %%A in ('findstr "^:::[^:] ^:::$" "%~f0"') do #echo(%%A)|more /e
exit /b 0
) 2>nul
:: Display help
if defined /? (
for /f "delims=: tokens=*" %%A in ('findstr "^:::[^:] ^:::$" "%~f0"') do echo(%%A
exit /b 0
)
:: Display version
if defined /v (
for /f "delims=: tokens=*" %%A in ('findstr /ric:"^:::%~nx0 version" "%~f0"') do echo(%%A
exit /b 0
)
:: If no file specified, then read stdin and write to a temp file
set "tempFile="
if "%~1" equ "" set "tempFile=%~nx0.%time::=_%.%random%.tmp"
if defined tempFile cscript //nologo //E:JScript "%~f0" "%temp%\%tempFile%"
if defined /P cd /d "%/P%" || exit /b 1
if defined /C goto :check
:generate
if defined tempFile cd /d "%temp%"
if not defined /A set "/A=sha256"
if defined /S set "/S=/s"
if defined /T (set "/T= ") else set "/T=*"
call :defineEmpty
if not defined /P goto :generateLoop
if not defined /I goto :generateLoop
if "%/P:~-1%" equ "\" (set "/I=%/P:\=/%") else set "/I=%/P:\=/%/"
set "rtn=0"
:generateLoop
(
for /f "delims=" %%F in (
'forfiles %/s% /m "%tempFile%%~1" /c "cmd /c if #isdir==FALSE echo #relpath" 2^>nul'
) do for /f "delims=" %%A in (
'certutil.exe -hashfile %%F %/A% ^| find /v ":" ^|^| if %%~zF gtr 0 (echo X^) else echo %empty%'
) do (
set "file=%%~F"
set "hash=%%A"
setlocal enableDelayedExpansion
set "file=!file:~2!"
if defined tempFile (
if !hash! equ X (
set "rtn=1"
echo ERROR
) else echo !hash: =!
) else (
if !hash! equ X (
set "rtn=1"
echo ERROR: !/I!!file!
) else echo !hash: =! !/T!!/I!!file:\=/!
)
endlocal
)
) || (
set "rtn=1"
echo MISSING: %/T%%1
)
shift /1
if "%~1" neq "" goto :generateLoop
if defined tempFile del "%tempFile%"
exit /b %rtn%
:check
if defined /Q for %%V in (/NE /NM /NS) do set "%%V=1"
set /a manifestCnt=missingManifestCnt=invalidCnt=missingCnt=failCnt=okCnt=0
:checkLoop
set "alogorithm=%/A%"
if defined tempFile set "tempFile=%temp%\%tempFile%"
for %%F in ("%tempFile%%~1") do call :checkFile "%%~F"
if defined tempFile del "%tempFile%"
shift /1
if "%~1" neq "" goto :checkLoop
if not defined /NS (
echo ========== SUMMARY ==========
echo Total manifests = %manifestCnt%
echo Matched files = %okCnt%
echo(
if %missingManifestCnt% gtr 0 echo Missing manifests = %missingManifestCnt%
if %invalidCnt% gtr 0 echo Invalid manifests = %invalidCnt%
if %missingCnt% gtr 0 echo Missing files = %missingCnt%
if %failCnt% gtr 0 echo Failed files = %failCnt%
)
set /a "1/(missingManifestCnt+invalidCnt+missingCnt+failCnt)" 2>nul && (
echo(
exit /b 1
)
exit /b 0
:checkFile
set /a manifestCnt+=1
if not defined /Q if defined tempfile (echo ---------- ^<stdin^> ----------) else echo ---------- %1 ----------
if not defined algorithm set "/A="
set "src=%~1"
if not defined /A echo *.md5*.sha1*.sha256*.sha384*.sha512*|find /i "*%~x1*" >nul && for /f "delims=." %%A in ("%~x1") do set "/A=%%A"
findstr /virc:"^[0123456789abcdef][0123456789abcdef]* [ *][^ *?|<>]" %1 >nul 2>nul && (
if not defined /NE if defined tempFile (echo *INVALID: ^<stdin^>) else echo *INVALID: %1
set /a invalidCnt+=1
exit /b
)
(
for /f "usebackq tokens=1* delims=* " %%A in (%1) do (
set "hash0=%%A"
set "file=%%B"
if defined /A (call :defineEmpty) else call :determineFormat
setlocal enableDelayedExpansion
set "file=!file:/=\!"
for /f "tokens=1* delims=" %%C in (
'certutil.exe -hashfile "!file!" !/A! ^| find /v ":" ^|^| if exist "!file!" (echo !empty!^) else echo X'
) do set "hash=%%C"
if /i "!hash0!" equ "!hash: =!" (
if not defined /NM echo OK: !file!
endlocal
set /a okCnt+=1
) else if !hash! equ X (
if not defined /NE echo *MISSING: !file!
endlocal
set /a missingCnt+=1
) else (
if not defined /NE echo *FAILED: !file!
endlocal
set /a failCnt+=1
)
)
) 2>nul || (
if not defined /NE echo *MISSING: %1
set /a missingManifestCnt+=1
)
exit /b
:determineFormat
if "%hash0:~127%" neq "" (
set "/A=SHA512"
) else if "%hash0:~95%" neq "" (
set "/A=SHA384"
) else if "%hash0:~63%" neq "" (
set "/A=SHA256"
) else if "%hash0:~39%" neq "" (
set "/A=SHA1"
) else set "/A=MD5"
:defineEmpty
if /i "%/A%"=="md5" (
set "empty=d41d8cd98f00b204e9800998ecf8427e"
set "/A=MD5"
) else if /i "%/A%"=="sha1" (
set "empty=da39a3ee5e6b4b0d3255bfef95601890afd80709"
set "/A=SHA1"
) else if /i "%/A%"=="sha256" (
set "empty=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
set "/A=SHA256"
) else if /i "%/A%"=="sha384" (
set "empty=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"
set "/A=SHA384"
) else if /i "%/A%"=="sha512" (
set "empty=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
set "/A=SHA512"
) else (
echo ERROR: Invalid /A algorithm>&2
(goto) 2>nul&exit /b 1
)
exit /b
************* JScript portion **********/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var out = fso.OpenTextFile(WScript.Arguments(0),2,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1000000);
out.Write(chr);
}

try with MD5.BAT :
set "file=c:\myfile.ext"
call md5.bat "%file%" md5
if "%md5%" equ "79054025255fb1a26e4bc422aef54eb4" (
echo MD5 identical!
) else (
echo MD5 does not match.
)

Related

How to a take a input as a varable in a batch file

#echo off
:loop
set /p input="> "
echo %input%|find /i "my name is" >nul && echo That's a nice name.
How would I take input and if it has my name is in it it stores the name as a variable.
My goal is to take there name and store it as a variable and use it when it reply.
As already stated, if you're going to be taking input via set /p you want to be validating or restricting what a user can input. The below is a function that can be used to assign variables that match permitted findstr regex patterns.
:GetIN [ReturnVar] ["qouted findstr search pattern"] [Optional:"/C" - required for patterns with spaces]
#Echo off
SETLOCAL EnableExtensions EnableDelayedExpansion
rem - lines prefixed with ::: form part of this scripts Help Documentation. Do not remove.
> "%TEMP%\%~n0_help.~tmp" (For /F "Delims=" %%G in ('Type "%~f0"^| Findstr.exe /BLIC:":::" 2^> nul ')Do For /F "Tokens=1* Delims=:" %%v in ("%%G")Do Echo/^|%%v)
:::================================================================================================================
::: [ GetIn Input function or standalone script for validating input with findstr regular expressions. ]
::: Version = 2.2 Release = October 2020 Author = T3RRY
:::========================================= ^^^! important notes on usage ^^^! =========================================
::: ^^^! Pipes not supported ^^^!
::: ^ + Not suited for use with pipes due to forced loop for failed matches
::: < ^^^! > + Test your regex pattern to ensure it is valid for the required input
::: v format to prevent your user/s being stuck in a goto loop.
::: Error output for invalid search patterns is not displayed.
::: + EXIT.CODES - Errorlevel assessment after call:
::: - Errorlevel 3 - User has failed to provide input * OR * regex pattern is
::: invalid * OR * GetIn is a called label in a file that has recieved input
::: from a pipe that has not been handeled before the call.
::: - Errorlevel 2 - GetIn is a script that has been invoked by a pipe
::: - Errorlevel 1 - GetIn help has been Invoked * OR * GetIn has been called
::: without Arg 2 for Regex Pattern
::: - Errorlevel 0 - Input has been assigned that matches the regex Pattern
:::================================================================================================================
rem [* Display GetIn Usage with findstr regex info and practical examples *]
>> "%TEMP%\%~n0_help.~tmp" (
Echo/^| USAGE: Call %~0 [ReturnVar] ["findstr search pattern"] [Optional:"/C" -required for patterns with spaces]
Echo/^|================================================================================================================
Echo/^| Examples [ Description is replaced with Arg 1-ReturnVar ]:
Echo/^|
Echo/^| Call %~0 4-Digit-Number "[0-9][0-9][0-9][0-9]"
Echo/^| Call %~0 Alphanumeric-String "[0-9a-zA-Z]*"
Echo/^| Call %~0 semicolon-delimited-hexadecimal-String "[0-9a-fA-F][0-9a-fA-F][:][0-9a-fA-F][0-9a-fA-F]"
Echo/^| Call %~0 AlphaString-with-3-Digit-Numeric-suffix "[a-zA-Z]*[0-9][0-9][0-9]"
Echo/^| Call %~0 list-of-delimited-words "[a-zA-Z, ]*" /C
Echo/^| Call %~0 pair-of-delimited-numbers "[0-9]*[, ][0-9]*" /C
Echo/^|
Echo/^| ^^ Limit of 15 character class expressions applies
Echo/^| ^< ^^^! ^> Case sensitive classes should use actual letters as [STRING] instead of range [A-Z]
Echo/^| v See: https://stackoverflow.com/a/20159191/12343998
Echo/^| -Note: %~0 does not support Input from pipes.
Echo/^|----------------------------------------------------------------------------------------------------------------
For /F "Delims=" %%G in (' Findstr.exe /? ^| More +33 ') Do Echo/^| %%G
Echo/^| https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/findstr
Echo/^| recommended reference: https://stackoverflow.com/questions/8844868/
Echo/\================================================================================================================
)
(Set LF=^
%= Linefeed. do not modify. =%)
Set "Exit.Code=0"
rem [ test if standalone script - if so test if accessed by pipe; reject execution if true ]
If "%~0" == "%~n0" For %%G in (!cmdcmdline!)Do Echo/%%~G|Findstr.exe /LIC:"/S" > nul 2> nul && (Set "Exit.Code=2" & Goto :Usage)
:retry
Del /Q "%TEMP%\%~n0_validate.~tmp" 2> nul
If !Exit.Code! GTR 5 Goto :Usage
Set "rVar=" & Set "_C=0" & Set "_S=0"
If "%~2" == "" GOTO :Usage
Setlocal DISABLEdelayedExpansion & rem ::: dispay occurances of carets [^] in pattern
Endlocal & Set "Pattern=%~2"
rem /* optional prompt */
rem /* Set /P "rVar=Input Format for %1:!LF!!Pattern!!LF!!LF!Enter %1: " */
> "%TEMP%\%~n0_validate.~tmp" (Echo/!rVar!)
If /I "%~3" == "/C" (
TYPE "%TEMP%\%~n0_validate.~tmp"| findstr.exe /RXC:"%~2" > nul || (Set /A "Exit.Code+=1" & <nul Set /P"= ^! Invalid value.!LF!" & Goto :Retry)
) Else (
TYPE "%TEMP%\%~n0_validate.~tmp"| findstr.exe /RX "^%~2$" >nul || (Set /A "Exit.Code+=1" & <nul Set /P"= ^! Invalid value.!LF!" & Goto :Retry)
)
Echo/%1: [!rVar!] Accepted
If defined echostate Echo on
ENDLOCAL & #Set "%~1=%rVar%"
Del /Q "%TEMP%\%~n0_validate.~tmp"
Exit /B 0
:Usage
Mode 1000
If "%Exit.Code%" == "0" (Set "Exit.Code=1")
If "%Exit.Code%" == "2" (
>> "%TEMP%\%~n0_help.~tmp" (
Echo/------------------------------- ^^! pipes not supported ^^! -------------------------------
Echo/=================================================================================================================
))
If %Exit.Code% GTR 5 (
>> "%TEMP%\%~n0_help.~tmp" (
Echo/------------------------------- ^^! Attempt limit exceeded. Input rejected ^^! -------------------------------
Echo/------------------------------- ^^! pipes not supported ^^! -------------------------------
Echo/=================================================================================================================
)
Set "Exit.Code=3"
)
Type "%TEMP%\%~n0_help.~tmp" | ( More )
Del /Q "%TEMP%\%~n0_help.~tmp" 2> nul
ENDLOCAL & exit /B %Exit.Code%
rem ::: [ ------------------------------------------------------------------------------------------------- End GetIn ]
set "name=Input error"
if "%input:my name is =%" neq "%input%" set "name=%input:my name is =%"
echo %name%
The if statement tests whether the string my name is is part of the value in input. If it is, then remove that string and the remainder is the name.
But, following #Compo's comments, this is very vulnerable, and in any case, who on Earth would reply my name is Fred Nurk to such a question? Fred Nurk would be the more likely reply.

Batch replacing everything after the last underscore character to the beginning of the file extension

So I want to replace this:
Brand_Name_18.jpg
With this
Brand_Name_close-up.jpg
I tried
#echo off &setlocal
for /f "tokens=1-3*delims=_" %%i in ('dir /b /a-d *.jpg ^| find /i /v "_0_"') do ren "%%i_%%j" "%%i_%%j_0_"
but had no success. I also played with the PowerShell and was able to replace some characters there, but not particularly what I want. The characters I wanted to add were appearing at the end of the file extension, which broke everything...
Dir | rename-item -newname { $_.Name + "_close-up }
.
get-childitem -recurse | rename-item -newname { $_.name -replace "_","_close-up" }
I would also like to make the batch appear in the window when I right click, so I could apply it to a file that I need it to be applied on. So that has probably something to do with the registry, right?
SOLUTION thanks to T3RR0R
#echo off & Setlocal EnableDelayedExpansion
REM Change Directory and Remove /R recursive switch as / if necessary.
For /R %%A in (Brand_Name_*.jpg) do (
REN %%~nxA Brand_Name_close-up.jpg
)
pause
This will do the trick. If you're processing a large subset of files, you'll be best off modifying it to rename using a suitable naming convention that doesn't require input.
#echo off & Setlocal EnableDelayedExpansion
Goto :Main
:ModString <string length> <string> <target substring> <prefix VarName> <Substring Length>
Set String=%2
For /L %%L in (%1,-1,1) do (
Set Test=!String:~%%L,%5!
If /I "!Test!"=="%3" (
Set %4=!String:~0,%%L!
Exit /B
)
)
Set /P "prefix=%3 not found. Set prefix:"
Exit /B
:StrLen <resultVar> <string>
(
Setlocal EnableDelayedExpansion
(set^ tmp=%~2)
If Defined tmp (
Set "len=1"
For %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) Do (
If "!tmp:~%%P,1!" NEQ "" (
Set /a "len+=%%P"
Set "tmp=!tmp:~%%P!"
)
)
) else (
Set "len=0"
)
)
(
ENDLOCAL
Set "%~1=%len%"
exit /b
)
:Main
Set /P "Searchfolder=< Drop & Drag or Enter Parent Folder:"
IF Not Exist "!Searchfolder!" Goto :Main
Set /P "Extension=Extension:"
Set /P "SearchString=Enter String to Locate:"
For %%V in (Extension SearchString) do IF "!%%V!"=="" Goto :Main
For /R %%A in (*!SearchString!*.!Extension!) do (
Call :StrLen substringLen "!SearchString!"
Call :StrLen prefixLen "%%~nA"
Call :ModString !prefixLen! %%~nA !SearchString! prefix !substringLen!
Echo(Rename: %%~nxA
Set /P "Newstring="
REN %%~nxA !prefix!!SearchString!!Newstring!%%~xA
)
CHOICE /N /C ny /M "more N/Y?"
IF errorlevel 2 (
Set Extension=
Set Searchstring=
Goto :main
)
exit
Update
With the only known in your usage being that the underscore is the last instance in the name / string that you wish to retain in the new filename, the function needs to be modified to find it's position in the string using a String Length function in order to Modify the string from that point using Substring Modification.
The StrLen function is the work of Jeb
I've modified the code to now take a bit more input, and be more flexible. You can now use it with other extensions and define the String to search for at runtime.

use batch file to read and write version number into a file

I'm trying to write a batch file to increment version number every time i run it, but I'm confuse about "for /f" and the behaviour of the batch file when I test it by using command prompt. Please help me with this.
here's my batch file
for /f "tokens=2,3 " %%i in (version.h) do (
set /a number=%%j+1
echo %%i
echo %%j
echo %number%
if %%i==REVISION (
echo line1
echo #define %%i "%number%" >> temp.file
) else (
echo line2
echo #define %%i %%j >> temp.file
)
)
del /q version.h
ren temp.file version.h
and here's my version.h
#define MAJOR "1"
#define MINOR "0"
#define REVISION "242"
The batch file can only produce correct result at the first run(#define REVISION "243"), and has a weird result at the second run(#define REVISION "0"). The third run's result is correct("#define REVISION "244"), but the forth run it goes weird again(#define REVISION "1"), and so on.
It seems that I didn't parse the correct string so I cannot have correct result every time.
I typed "for /?" in the command prompt and read the help message, but still cannot understand it, please help me with this. Any reply would be appreciate!
The following script does what you want and preserves empty lines and special characters present in the target file. There is no temporary file involved, the file modification is accomplished in-place.
So here is the code -- reference the explanatory remarks for how it works:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "FILE=%~1" & rem // (provide the target file as command line argument)
set "DIRECTIVE=#define" & rem // (name of the directive to search)
set "DEFINITION=REVISION" & rem // (name of the definition to search)
set "CASESENS=" & rem // (set to non-empty for case-sensitive searches)
set "QUOTED="^" & rem // (set to non-empty for quoting returned number)
rem // Resolve arguments and options:
if not defined FILE ((>&2 echo ERROR: no file specified!) & exit /B 1)
if defined CASESENS (set "CASESENS=") else (set "CASESENS=/I")
if defined QUOTED (set "QUOTED="^") else (set "QUOTED=")
rem // Loop through all lines in the target file:
setlocal EnableDelayedExpansion
for /F delims^=^ eol^= %%L in ('
rem/ /* Prefix lines with line numbers to not lose empty ones; ^& ^
rem/ after having read file, deplete its entire content: */ ^& ^
findstr /N /R "^^" "!FILE!" ^& ^> "!FILE!" break
') do (
endlocal
set "FLAG="
set "LINE=%%L"
rem // Split line into three tokens:
for /F "tokens=1-3 eol= " %%I in ("%%L") do (
set "FIELD1=%%I"
set "FIELD2=%%J"
set "NUMBER=%%~K"
setlocal EnableDelayedExpansion
rem // Check first token for matching directive name:
if %CASESENS% "!FIELD1:*:=!"=="!DIRECTIVE!" (
rem // Check second token for matching definition name:
if %CASESENS% "!FIELD2!"=="!DEFINITION!" (
endlocal
rem // Increment number of third token:
set /A "NUMBER+=1"
set "FLAG=#"
setlocal EnableDelayedExpansion
)
)
endlocal
)
setlocal EnableDelayedExpansion
rem // Write output line into target file:
>> "!FILE!" (
rem // Check whether dirctive and definition matched:
if defined FLAG (
rem // Match found, so write new line with incremented number:
echo(!DIRECTIVE! !DEFINITION! %QUOTED%!NUMBER!%QUOTED%
) else (
rem // No match found, so write original line:
echo(!LINE:*:=!
)
)
)
endlocal
endlocal
exit /B

Split string in batch file

I am new to using batch files so could someone please help me split a string i am getting from a file.
I am using %USERPROFILE% to get my string.
The string is: "C:\Users\nicholas"
I would like to keep the C:\ part, but get rid of the \Users\nicholas part.
for /f "tokens=2 delims=\" %A in ('set userprofile') do echo %A\
See for /?
Also
echo %userprofile:~0,3%
If you carefully read the output of set /?, you'll find the answer:
May also specify substrings for an expansion.
%PATH:~10,5%
would expand the PATH environment variable, and then use only the 5
characters that begin at the 11th (offset 10) character of the expanded
result.
So, you can use something like this to get the first 3 characters of your string:
> echo %userprofile:~0,3%
C:\
I As you need the drive where where the users are located you can use directly
%systemdrive% variable - this is the drive where the windows is installed
II the easiest way to get a drive from path:
for %%a in ("%userprofile%") do echo %%~da\
%~da - expands a path to its drive only
III over-complicated but powerful way (split function that can be used for a different things):
#echo off
call :split "%userprofile%" "\" 1 drive
echo %drive%\
goto :eof
:split [%1 - string to be splitted;%2 - split by;%3 - possition to get; %4 - if defined will store the result in variable with same name]
::http://ss64.org/viewtopic.php?id=1687
setlocal EnableDelayedExpansion
set "string=%~2%~1"
set "splitter=%~2"
set /a position=%~3
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!string:%%~R%%~R=%%~L!"
set "var=!var:%%~R=%%~L!"
if "!var!" EQU "!string!" (
echo "%~1" does not contain "!splitter!" >&2
exit /B 1
)
)
)
if "!var!" equ "" (
endlocal & if "%~4" NEQ "" ( set "%~4=")
)
if !position! LEQ 0 ( set "_skip=" ) else (set "_skip=skip=%position%")
for /f "eol= %_skip% delims=" %%P in (""!var!"") DO (
if "%%~P" neq "" (
set "part=%%~P"
goto :end_for
)
)
set "part="
:end_for
if not defined part (
endlocal
echo Index Out Of Bound >&2
exit /B 2
)
endlocal & if "%~4" NEQ "" (set %~4=%part%) else echo %part%
exit /b 0

Windows batch: How to remove last part of folders names

In a batch file I get a folder with a list of subfolders with an unknown number of underscores e.g.:
a_b_10
c_d_e_2
f_17
I need to remove the last token of the names i.e.
a_b
c_d_e
f
Thanks
You could try to get the last part with an underscore and then remove this from your string, but this only works when the last part is unique.
In your sample the last part seems to be always a number in spite of the other parts.
This uses the trick to parse parts of a string by replace the delimiter by a linefeed character.
#echo off
setlocal EnableDelayedExpansion
set LF=^
for /F "delims=" %%X in ('dir /b /AD') do (
call :removeLastPart "%%~X"
)
exit /b
:removeLastPart
set "str=%~1"
for %%L in ("!LF!") DO (
for /F "delims=" %%P in ("!str:_=%%~L!") do set "last=%%P"
)
echo The last part is '!last!'
REM ** now remove the last part by replacing with nothing **
set "str=!str:_%last%=!"
echo !str!
exit /b
#echo off
set "root_dir=C:\scriptests"
setlocal enableDelayedExpansion
for /f %%d in ('dir /b /a:d *_*') do (
call :lastindexof "%%d" _ lio
set "f_name=%%~d"
echo renaming !f_name!
for %%S in (!lio!) do ren !f_name! !f_name:~0,%%S!
)
endlocal
exit /b 0
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal disableDelayedExpansion
set "str=%~1"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
setlocal enableDelayedExpansion
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
echo -1
)
call :strlen0.3 str strlen
call :strlen0.3 last_part plen
call :strlen0.3 splitter slen
set /a lio=strlen-plen-slen
endlocal & set lio=%lio%
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b 0
:strlen0.3 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%A in (2187 729 243 81 27 9 3 1) do (
set /A mod=2*%%A
for %%Z in (!mod!) do (
if "!s:~%%Z,1!" neq "" (
set /a "len+=%%Z"
set "s=!s:~%%Z!"
) else (
if "!s:~%%A,1!" neq "" (
set /a "len+=%%A"
set "s=!s:~%%A!"
)
)
)
)
endlocal & if "%~2" neq "" (set %~2=%len%) else echo **%len%**
exit /b
This solution will create the "renfolders.bat.txt" file for you to check in notepad, and run it as a batch file if you are happy with it.
This uses a helper batch file called repl.bat - download from: https://www.dropbox.com/s/qidqwztmetbvklt/repl.bat
Place repl.bat in the same folder as the batch file or in a folder that is on the path.
dir *_* /b /s /ad |repl ".*\\(.*)_.*" "ren \q$&\q \q$1\q" xa >"renfolders.bat.txt"
I just figured out this solution:
for /f %%f in ('dir /b /AD c:\MainFolder\') do (
set var=%%f
set var=!var:_= !
set /a count=0
for %%i in (!var!) do (set /a count+=1)
set /a count2=0
for %%i in (!var!) do (
set /a count2+=1
if /I !count2! equ 1 (set var2=%%i) else if not !count2! equ !count! (set var2=!var2!_%%i)
)
echo !var2!
)
Here is a novel approach
For each folder containing at least one _ in name:
create a temporary empty file with the same name
rename the temporary file, stripping off everything after the final _
read the new name into a variable
strip off the final _ from the name
For an explanation of how the rename works, see How does the Windows RENAME command interpret wildcards?
#echo off
setlocal enableDelayedExpansion
set "loc=%temp%\removeToken"
md "%loc%"
for /d %%F in (*_*) do (
copy nul "%loc%\%%F" >nul
ren "%loc%\*" "*_"
for %%A in ("%loc%\*") do set "new=%%~nxA"
del /q "%loc%\*"
echo old=%%F
echo new=!new:~0,-1!
echo(
)
rd "%loc%"
EDITED - Wrong copy of the code posted
It separates the elements of the directory name and iterates over them discarting the last element
#echo off
setlocal enabledelayedexpansion
for /d %%a in (*_*) do (
set "old=%%~na" & set "new=" & set "previous="
for %%b in ("!old:_=" "!") do (
if not defined previous (
set "previous=%%~b"
) else if not defined new (
set "new=!previous!"
) else set "new=!new!_!previous!"
set "previous=%%~b"
)
echo ren "%%~fa" "!new!"
)
endlocal

Resources