web-dev-qa-db-ja.com

文字列のハッシュを取得し、最終的にそれをハッシュと比較します

ワンライナーを実行して、文字列のハッシュを取得し、最終的にそれを後でコマンドの最後にコピーできるハッシュと比較できるようにしたいです。出力の何が問題なのかがわかれば、比較の方法を理解できると確信しています。今のところの目標は、ファイルがWindowsにネイティブであるため、certutilを使用してファイルのハッシュを出力することです。私はこれを実行します:

setlocal enabledelayedexpansion && set "firstLine=1" & for /f "skip=1delims=" %i in ('certutil -hashfile file.Zip SHA512') do (if firstLine==1 (set "x=%i"&set "firstLne=0"&echo "%x%")) & endlocal

そして最終的に:

C:\Users\John\Downloads>(if firstLine == 1 (set "x=9ba9467f05f1c7fa7161f857b0085461ce28401a2fe01a8062eec2254eaafc4b239fb3dc9298b5df5f27c2bb64618a8606a6885aa171604c541f4d5fe394b361"  & set "firstLne=0"  & echo "%x%" ) )  & endlocal

C:\Users\John\Downloads>(if firstLine == 1 (set "x=CertUtil: -hashfile command completed successfully."  & set "firstLne=0"  & echo "%x%" ) )  & endlocal

C:\Users\John\Downloads>

Do以降のすべてが文字列として再度実行されているようです。出力の最初の行はスキップされますが、ifステートメント内のすべてが実行できないため、forステートメントの次の2行は「実行」されます。

1
John

コードをデバッグするときは、コードを別々の行に展開すると便利です。 「バッチ」ファイルに行を配置して実行し、解決の試みをテストすることもできます。読みやすくするために、行った変更を複数行に展開して示します。

参考までに、コードを複数行に展開すると、次のようになります。

setlocal enabledelayedexpansion && set "firstLine=1"

for /f "skip=1delims=" %i in ('certutil -hashfile file.Zip SHA512') do (
    if firstLine==1 (
        set "x=%i"
        set "firstLne=0"
        echo "%x%"
    )
)

endlocal

上記をバッチファイルに配置するには、%i%%iに置き換える必要があることに注意してください。 DOS/CMDはそのように奇妙です。

問題と解決策。

あなたの質問には実際には2つの問題があります...そしてそれらの1つは他をマスキングしています。

あなたが誤解している問題は、あなたが「エコー」をオンのままにしていることです。説明すると、forは、ループの反復ごとに1回、コマンドとしてdo以降のすべてを実行します。 DOS/CMDでは、コマンドは実行前に端末にエコーされます。したがって、forが指定されたコマンドを実行するたびに、CMDはそれらのコマンドを端末に出力します。これは完全に正常です。しかし、それは混乱を招き、誤解を招く恐れがあります。この動作を変更するには、最初にECHO OFFを発行する必要があります。そのようです:

echo off
setlocal enabledelayedexpansion && set "firstLine=1"

for /f "skip=1delims=" %i in ('certutil -hashfile file.Zip SHA512') do (
    if firstLine==1 (
        set "x=%i"
        set "firstLne=0"
        echo "%x%"
    )
)

endlocal

結果は、いかなる種類の出力にもなりません。それは本当の問題のためです。その問題は、ifステートメントの条件です。 「変数マーカー」を省略しました。

firstLine==1は文字列firstLine1と比較し、それらが同じではないと判断します...もちろんそうではないためです。ただし、%firstLineの周りに配置することはできません。変数の展開が遅れるという落とし穴があります...!ではなく%を使用したいです。そのため、条件は!firstLine!==1である必要があります。

また、echo %x%echo !x!に修正する必要があります

何が起こっていたのかというと、実行したコマンドをエコーし​​ていました...しかし、if条件は常にfalseであったため、他には何もしませんでした。コマンドのエコーは、本当の問題を覆い隠す赤ニシンになりました。

文字列を比較するときは、角かっこ([])で囲むこともお勧めします。ほとんどのソースでは、代わりに二重引用符"を使用することを提案していますが、これにより、DOS/CMDが二重引用符を解析する方法に多くの問題が発生する可能性があります。また、ここにあるように何も使用しないと、変数に値がまったくない場合に問題が発生する可能性があります。例外は、EQUGTRなどの数値比較演算子を使用して数値比較を実行する場合です。

これはあなたに与えます:

echo off
setlocal enabledelayedexpansion && set "firstLine=1"

for /f "skip=1delims=" %i in ('certutil -hashfile file.Zip SHA512') do (
    if [!firstLine!]==[1] (
        set "x=%i"
        set "firstLne=0"
        echo "!x!"
    )
)

endlocal

しかし、これをワンライナーに変えることは、他のいくつかの理由で完全には機能しません。

その他の重大な問題

SETLOCALBATCHファイルでのみ機能します

コマンド自体からヘルプテキストを引用するには:

C:\Users\wolferz>help setlocal

バッチファイルで環境変更のローカリゼーションを開始します。 SETLOCALが発行された後に行われた環境変更は、バッチファイルに対してローカルです。以前の設定を復元するには、ENDLOCALを発行する必要があります。バッチスクリプトの終わりに達すると、そのバッチスクリプトによって発行された未処理のSETLOCALコマンドに対して暗黙のENDLOCALが実行されます。

しかしながら!コマンドラインのワンライナーで遅延展開を使用するための回避策があります。これにより、変数がその1行の実行に対してローカルに保たれます。コマンドの前にCMD /V:ON /Cを付け、setlocal enabledelayedexpansionendlocalを削除し、ワンライナーを二重引用符で囲みます。

これは何をしますか?

CMDは、コマンドプロンプトを実行するためのコマンドです。実際には、コマンドプロンプト内でコマンドプロンプトを実行できます。この場合、実行中に設定した環境変数をローカライズする効果があります。

/V:ONは、この新しいネストされたコマンドプロンプトで実行されるすべてのコマンドの遅延拡張を有効にします。

また、/Cは、二重引用符で囲まれたものが、この新しいネストされたコマンドプロンプトで実行するコマンドであることをCMDに通知します。

したがって、これにより、遅延拡張がオンになっている新しいネストされたコマンドプロンプト内でワンライナーが実行されます。すごい!しかし、まだ完全ではありません。

Forオプションにはスペースが必要です

"skip=1delims=""skip=1 delims="である必要があります。

i In firstLine In ifコードブロックがありません

firstLne=0firstLine=1である必要があります

修正されたワンライナー

この時点で、次のようになります。

echo off
set "firstLine=1"

for /f "skip=1 delims=" %i in ('certutil -hashfile file.Zip SHA512') do (
    if [!firstLine!]==[1] (
        set "x=%i"
        set "firstLine=0"
        echo "!x!"
    )
)

これは、(CMD /V:ON /Cを使用して)の1つのライナーに凝縮されます。

cmd /v:on /c "echo off & set "firstLine=1" & for /f "skip=1 delims=" %i in ('certutil -hashfile file.Zip SHA512') do ( if [!firstLine!]==[1] ( set "x=%i" & set "firstLine=0" & echo "!x!"" ) )

今、私はこれがあなたが望むことを正確に行うことを約束することはできません...しかし、構文エラーのために失敗することはありません。

また、最後のアドバイス:DOS/CMDではなくPowerShellを学びましょう。 DOS/CMDは、何十年にもわたる問題と下位互換性によって引き起こされる混乱した構文によって妨げられていることに加えて、最近のWindowsインストールではデフォルトのシェルではなく、今後10年間で完全に廃止される可能性があります。 PowerShellは、はるかにインテリジェントな構文とはるかに強力なスクリプト機能を備えた新たなスタートです。また、PowerShell Core 6の登場により、Windows以外のプラットフォームに移植できるようになりました。

1
Cliff Armstrong