1

The code of certutil-hash.cmd:

@echo off
certutil -hashfile "%~dpnx0" md5
pause>nul

I want to save the whole second line with the hash value in a variable. CMD-Output:

MD5 hash from C:\Users\ZerTerO\Desktop\certutil-hash.cmd:
9913d66d0b741494962e94ff540a8147
CertUtil: -hashfile command executed successfully.

The only solution would look like this to me:

@echo off
cls
call:hashfile
set "md5=%md5: =%"
echo.%md5%
pause>nul
exit

:hashfile
for /f "skip=1 tokens=1 delims=" %%a in ('certutil -hashfile "%~dpnx0" md5') do (set "md5=%%a" & goto:eof)

Is there a more elegant solution?

I only have to do this because Windows 7 and Windows 8 write spaces between the values:

set "md5=%md5: =%"

Thanks in advance...

ZerTerO

ZerTerO
  • 13
  • 3
  • There is no space between the pairs in the output from the latest version of Windows 10. However, it is worth mentioning that the `certutil` command is certainly different accross Windows versions. In Windows Vista, for instance, it does not accept a `HashAlgorithm` argument, only an `InFile`, and it will only be returned as `SHA1`. So in order to use the command, you should ideally determine the version of Windows to better predict its output. – Compo Jul 23 '20 at 17:14
  • In Windows 7 and Windows 8, certutil only supports md5. I tested it today. – ZerTerO Jul 23 '20 at 17:40
  • 1
    @ZerTerO - Did you remember that the hash needs to be capitalized? [Because Windows 7 absolutely can handle other hashes.](https://imgur.com/3Pi8W9S) – SomethingDark Jul 23 '20 at 17:53
  • Take a look at Dave Benham's [Hashsum.bat](https://www.dostips.com/forum/viewtopic.php?t=7592). – Squashman Jul 24 '20 at 03:21

1 Answers1

3

For the CertUtil versions which do work with a HashAlgorithm argument, (please see my comment), you can better deal with the loop, like this:

Set "md5="
For /F Delims^= %%G In ('CertUtil -HashFile "%~f0" MD5^|FindStr /VRC:"[^a-f 0-9]"')Do Set "md5=%%G"

Or more robustly like this:

Set "md5="
For /F Delims^= %%G In ('^""%__APPDIR__%certutil.exe" -HashFile "%~f0" MD5^|"%__APPDIR__%findstr.exe" /VRC:"[^a-f 0-9]"^"')Do @Set "md5=%%G"

Using to exclude any lines which contain a character which is not an alphabetic character a, b, c, d, e, or f; a numeric character 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9; or a space character, is in my opinion, more elegant than skipping the first line and then breaking out of the loop after the second line is processed.

As for your resulting variable, I'm not sure, in this case, whether I'd bother using set "md5=%md5: =%" to specifically remove possible spaces, I'd just use echo.%md5: =%. However if I was to be using the resulting variable often enough in my remaining script, I'd perform that task, within the labelled section:

@Echo Off
SetLocal EnableExtensions
ClS

Set "algo=MD5"

Call :HashFile "%~f0" %algo%

Echo(%hash%

Pause 1> NUL
GoTo :EOF

:HashFile
Set "hash="
For /F Delims^= %%G In ('^""%__APPDIR__%certutil.exe" -HashFile %1 %2 2^> NUL ^
 ^| "%__APPDIR__%findstr.exe" /V /R /C:"[^a-f 0-9]"^"') Do Set "hash=%%G"
If Not "%hash: =%" == "%hash%" Set "hash=%hash: =%"
Exit /B

Line 5 should ideally be part of a routine which determines if the version of on the OS supports two arguments. If it doesn't that line would read Set "algo=". You'll also notice that I have moved your hardcoded filename out of the loop, and used it as the first input argument to the Call command. This should make your script more modular, and IMO elegant. I also think that it's more elegant to check if the %hash% contains spaces, before asking to replace them with nothing. Elegance and efficiency are not necessarily the same.

Compo
  • 36,585
  • 5
  • 27
  • 39