DOSバッチファイルでは、特定のことを達成する方法が多少難読化されています。幸いなことに、バッチスクリプトの素晴らしいリファレンスサイトがあります: Simon SheppardのSS64 。 (同じサイトには、Bashに関する多くの情報もあります。)
1つの問題は、ディレクトリが空かどうかに基づいて実行を分岐することです。明らかなif exist "%dir%\*.*"
は機能しません。ただし、次の条件付き実行トリックを使用して実行できます。
( dir /b /a "%dir%" | findstr . ) > nul && (
echo %dir% non-empty
) || (
echo %dir% empty
)
別の厄介な問題は、ファイルの内容に従って分岐することです。これも次のように実行できます。
( fc /B "%file1%" "%file2%" | find "FC: no differences encountered" ) > nul && (
echo "%file1%" and "%file2%" are the same
) || (
echo "%file1%" and "%file2%" are different
)
だから、私の質問は:
ファイルのタイムスタンプに従って分岐する方法はありますか?
これは私が欲しいものです:
REM *** pseudo-code!
if "%file1%" is_newer_than "%file2%" (
echo "%file1%" is newer
) else if "%file1%" is_older_than "%file2%" (
echo "%file2%" is newer
) else (
echo "%file1%" and "%file2%" are the same age
)
ありがとう。
1行のバッチスクリプトで2つのファイルの新しい方を見つけることができます。ファイルを日付順にリストし、最も古いものを最初にリストします。つまり、リストされている最後のファイルが新しいファイルでなければなりません。そのため、毎回ファイル名を保存すると、変数に入力された姓が最新のファイルになります。
例えば:
SET FILE1=foo.txt
SET FILE2=bar.txt
FOR /F %%i IN ('DIR /B /O:D %FILE1% %FILE2%') DO SET NEWEST=%%i
ECHO %NEWEST% is (probably) newer.
残念ながら、これは同じ日付スタンプには対応していません。したがって、最初にファイルの日付とタイムスタンプが同じかどうかを確認する必要があります。
SET FILE1=foo.txt
SET FILE2=bar.txt
FOR %%i IN (%FILE1%) DO SET DATE1=%%~ti
FOR %%i IN (%FILE2%) DO SET DATE2=%%~ti
IF "%DATE1%"=="%DATE2%" ECHO Files have same age && GOTO END
FOR /F %%i IN ('DIR /B /O:D %FILE1% %FILE2%') DO SET NEWEST=%%i
ECHO Newer file is %NEWEST%
:END
デイブウェッブの主張は素晴らしいものですが、もちろん同じディレクトリ内のファイルに対してのみ機能します。
これは、任意の2つのファイルで機能するソリューションです。
最初にファイル時間を取得します( Windowsコマンドラインでファイルの最終更新日を取得する方法? を参照)。
for %%a in (MyFile1.txt) do set File1Date=%%~ta
for %%a in (MyFile2.txt) do set File2Date=%%~ta
ただし、Cmd.exeはそれらをスティングとして比較するため、日付と時刻を手動でコンポーネントに分割する必要があります。したがって、2> 10および10:00 AM> 2:00 PMです。
最初に年、月、日、AM/PM、時間、分と秒を比較します(実際には時間がかかりますが、分についてはより良いアイデアはありません)。最後の最終コード。
ただし、ファイルが同じ分にあるが、秒ごとに異なる場合、このソリューションは機能しません。
このレベルの精度に達している場合は、「forfiles」コマンドを使用してファイル時間を取得します( https://superuser.com/questions/91287/windows-7-file-properties-date-modified-howを参照してください-do-you-show-seconds )。
for /F "tokens=*" %%a in ('forfiles /m MyFile1.txt /c "cmd /c echo @fdate @ftime"')
do set File1Date=%%a
for /F "tokens=*" %%a in ('forfiles /m MyFile2.txt /c "cmd /c echo @fdate @ftime"')
do set File2Date=%%a
「ForFiles」にはスペースを含むパスを使用できないという制限があるため、スペースを含むパスがある場合は、最初にそのディレクトリに変更する必要があります。 forfiles-フォルダーパス内のスペース =
比較コード
:compareFileTime
set "originalFileTime=%1"
set "secondFileTime=%2"
for /F "tokens=1,2,3 delims= " %%a in (%originalFileTime%) do (
set "originalDatePart=%%a"
set "originalTimePart=%%b"
set "originalAmPmPart=%%c"
)
for /F "tokens=1,2,3 delims= " %%a in (%secondFileTime%) do (
set "secondDatePart=%%a"
set "secondTimePart=%%b"
set "secondAmPmPart=%%c"
)
for /F "tokens=1,2,3 delims=/" %%a in ("%originalDatePart%") do (
set "originalMonthPart=%%a"
set "originalMonthDayPart=%%b"
set "originalYearPart=%%c"
rem We need to ensure that the year is in a 4 digit format and if not we add 2000 to it
rem Cmd considers "50" > "100" but 50 < 100, so don't surround it with qoutes
if %%c LSS 100 set "originalYearPart=20%%c
)
for /F "tokens=1,2,3 delims=/" %%a in ("%secondDatePart%") do (
set "secondMonthPart=%%a"
set "secondMonthDayPart=%%b"
set "secondYearPart=%%c"
rem We need to ensure that the year is in a 4 digit format and if not we add 2000 to it
rem Cmd considers "50" > "100" but 50 < 100, so don't surround it with quotes
if %%c LSS 100 set "secondYearPart=20%%c
)
if %originalYearPart% GTR %secondYearPart% goto newer
if %originalYearPart% LSS %secondYearPart% goto older
rem We reach here only if the year is identical
rem Cmd considers "2" > "10" but 2 < 10, so don't surround it with quotes or you will have to set the width explicitly
if %originalMonthPart% GTR %secondMonthPart% goto newer
if %originalMonthPart% LSS %secondMonthPart% goto older
if %originalMonthDayPart% GTR %secondMonthDayPart% goto newer
if %originalMonthDayPart% LSS %secondMonthDayPart% goto older
rem We reach here only if it is the same date
if %originalAmPmPart% GTR %secondAmPmPart% goto newer
if %originalAmPmPart% LSS %secondAmPmPart% goto older
rem we reach here only if i=t is the same date, and also the same AM/PM
for /F "tokens=1 delims=:" %%a in ("%originalTimePart%") do set "originalHourPart=%%a"
for /F "tokens=1 delims=:" %%a in ("%secondTimePart%") do set "secondHourPart=%%a"
rem Cmd considers "2" > "10" but 2 < 10, so don't surround it with qoutes or you will have to set the width explicitly
if %originalHourPart% GTR %secondHourPart% goto newer
if %originalHourPart% LSS %secondHourPart% goto older
rem The minutes and seconds can be compared directly
if %originalTimePart% GTR %secondTimePart% goto newer
if %originalTimePart% LSS %secondTimePart% goto older
if %originalTimePart% EQU %secondTimePart% goto same
goto older
exit /b
:newer
echo "newer"
exit /b
:older
echo "older"
exit /b
:same
echo "same"
exit /b
真剣に、あなたは何か他のものを学び始めるべきです。それは冗談ではありません。 DOS(cmd.exe)には深刻な日付操作機能が欠けており、さらに多くの欠陥があります。 DOSバッチ、vbscriptに加えて、ネイティブに提供される次の優れた代替手段を次に示します。
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objArgs = WScript.Arguments
strFile1 = objArgs(0)
strFile2 = objArgs(1)
Set objFile1 = objFS.GetFile(strFile1)
Set objFile2 = objFS.GetFile(strFile2)
If objFile1.DateLastModified < objFile2.DateLastModified Then
WScript.Echo "File1: "&strFile1&" is older than "&strFile2
Else
WScript.Echo "File1: "&strFile1&" is newer than "&strFile2
End If
コマンドラインで実行する
C:\test>dir
Volume in drive C has no label.
Volume Serial Number is 08AC-4F03
Directory of C:\test
11/06/2009 07:40 PM <DIR> .
11/06/2009 07:40 PM <DIR> ..
11/06/2009 06:26 PM 135 file
11/02/2009 04:31 PM 4,516 m.txt
C:\test>cscript /nologo test.vbs file m.txt
File1: file is newer than m.txt
もちろん、新しいバージョンのWindowsでは、Powershellを試してみることができます...
これが簡単な解決策です。日付部分を最上位から最下位まで単一の文字列として連結することにより、結果に対して単純な文字列比較を行うことができます。
つまり、YYYYMMDDAMPMHHMM値を比較すると、日付の各セグメントを個別に比較することなく、目的の結果が得られます。この値は、2番目のFORコマンドで抽出された日付文字列のさまざまな部分を連結することによって取得されます。
call :getfiledatestr path\file1.txt file1time
call :getfiledatestr path\file2.txt file2time
if %file1time% equ %file2time% (
echo path\file1.txt is the same age as path\file2.txt to within a minute
) else if %file1time% lss %file2time% (
echo path\file1.txt is older than path\file2.txt
) else (
echo path\file1.txt is newer than path\file2.txt
)
goto :eof
@REM usage:
@REM :getfiledatestr file-path envvar
@REM result returned in %envvar%
:getfiledatestr
for %%f in (%1) do set getfiledatestr=%%~tf
@REM for MM/DD/YYYY HH:MM AMPM use call :appendpadded %2 %%c %%b %%a %%f %%d %%e
@REM for DD/MM/YYYY HH:MM AMPM use call :appendpadded %2 %%c %%b %%a %%f %%d %%e
@REM for YYYY/DD/MM HH:MM AMPM use call :appendpadded %2 %%a %%b %%c %%f %%d %%e
set %2=
for /f "tokens=1,2,3,4,5,6 delims=/: " %%a in ("%getfiledatestr%") do (
call :appendpadded %2 %%c %%b %%a %%f %%d %%e
)
@goto :eof
@REM Takes an env var as the first parameter
@REM and values to be appended as the remaining parameters,
@REM right-padding all values with leading 0's to 4 places
:appendpadded
set temp_value=000%2
call :expand set %1=%%%1%%%%temp_value:~-4%%
shift /2
if "%2" neq "" goto appendpadded
set temp_value=
@goto :eof
@REM forces all variables to expand fully
:expand
%*
@goto :eof
Makefileのスタイルで何かをしたい特定の状況で、ソースファイルが新しい場合にのみソースファイルに基づいて宛先ファイルを上書きする場合、この恐ろしいが簡単な方法を思いつきました。これは、ソースファイルよりも古い宛先ファイルの既存の内容をまったく気にしない場合のオプションにすぎません。
for /f "delims=" %%r in ('xcopy /D /Y /f /e "%inputdir%\%source_filename%" "%outputdir%\%dest_filename%"') do (
IF "%%r" EQU "1 File(s) copied" %build_command% "%inputdir%\%source_filename%" "%outputdir%\%dest_filename%"
)
これが行うことは、xcopy
は、Originファイルが新しい場合にのみ宛先ファイルを上書きします。新しくない場合、%% rは「0個のファイルがコピーされた」ため、条件付きコマンドは実行されず、宛先ファイルは上書きされません。新しい場合、%% rは「1ファイルのコピー」であるため、宛先ファイルはソースファイルのコピーであるため、ビルドコマンドが実行され、宛先ファイルの新しいバージョンに置き換えられます。実際にあるはずです。
たぶん、Perlスクリプトを書いたはずです。
(注:宛先ファイル名の最後にアスタリスクを付けることにより、xcopy
が宛先ファイルが最初に存在しない状況を処理することもできます。そうしない場合は、xcopy
は、宛先がファイル名であるかフォルダー名であるかがわからず、ファイル名への回答をデフォルトにするフラグがありません。)
Wes answer に基づいて、更新されたスクリプトを作成しました。更新が多すぎてコメントに入れることができませんでした。
私のバージョンは以下です。
@REM usage:
@REM :getfiledatestr file-path envvar
@REM result returned in %envvar%
:getfiledatestr
for %%A in (%1) do (
set getfilefolderstr=%%~dpA
set getfilenamestr=%%~nxA
)
@REM Removing trailing backslash
if %getfilefolderstr:~-1%==\ set getfilefolderstr=%getfilefolderstr:~0,-1%
@REM clear it for case that the forfiles command fails
set getfiledatestr=
for /f "delims=" %%i in ('"forfiles /p ""%getfilefolderstr%"" /m ""%getfilenamestr%"" /c "cmd /c echo @fdate @ftime" "') do set getfiledatestr=%%i
@REM old code: for %%f in (%1) do set getfiledatestr=%%~tf
set %2=
@REM consider missing files as oldest files
if "%getfiledatestr%" equ "" set %2=""
@REM Currently supported date part delimiters are /:. and space. You may need to add more delimiters to the following line if your date format contains alternative delimiters too. If you do not do that then the parsed dates will be entirely arbitrarily structured and comparisons misbehave.
for /f "tokens=1,2,3,4,5,6 delims=/:. " %%a in ("%getfiledatestr%") do (
@REM for MM/DD/YYYY HH:MM:SS AMPM use call :appendpadded %2 %%c %%b %%a %%g %%d %%e %%f
@REM for DD/MM/YYYY HH:MM:SS AMPM use call :appendpadded %2 %%c %%b %%a %%g %%d %%e %%f
@REM for YYYY/DD/MM HH:MM:SS AMPM use call :appendpadded %2 %%a %%b %%c %%g %%d %%e %%f
call :appendpadded %2 %%c %%b %%a %%g %%d %%e %%f
)
@goto :eof
@REM Takes an env var as the first parameter
@REM and values to be appended as the remaining parameters,
@REM right-padding all values with leading 0's to 4 places
:appendpadded
set temp_value=000%2
call :expand set %1=%%%1%%%%temp_value:~-4%%
shift /2
if "%2" neq "" goto appendpadded
set temp_value=
@REM cmd is not able to compare integers larger than 1+31 bits, so lets convert them to quoted numeric strings instead. The current implementation generates numeric strings much longer than 31 bit integers worth.
call :expand set %1="%%%1%%%"
@goto :eof
@REM forces all variables to expand fully
:expand
%*
@goto :eof