コマンドプロンプト SCP バッチ (ハッシュ値比較あり)

【概要】
Windosのコマンドプロンプトにて、LinuxとWindows間でファイルをSCP(送受または受信)でコピーします。
コマンドプロンプトのSCPコマンドおよびSSHコマンドを利用します。
SCP後に、Windows側とLinux側の各ファイルのハッシュ値(SHA1)を比較して正常にコピーされているか確認します。

【動作環境】
下記で動作確認を行いました。
■Windows側:Windows10 Pro、Windows11 Pro
 ※コマンドプロンプトでSSH、SCPが使えるかどうかは、コマンドプロンプトにて下記を実施してご確認ください。
  「ssh」を入力してEnter→「usage: ssh」と表示
  「scp」を入力してEnter→「usage: scp」と表示
■Linux側:Ubuntu 22.04、Amazon Linux 2023

【作成手順】
1.任意のフォルダ名でフォルダを作成します。
2.テキストエディタ(メモ帳など)を開きます。
3.下記の「【scp_winlin3.bat】」の内容をテキストに貼り付けます。
4.以下の箇所を更新します。記入方法は該当箇所の記載例(「example」の箇所)を参照ください。        
  ※SCP_SOURCE、SCP_TARGETのどちらか片方はLinux、もう片方はWindowsの記載方法で記載する必要があります。

set SCP_SOURCE=""
set SCP_TARGET=""
set SCP_SSH_KEY=""

5.作成したテキストを、作成したフォルダにファイル名「scp_winlin3.bat」で保存します。

【scp_winlin3.bat】

@echo off
setlocal enabledelayedexpansion

REM ################################################
REM ### Windows batch for Windos-Linux scp
REM ################################################

REM change directory
cd /d %~dp0

REM logfile path
set BAT_LOGFILE="%~dp0scp_winlin3_bat.log.txt"

REM scp source
REM example 1(set SCP_SOURCE="C:\Users\testuser\Desktop\scp_source")
REM example 2(set SCP_SOURCE="ubuntu@192.168.1.10:/home/ubuntu/scp_source")
set SCP_SOURCE=""

REM scp target
REM example 1(set SCP_TARGET="ubuntu@192.168.1.10:/home/ubuntu/scp_target")
REM example 2(set SCP_TARGET="C:\Users\testuser\Desktop\scp_target")
set SCP_TARGET=""

REM ssh key
REM example (set SCP_SSH_KEY="C:\Users\testuser\Desktop\scp_key\sshkey.pem")
set SCP_SSH_KEY=""

echo %date% %time% INFO: SCP Start[%SCP_SOURCE% to %SCP_TARGET%] >> %BAT_LOGFILE%

REM run scp
scp -r -i %SCP_SSH_KEY% %SCP_SOURCE% %SCP_TARGET% >> %BAT_LOGFILE% 2>&1

REM return code check
if %errorlevel% equ 0 (
  echo %date% %time% INFO: SCP End[%SCP_SOURCE% to %SCP_TARGET%] >> %BAT_LOGFILE%
) else (
  echo %date% %time% ERROR: SCP End[%SCP_SOURCE% to %SCP_TARGET%] Return code %errorlevel% >> %BAT_LOGFILE%
  echo %date% %time% INFO: Script End. >> %BAT_LOGFILE%
  exit /b
)


if not exist %~dp0scp_winlin3_hashfies (
  mkdir %~dp0scp_winlin3_hashfies
)

REM get windows hash value
if exist %~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt (
  copy %~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt %~dp0scp_winlin3_hashfies\winlin3_sha1_windows_old.txt
  type nul > %~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt
)

set WINDOWS_PATH=
echo %SCP_SOURCE% | find "/" >NUL
if not ERRORLEVEL 1 (
  call :SUB_LAST_DIR %SCP_SOURCE:/=\%
  echo !LAST_DIR!
  set WINDOWS_PATH=!SCP_TARGET!\!LAST_DIR!
) else (
  set WINDOWS_PATH=%SCP_SOURCE%
)

set ROW_COUNT=0
for /r %WINDOWS_PATH% %%i in (*.*) do (
  set ROW_COUNT=0
  for /f "usebackq" %%j in (`certutil -hashfile %%i sha1`) do (
    set /a ROW_COUNT=!ROW_COUNT!+1
    if !ROW_COUNT!==2 (
      echo %%j %%~nxi >> %~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt
    )
  )
)

REM get linux hash value
if exist %~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt (
  copy %~dp0scp_winlin3_hashfies\winlin3_sha1_linux.txt %~dp0scp_winlin3_hashfies\winlin3_sha1_linux_old.txt
  type nul > %~dp0scp_winlin3_hashfies\winlin3_sha1_linux.txt
)

echo %SCP_SOURCE% | find "\" >NUL
if not ERRORLEVEL 1 (
  call :SUB_LINUX_INFO %SCP_TARGET%
  call :SUB_LAST_DIR %SCP_SOURCE%
  echo !LAST_DIR!
  set LINUX_PATH=!LINUX_PATH!/!LAST_DIR!
) else (
  call :SUB_LINUX_INFO %SCP_SOURCE%
)

ssh -i %SCP_SSH_KEY% %LINUX_USER%@%LINUX_IP% for file in `find %LINUX_PATH% -maxdepth 5 -type f` ; do if [[ -n ${file} ]] ; then  if [[ ^^! \"${file}\" =~ \"/.\" ]] ; then  sha1sum ${file}; fi; fi ; done >> %~dp0scp_winlin3_hashfies\winlin3_sha1_linux.txt


REM compare hash
if exist %~dp0scp_winlin3_hashfies\winlin3_sha1_check.txt (
  copy %~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt %~dp0scp_winlin3_hashfies\winlin3_sha1_check_old.txt
  type nul > %~dp0scp_winlin3_hashfies\winlin3_sha1_check.txt
)

set ROW_COUNT_1=0
for /f "tokens=1,2" %%a in (%~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt) do (
  set /a ROW_COUNT_1=!ROW_COUNT_1!+1
)

set ROW_COUNT_2=0
for /f "tokens=1,2" %%i in (%~dp0scp_winlin3_hashfies\winlin3_sha1_linux.txt) do (
  set /a ROW_COUNT_2=!ROW_COUNT_2!+1
)

set ERR_FLG=0
if %ROW_COUNT_2% neq %ROW_COUNT_1% (
  set ERR_FLG=1
  echo CHEKC_NG ROW COUNT IS NOT MATCH >> %~dp0scp_winlin3_hashfies\winlin3_sha1_check.txt
  echo %date% %time% ERROR: SCP HASH CHECK[ROW COUNT IS NOT MATCH] >> %BAT_LOGFILE%
)

set ROW_COUNT=0
for /f "tokens=1,2" %%a in (%~dp0scp_winlin3_hashfies\winlin3_sha1_windows.txt) do (
  set ROW_COUNT=0
  for /f "tokens=1,2" %%i in (%~dp0scp_winlin3_hashfies\winlin3_sha1_linux.txt) do (
    set /a ROW_COUNT=!ROW_COUNT!+1
    if "%%a" == "%%i" (
      echo CHECK_OK WINDOWS:%%a LINUX:%%i %%b >> %~dp0scp_winlin3_hashfies\winlin3_sha1_check.txt
      set ROW_COUNT=0
    )
    if !ROW_COUNT! geq !ROW_COUNT_2! (
      set ERR_FLG=1
      echo CHEKC_NG WINDOWS:%%a LINUX:%%i %%b >> %~dp0scp_winlin3_hashfies\winlin3_sha1_check.txt
    )
  )
)

if %ERR_FLG% gtr 0 (
  echo %date% %time% ERROR: SCP HASH CHECK[HASH IS NOT MATCH] >> %BAT_LOGFILE%
) else (
  echo %date% %time% INFO: SCP HASH CHECK OK >> %BAT_LOGFILE%
)

echo %date% %time% INFO: Script End. >> %BAT_LOGFILE%

exit /b


:SUB_LAST_DIR
set LAST_DIR=%~n1
exit /b


:SUB_LINUX_INFO
set LINUX_USER=
set LINUX_IP=
set LINUX_PATH=
for /f "usebackq delims=: tokens=1" %%i in (`echo %1`) do (
  for /f "usebackq delims=@ tokens=1" %%j in (`echo %%i`) do (
    set LINUX_USER=%%j
    set LINUX_USER=!LINUX_USER:~1!
  )
  for /f "usebackq delims=@ tokens=2" %%k in (`echo %%i`) do (
    set LINUX_IP=%%k
  )
)
for /f "usebackq delims=: tokens=2" %%l in (`echo %1`) do (
  set LINUX_PATH=%%l
  set LINUX_PATH=!LINUX_PATH:~0,-1!
)
exit /b

【実行準備】
※初回実行時に1度だけ、SCP先のサーバへの接続許可をしておく必要があります。
 known_hostsファイルに登録しておくためですので、一度実施したら繰り返し実施する必要はありません。
 バッチを手動実行して、「Are you sure you want to continue connecting (yes/no/[fingerprint])?」で「yes」を入力して「Enter」キーを押下します。

【実行手順】
1.「scp_winlin3.bat」をダブルクリックして実行します。
2.「scp_winlin3_bat.log.txt」が作成されるので、実行結果を確認します。
  また、「scp_winlin3_hashfies」フォルダが作成され、「winlin3_sha1_check.txt」にハッシュ値の比較結果が記載されます。

【その他】
・Linuxのディレクトリのハッシュ値取得範囲は「-maxdepth 5」で5階層までに絞っています。
・「SCP_TARGET」で指定するディレクトリやフォルダは、SSH接続時に作成を行わないため、存在するものを指定する必要があります。
・Linuxに対してSSH実行するユーザに、接続先のディレクトリへの書き込み権限がない場合、「Permission denied」となり処理が失敗することがありますのでご注意ください。