Je voudrais comparer deux dossiers, strictement pour les noms de fichiers. (non le contenu du fichier). Je suis sur Windows 10 et souhaitez utiliser des outils de ligne de commande.
Voici un scénario qui peut expliquer le problème.
Folders
-------
source source2
- file_1 - file_1
- file_2 - file_2
- file_3 - file_4
- file_5 - file_5
- file_6 - file_7
Output should be:
-----------------
Source -> Missing files file_4
Source2 -> Missing files file_3
J'ai fait des recherches et il y a beaucoup d'outils diff
, mais je ne suis pas intéressé à comparer le contenu du fichier. Tout ce que je veux, c'est comparer deux dossiers et sortie quels fichiers sont manquants dans les deux dossiers (par rapport à l'autre).
Je préfère ne pas utiliser PowerShell.
Merci.
Vous pouvez enregistrer le code suivant comme ccomp.cmd
, puis appelez-le avec le /?
Drapeau pour savoir comment l'utiliser (testé sur Windows 7, Windows 10):
Syntaxe:
ccomp <dir_tree1> <dir_tree2>
Code:
@echo off
if "!ERRORLEVEL!" == "%ERRORLEVEL%" (
REM if delayed expansion is enabled:
(
echo.
echo ERROR: This script must be called in a Disabled Delayed Expansion block ^(default^)^^^!
echo.
call :PressAKey "Press a key to exit..."
echo.
)>con
exit /b 1
)
setlocal disabledelayedexpansion
if defined _first_time goto :label1MAIN
REM Test CHCP:
chcp /?>nul 2>nul||(
(
echo. & echo ERROR: Could not start chcp ^(necessary^)!
)>con
exit /b 1
)
REM Get the initial code page:
call :GetCurrentCodePage _initial_CP
REM Change the code page (character encoding) for "CON" (console) to 65001 (UTF-8):
set _con_error=false
mode con cp select=65001>nul 2>nul||(
(
echo. & echo WARNING: Could not change the code page for CON ^(Console^)!
)>con
set _con_error=true
)
set _help_flag=0
set /a _count=1
set "_script_path=%~Dpnx0"
:repeat1MAIN
if _%1_ == _""_ (
REM "%1" is """"
set _param%_count%=""
shift
set /a _count+=1
goto :repeat1MAIN
) else (
if /i "%~1" == "/?" (
set _help_flag=1
shift
goto :repeat1MAIN
) else (
if /i "%~1" == "/help" (
set _help_flag=1
shift
goto :repeat1MAIN
) else (
if /i "%~1" == "/h" (
set _help_flag=1
shift
goto :repeat1MAIN
) else (
if not "%~1" == "" (
REM if "%1" is null, it means that no more parameters are provided
set "_param%_count%=%~1"
shift
set /a _count+=1
goto :repeat1MAIN
)
)
)
)
)
set /a _count-=1
set /a _param_count=_count
REM start Checking parameters \/
if "%_help_flag%" == "1" (
call :DisplayHelp
exit /b 0
) else (
if %_param_count% gtr 2 (
(
echo. & echo ERROR: Too many parameters!
)>con
exit /b 1
) else (
if %_param_count% lss 2 (
(
echo. & echo ERROR: Too few parameters!
)>con
exit /b 1
)
)
)
set "_error=false"
if "%_param1%" == """" (
(
echo. & echo ERROR: First provided directory parameter must not be empty!
)>con
set "_error=true"
) else (
call :TestIfDirAcccessible _param1 _is_param1_dir
setlocal enabledelayedexpansion
if not "!_is_param1_dir!" == "0" (
endlocal
(
echo. & echo ERROR: First provided directory parameter: "%_param1%" is not a directory or is not accessible!
)>con
set "_error=true"
) else (
endlocal
)
)
if "%_param2%" == """" (
(
echo. & echo ERROR: Second provided directory parameter must not be empty!
)>con
set "_error=true"
) else (
call :TestIfDirAcccessible _param2 _is_param2_dir
setlocal enabledelayedexpansion
if not "!_is_param2_dir!" == "0" (
endlocal
(
echo. & echo ERROR: Second provided directory parameter "%_param2%" is not a directory or is not accessible!
)>con
set "_error=true"
) else (
endlocal
)
)
call :TestIfPathIsUNC _param1 _result1
call :TestIfPathIsUNC _param2 _result2
if "%_result1%" == "true" (
set "_error=true"
(
echo. & echo ERROR: Path1: "%_param1%" seems to be a UNC path ^(contains \\^), and UNC paths are not supported by this program ^(but a UNC path can be mounted ^(for example by using pushd^), in order to make it accessible^)!
)>con
)
if "%_result2%" == "true" (
set "_error=true"
(
echo. & echo ERROR: Path2: "%_param2%" seems to be a UNC path ^(contains \\^), and UNC paths are not supported by this program ^(but a UNC path can be mounted ^(for example by using pushd^), in order to make it accessible^)!
)>con
)
if "%_error%" == "true" (
(
echo. & call :PressAKey "Press a key to exit!"
echo.
)>con
exit /b 1
)
cmd /u /c ^(echo.^&echo Path1: "%_param1%"^)
cmd /u /c ^(echo.^&echo Path2: "%_param2%"^)
REM end Checking parameters /\
REM If everything seems ok, proceed to PROCESSING:
pushd "%_param1%\">nul
set "_param1=%CD%"
if "%_param1:~-1%" == "\" set "_param1=%_param1:~0,-1%"
popd>nul
pushd "%_param2%\">nul
set "_param2=%CD%"
if "%_param2:~-1%" == "\" set "_param2=%_param2:~0,-1%"
popd>nul
call :ConvertDriveLetterToUpperCase _param1 _param1
call :ConvertDriveLetterToUpperCase _param2 _param2
call :GetStrLen _param1 _param1_len
call :GetStrLen _param2 _param2_len
pushd "%_script_path%\..">nul
if not defined _first_time (
call :EscapePathString _script_path _script_path_escaped
call :EscapePathString _param1 _param1_escaped
call :EscapePathString _param2 _param2_escaped
)
popd>nul
REM Change the code page (character encoding) for "CON" (console) to 437 (ANSI):
if "%_con_error%" == "false" (
mode con cp select=437>nul 2>nul
)
:label1MAIN
REM CHCP 437 = ANSI CODE PAGE
REM CHCP 65001 = UTF-8 CODE PAGE
if not defined _first_time (
set _first_time=defined
chcp 65001>nul
cmd /u /c ^(echo.^&echo Start time: %date% %time%^&echo.^)
cmd /u /c ^(for /f ^"tokens=^*^" %%l in ^(^'^^^( setlocal^^^&^^^"%%_script_path_escaped%%^^^" ^^^"%%_param1_escaped%%^^^" ^^^"%%_param2_escaped%%^^^"^^^&title Sorting Results ^^^^^^^^^^^^^^^(finally^^^^^^^^^^^^^^^)^^^&endlocal ^^^)^^^|sort^'^) do @^(if not defined _once ^( set "_once=defined"^&echo %%l^) else echo %%l ^)^)^&if defined _once echo.
set "_first_time="
set "_second_time="
set "_third_time="
cmd /u /c ^(call echo End time: %%date%% %%time%%^)
chcp 437>nul
REM Restore the initial code page:
chcp %_initial_CP%>nul 2>nul
) else (
if not defined _second_time (
set _second_time=defined
(
call :ProcedureAnalyzeFiles
)
) else (
if not defined _third_time (
set "_third_time=defined"
(
call :ProcedureProcessFilenamesLengthAndSize1
chcp 65001>nul
call :ProcedureProcessFilenamesLengthAndSize2
chcp 437>nul
@REM After sorting: "-" is displayed before all other characters:
@call echo ----------------------------------------""%%_count1%%""
@title Sorting results. Please wait...
)
)
)
)
endlocal & (
if defined _first_time set "_first_time=%_first_time%"
if defined _second_time set "_second_time=%_second_time%"
if defined _third_time set "_third_time=%_third_time%"
)
goto :eof
REM \\\/// Next subroutines use jeb's syntax for working with delayed expansion: \\\///
:GetStrLen
REM - by jeb - adaptation
(
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~2=%len%"
exit /b
)
:EscapePathString
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
call :GetStrLen string string_len
set /a string_len-=1
for /l %%i in (0,1,!string_len!) do (
rem escape "^", "(", ")", "!", "&"
if "!string:~%%i,1!" == "^" (
set "result=!result!^^^^"
) else (
if "!string:~%%i,1!" == "(" (
set "result=!result!^^^("
) else (
if "!string:~%%i,1!" == ")" (
set "result=!result!^^^)"
) else (
if "!string:~%%i,1!" == "^!" (
set "result=!result!^^^!"
) else (
if "!string:~%%i,1!" == "&" (
set "result=!result!^^^&"
) else (
if "!string:~%%i,1!" == "%%" (
set "result=!result!%%"
) else (
set "result=!result!!string:~%%i,1!"
)
)
)
)
)
)
)
)
(
endlocal
set "%~2=%result%"
exit /b
)
:TestIfDirAcccessible
(
setlocal EnableDelayedExpansion
set _returned_code=0
(pushd "!%~1!">nul 2>nul||set "_returned_code=1")&&popd>nul
)
(
endlocal
set "%~2=%_returned_code%"
exit /b
)
:TestIfPathIsUNC
(
setlocal EnableDelayedExpansion
set "_current_path=!%~1!"
set "_is_unc_path=true"
if "!_current_path:\\=!" == "!_current_path!" (
set "_is_unc_path=false"
)
)
(
endlocal
set "%~2=%_is_unc_path%"
exit /b
)
:GetCurrentCodePage
(
setlocal EnableDelayedExpansion
set "_current_cp=-1"
for /f "tokens=1,2* delims=:" %%a in ('chcp 2^>nul') do (
set "_current_cp=%%~b"
)
)
(
endlocal
set "%~1=%_current_cp%"
exit /b
)
:ConvertDriveLetterToUpperCase
(
setlocal EnableDelayedExpansion
set "_path=!%~1!"
for /f "tokens=1 delims=\" %%f in ('echo "!_path!"') do set "_path_drive=%%~df"
set "_upper_drive=!_path_drive!"
for %%D in (A: B: C: D: E: F: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: W: X: Y: Z:) do (
if /i "!_path_drive!" == "%%D" (
set "_upper_drive=%%D"
goto :endConvertDriveLetterToUpperCase
)
)
)
:endConvertDriveLetterToUpperCase
(
endlocal
set "%~1=%_upper_drive%%_path:~2%"
exit /b
)
REM ///\\\ The subroutines above use jeb's syntax for working with delayed expansion: ///\\\
:GenerateShortName
@set "%~1=%~s2"
@goto :eof
:GenerateShortNameSize
@set "%~1=%~z2"
@goto :eof
:ProcedureAnalyzeFiles
setlocal
for /f "tokens=1,2,3,4,5* delims=/" %%p in ('chcp 65001^>nul^&^(setlocal^&^"%_script_path_escaped%^" ^"%_param1_escaped%^" ^"%_param2_escaped%^"^|sort^&endlocal^)^&chcp 437^>nul') do (
if not defined _second_time_for1 (
set /a _total_count=%%~p>nul 2>nul
set /a _currrent_count=1
set _second_time_for1=defined
) else (
set /a _current_count+=1
REM START PROCESSING:
setlocal enabledelayedexpansion
title Analyzing file !_current_count! of !_total_count!...
set "_previous_file=!_current_file!"
set "_previous_file_type=!_current_file_type!"
set "_previous_file_visibility=!_current_file_visibility!"
set "_previous_file_dir=!_current_file_dir!"
REM Due to a bug in sort, an extra " is added at the end of the line, so we need to remove it:
set "_previous_file_size=!_current_file_size:"=!"
set "_previous_file_base_dir=!_current_file_base_dir!"
setlocal disabledelayedexpansion
set "_current_file=%%~p"
set "_current_file_type=%%~q"
set "_current_file_visibility=%%~r"
set "_current_file_dir=%%~s"
set "_current_file_size=%%~t"
setlocal enabledelayedexpansion
if "!_current_file_dir!" == "1" (
endlocal
set "_current_file_base_dir=%_param1%"
) else (
if "!_current_file_dir!" == "2" (
endlocal
set "_current_file_base_dir=%_param2%"
) else (
endlocal
)
)
setlocal enabledelayedexpansion
REM Due to a bug in sort, an extra " is added at the end of the line, so we need to remove it:
set "_current_file_size=!_current_file_size:"=!"
REM if not first time:
if NOT "!_previous_file_type!" == "" (
if "!_next!" == "1" (
if "!_current_file_type!" == "!_previous_file_type!" (
if "!_current_file_type!" == "file" (
if NOT "!_current_file!" == "!_previous_file!" (
echo Only in "!_previous_file_dir!" - !_previous_file_type!: "!_previous_file_base_dir!!_previous_file!"
) else (
set "_for_one_cannot_get_size=0"
if "!_previous_file_size!" == "-1" set "_for_one_cannot_get_size=1"
if "!_current_file_size!" == "-1" set "_for_one_cannot_get_size=1"
if "!_for_one_cannot_get_size!" == "0" (
if "!_previous_file_size!" GTR "!_current_file_size!" (
echo "!_previous_file_base_dir!!_previous_file!" ^(!_previous_file_dir!^) size ^(!_previous_file_size!B^) is bigger than "!_current_file_base_dir!!_current_file!" ^(!_current_file_dir!^) size ^(!_current_file_size!B^)
) else (
if "!_previous_file_size!" LSS "!_current_file_size!" (
echo "!_previous_file_base_dir!!_previous_file!" ^(!_previous_file_dir!^) size ^(!_previous_file_size!B^) is smaller than "!_current_file_base_dir!!_current_file!" ^(!_current_file_dir!^) size ^(!_current_file_size!B^)
)
)
)
)
) else (
if NOT "!_current_file!" == "!_previous_file!" (
echo Only in "!_previous_file_dir!" - !_previous_file_type!: "!_previous_file_base_dir!!_previous_file!"
)
)
) else (
echo Only in "!_previous_file_dir!" - !_previous_file_type!: "!_previous_file_base_dir!!_previous_file!"
)
)
)
endlocal
endlocal
endlocal
setlocal enabledelayedexpansion
if "%%~s" == "1" (
endlocal
set "_current_file_base_dir=%_param1%"
) else (
if "%%~s" == "2" (
endlocal
set "_current_file_base_dir=%_param2%"
) else (
endlocal
)
)
set "_temp=%%~p"
setlocal enabledelayedexpansion
REM if not first time:
if NOT "!_current_file_type!" == "" (
if "!_next!" == "1" (
if "%%~q" == "!_current_file_type!" (
if "!_temp!" == "!_current_file!" (
endlocal
set _next=2
) else (
endlocal
set _next=1
)
) else (
endlocal
set _next=1
)
) else (
endlocal
set _next=1
)
) else (
endlocal
set _next=1
)
set "_current_file=%%~p"
set "_current_file_type=%%~q"
set "_current_file_visibility=%%~r"
set "_current_file_dir=%%~s"
set "_current_file_size=%%~t"
)
)
REM Treat the last file separately:
chcp 65001>nul
setlocal enabledelayedexpansion
if "!_next!" == "1" (
echo Only in "!_current_file_dir!" - !_current_file_type!: "!_current_file_base_dir!!_current_file!"
)
endlocal
chcp 437>nul
endlocal
goto :eof
:ProcedureProcessFilenamesLengthAndSize1
@setlocal
@set /a _count1=0
@REM Process directories:
@chcp 65001>nul
@title Loading directory paths for directory 1. Please wait...
@pushd "%_param1%">nul
@for /r /d %%f in (*) do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param1_len%!///dir///1///1///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param1_len%!///dir///1///1///!_size!"
) else (
@echo "!_current_path:~%_param1_len%!///dir///1///1///-1"
)
)
@endlocal
)
@popd>nul
@title Please wait...
@REM Process directories:
@title Loading directory paths for directory 2. Please wait...
@pushd "%_param2%">nul
@for /r /d %%f in (*) do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param2_len%!///dir///1///2///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param2_len%!///dir///1///2///!_size!"
) else (
@echo "!_current_path:~%_param2_len%!///dir///1///2///-1"
)
)
@endlocal
)
@popd>nul
@title Please wait...
@REM Process files:
@title Loading file paths for directory 1. Please wait...
@pushd "%_param1%">nul
@for /r %%f in (*) do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param1_len%!///file///1///1///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param1_len%!///file///1///1///!_size!"
) else (
@echo "!_current_path:~%_param1_len%!///file///1///1///-1"
)
)
@endlocal
)
@popd>nul
@title Please wait...
@REM Process files:
@title Loading file paths for directory 2. Please wait...
@pushd "%_param2%">nul
@for /r %%f in (*) do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param2_len%!///file///1///2///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param2_len%!///file///1///2///!_size!"
) else (
@echo "!_current_path:~%_param2_len%!///file///1///2///-1"
)
)
@endlocal
)
@popd>nul
@title Please wait...
@chcp 437>nul
@endlocal & (
@set _count1=%_count1%
)
@goto :eof
:ProcedureProcessFilenamesLengthAndSize2
@setlocal
@REM Process hidden directories:
@for /f "tokens=*" %%f in ('title Loading hidden directory paths for directory 1. Please wait...^&@pushd "%_param1%"^>nul^&^&dir /a:dh /s /b 2^>nul^&^&popd^>nul^&title Please wait...') do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param1_len%!///dir///0///1///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param1_len%!///dir///0///1///!_size!"
) else (
@REM This file is hidden and probably has a Unicode path:
@echo "!_current_path:~%_param1_len%!///dir///0///1///-1"
)
)
@endlocal
)
@title Please wait...
@REM Process hidden directories:
@for /f "tokens=*" %%f in ('title Loading hidden directory paths for directory 2. Please wait...^&@pushd "%_param2%"^>nul^&^&dir /a:dh /s /b 2^>nul^&^&popd^>nul^&title Please wait...') do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@set /a _count1+=1 >nul
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param2_len%!///dir///0///2///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param2_len%!///dir///0///2///!_size!"
) else (
@REM This file is hidden and probably has a Unicode path:
@echo "!_current_path:~%_param2_len%!///dir///0///2///-1"
)
)
@endlocal
)
@title Please wait...
@REM Process hidden files:
@for /f "tokens=*" %%f in ('title Loading hidden file paths for directory 1. Please wait...^&@pushd "%_param1%"^>nul^&^&dir /a:-dh /s /b 2^>nul^&^&popd^>nul^&title Please wait...') do @(
@set "_current_path=%%~f"
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@set /a _count1+=1 >nul
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param1_len%!///file///0///1///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param1_len%!///file///0///1///!_size!"
) else (
@REM This file is hidden and probably has a Unicode path:
@echo "!_current_path:~%_param1_len%!///file///0///1///-1"
)
)
@endlocal
)
@title Please wait...
@REM Process hidden files:
@for /f "tokens=*" %%f in ('title Loading hidden file paths for directory 2. Please wait...^&@pushd "%_param2%"^>nul^&^&dir /a:-dh /s /b 2^>nul^&^&popd^>nul^&title Please wait...') do @(
@set "_current_path=%%~f"
@set /a _count1+=1 >nul
@if "%%~zf" == "" (
@call :GenerateShortName _short_name "%%~f"
@call :GenerateShortNameSize _size "%%_short_name%%"
)
@setlocal enabledelayedexpansion
@title Processing file !_count1!...
@if not "%%~zf" == "" (
@echo "!_current_path:~%_param2_len%!///file///0///2///%%~zf"
) else (
@if not "!_size!" == "" (
@echo "!_current_path:~%_param2_len%!///file///0///2///!_size!"
) else (
@REM This file is hidden and probably has a Unicode path:
@echo "!_current_path:~%_param2_len%!///file///0///2///-1"
)
)
@endlocal
)
@title Please wait...
@endlocal & (
@set _count1=%_count1%
)
@goto :eof
:DisplayHelp
echo.
echo %~n0 ^(Cmd COMPare^) - Compare two directory trees by file and folder paths and by size
echo.
echo Syntax: %~n0 ^<dir_tree1^> ^<dir_tree2^>
echo.
echo - where ^<dir_tree1^> and ^<dir_tree2^> are two directory trees to be compared, provided by the user
echo.
echo * Note 1:
echo - Files that are hidden and also have a Unicode path - are compared only by path, not also by size
echo - In some rare cases ^(for some Unicode file paths^) some files may be misreported
echo.
echo * Note 2:
echo - This program uses the ^"sort^" utility for sorting results, so the waiting time for the comparison of the two directories depends on (is a multiple of) n * log n ^(the time complexity for the quick sort algorithm ^(used in ^"sort^"^)^) - where - n is the total number of files in the two directories that are to be compared.
echo - The output of the program is Unicode
goto :eof
:PressAKey
set /p=%~1<nul
pause>nul
goto :eof
Ce script PowerShell fait ce que vous voulez.
$fso = Get-ChildItem -Recurse -path C:\Temp\Source
$fsoBU = Get-ChildItem -Recurse -path C:\Temp\Source2
Compare-Object -ReferenceObject $fso -DifferenceObject $fsoBU
Cela, et plusieurs autres options, sont discutées ici: https://stackoverflow.com/questions/3804109/what-is-the-best-way-a-compare-0-nfolder-Rees-on-windows