1

I have multiple files with the json extension in a directory. These are used to generate some other files but that should only happen if the json files have changed.

So far I have the following measly script:

for %%f in (*.json) do (
            echo %%f
            certutil -hashfile %%f SHA256 >> HASH.txt
    )

I loop through all the json files, generate the SHA256 hash value for each and then append it to the HASH.txt file using certUitl. My problem comes from the output that tool returns namely (I have a German Win 10):

SHA256-Hash der Datei BLAH.json:
d8338f6f2649bcb358e56e0973fe2f5a886771e0debbdb0fefef35976a1b88ca
CertUtil: -hashfile-Befehl wurde erfolgreich ausgeführt.

I'd like to get my hands on the hash value. With the example above that would be

d8338f6f2649bcb358e56e0973fe2f5a886771e0debbdb0fefef35976a1b88ca

which is the second line of the output. I've searched for a solution how to get only a specific line of the output and skip the rest but all I can find was related to text files which doesn't seem to help much. Any ideas how to do that?

1

You could make your life easier when filtering certutil output with a
findstr /V ":"
the hash is the only line without a colon.

@Echo off
(for %%f in (*.json) do certutil -hashfile "%%~f" SHA256 | findstr /V ":" 
)> HASH.txt

A different approach gettting any specified line number:

@Echo off
Set Line=2
(for %%f in (*.json) do for /f "tokens=1,* delims=:" %%A in (
  'certutil -hashfile "%%~f" SHA256 ^| findstr /n "^" ^|findstr "^%Line%:"'
) Do echo %%B) > HASH.txt

Another variant storing hash and full file name in HASHjson.txt

@Echo off
Set "Hash=HASHjson.txt"
For %%F in (*.json) do For /f %%A in (
    'certutil -hashfile "%%~F" SHA256 ^| findstr /V ":"'
) Do Findstr "^%%A" "%HASH%" >NUL 2>&1 && (
    Echo HASH %%A already present in %Hash%
) || (
    Echo Add %%A %%~fF
    >>"%Hash%" Echo %%A %%~fF
)

Sample run:

> hashjson.cmd
Add eaf7df6fd6fe0719b6eafef8e4f74070684125822d03e6642a7e42fb425b442b A:\AIMavJ5IlpEmYCN.json
Add 24f1508e5e4a920b3233a37ce959e1e4f3fcd2a4ed90daec7879bc58af2a3a98 A:\AQ0G8KFw6KIj0JR.json

> hashjson.cmd
HASH eaf7df6fd6fe0719b6eafef8e4f74070684125822d03e6642a7e42fb425b442b already present in HASHjson.txt
HASH 24f1508e5e4a920b3233a37ce959e1e4f3fcd2a4ed90daec7879bc58af2a3a98 already present in HASHjson.txt
| improve this answer | |
  • The one-line solution doesn't allow any further customization of what's appended to the file. I thought my situation through again and it seems that also storing the file name (something like <filename>.json : <hash-value>) would be nice since I will be able to later on check if the prefix (the <filename>.json) from each line in the text file actually exists as a file on the filesystem and THEN recalculate its hash value. Especially with many files this seems to be a faster approach instead of blindly "hashing" all files every time. Storing the output in a variable is better in this case. – rbaleksandar Aug 9 '17 at 7:23
  • Added a variant storing hash and file name check if hash already present. – LotPings Aug 9 '17 at 10:13
  • I'll adapt this (so that it's the filename that is compared and not the hash) but still a bit thank you. Really helpful. And you do like one-liners, I can tell. :D – rbaleksandar Aug 9 '17 at 12:04
1

Using this answer I got my wish:

for %%f in (*.json) do (
    echo %%f
    setlocal enabledelayedexpansion
    set /a count=1 
    for /f "skip=1 delims=:" %%a in ('CertUtil -hashfile "%%f" SHA256') do (
      if !count! equ 1 (
        echo %%a >> HASH.txt
      )
      set/a count+=1
    )
    endlocal
)

The script outputs a file HASH.txt with the content:

227041a0fb9086c047692b5fc5e96437abe7a2664cc7e2dbcaa0bbe9c89cd886 bf4cf9401299ee68d24c88d1ff93152d4333956c58b305144b6469d86a8a803f 7b64b046d6121d9ef7db9e8f4a46b11b583a2c294501cf805120982d1a3b4b4c b5ff9fcd040d55ba77a75578d8bee5d26382d3ba952c38952fefcbe08cc10b9f a78294c3e0d7e6311ada02da35ea25240edc7b0f7c2e601e9840162e5fb5228c f3b21270b95d514e24a68658c77718cbe827487cdc32b46864a67172ae0afebc 0affcf4c3ba645c7aff44cfcb08fabd8769e84042b935907199d031601bf8e6a 3c197b9b991c6e9d66db0fee74d2fe2ea332e7f649459b08d89ba34e89ae9d27 5d676c597f4839bf05feace8d02fa1ec6e4c08d8b5f75266c0237cdd816f838b 323fc8da682b19aa11c250dd5129c7a581572e82ad88fb9c52f8c0679d7b8c12 49ec95e5b45c74638e035a37983de74da91d4aea78d35abe749ce9d5556c1c5d 84f1b5865d7949127bcc4a64e664ea75422cf4364c3e4acc2989e991cd6d0056 588f1173ac020b589a632d2d6dba61958243d78efe10afd2bb21cc49a33a146a 6a9e8887d25f5719568bf5321845d90afc800457947c3522f76717e469e3acea 2d4800aa9c58c477f4bfeab47a98bc35aab8446a53453a43ed40a553066549c9 f5e307075774bfa0eb9b65891c428799658d36e1b42be28cba67a920069a2c21 ad755392444209412dc49d172603c4f7fa693589a0390ae626470f96d8ebb22b 8ac329790c5df762bfb5d3437b2470fcd238b426ad60541ae89394f2620d867c cb6e4239db4cf53e236dcea70c245f053b636b70830d3f80946433f702049b95 0403ab147b4d4788ce84df45c52c33e004cbdf9c77ad14a7b844e56b18bf9d00 b9459119c08710713f5fb5816446149bf1314b34aa359c7ef35c12c1bf40ef27 235bd76cf4c4684714ec42847a1bd0a673eb07f5ee19c9bc86ce6f1a9d24449e 082cb0bee5ef63ec6c6ed5307000af5c89af2e75c6f9abdad9d3fafe37a725bf 3af4b54bd63686df3175cf9e2d4f24bb4600385230b86f8342efa9a039f31c3c 96ca349e805dae74aabb8252d2c4ffdabe17cabe0421742e1d12e672070f06b5 d8338f6f2649bcb358e56e0973fe2f5a886771e0debbdb0fefef35976a1b88ca

Basically the output from certUtil is treated like the content of a text file allowing the use of skip, delim etc.

| improve this answer | |
  • You could make your life easier when filtering certutil output with a findstr /V ":" the hash is the only line without a colon. So you could replace the whole inner for with CertUtil -hashfile "%%f" SHA256 | findstr /V ":" >> HASH.txt – LotPings Aug 8 '17 at 15:04
  • Can you post this as the answer. It's much shorter and elegant than what I currently have. :D – rbaleksandar Aug 8 '17 at 15:32
1

To speed it up, use a filter instead of looping:

@echo off
setlocal EnableDelayedExpansion
if exist hash.txt del hash.txt

for %%f in (*.json) do (
   for /f "delims=" %%_ in ('certutil -hashfile %%f SHA256 ^| find /v ":"') do (
      set x=%%_
      set x=!x: =!
   )
   echo !x! >> hash.txt
)

The second line in the innermost block (second for is not a loop!) discards the blanks in the hash value.
endlocal is implicit with the end of the batchfile.

| improve this answer | |
0

If I can answer with unix command (Microsoft annouce it will embed all unix utility program inside Windows 10 update and new release) :

  • sed -n "3p" "C:\Temp\my_file.txt" (output line 3 of file or StdIn)

Otherwise, it is quite easy to 'copy/paste' old unix faitfull program compile for Windows to do the job quite easy :

For before this Windows 10 update, you can use :

  • type "C:\Temp\my_file.txt" | findstr /n . | findstr /b ^3: (this will output line 3 of the file, but the line number will have to be strip out)
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.