batch-filecmdcertutil

BATCH - Write a specific line of command output as a variable without temp files?


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


Solution

  • 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.