如何安全地让Windows批处理文件更新自身

9
我想运行一个名为upgrade.bat的Windows批处理文件,它将一堆文件从源目录复制到批处理文件所在的目录。问题是,其中一个被复制的文件可能是upgrade.bat的新版本,因此在其仍在运行时批处理文件将覆盖自己。
这似乎会导致批处理文件执行的一些不可预测的行为,所以我希望避免复制仍在运行的批处理文件。理想情况下,我希望现有版本的upgrade.bat在完成之前运行,并在下次运行新版本。是否有任何(简单)方法可以实现这一点?

2
在执行之前重命名文件? - Chad
@Chad - 你的意思是在 upgrade.bat 里面重命名 upgrade.bat 吗?那样做安全吗?结果可预测吗? - Moe Sisko
7个回答

7
为了实现这个目标,必须满足以下要求:
  • 新版本的批处理文件覆盖旧版本的批处理文件必须是批处理文件的最后一个命令,因此在copy命令之后的下一个命令必须是exit /Bexit
  • 在执行前,必须先将之前的命令加载到内存中。这可以通过将它们放在括号中轻松完成。
也就是说:
@echo off
rem Do program business here...
echo Anything

rem Parse/load following commands before execute they:
(
rem Copy many files, probably a newer version of myself
xcopy /Y *.*
rem You may execute other commands here...
echo Files copied!
rem Terminate *this version* of the running Batch file
exit /B
)

7
@ECHO OFF
SETLOCAL
IF /i NOT "%~dp0"=="%temp%\" (
 COPY /y "%~dpnx0" "%temp%\%~nx0" >nul
 "%temp%\%~nx0"

)
ECHO Now we run the rest of the original UPGRADE.BAT

upgrade.bat的开头,这些代码行应该是有效的。

查看我们是否从%temp%中的副本运行。如果不是,则将此文件复制到temp并从那里运行。

因此,批处理实际上是从%temp%运行的,原始版本可能会被覆盖。


@Peter_Wright:这看起来很有趣,但为什么在文件被复制和运行后,“ECHO现在我们运行原始UPGRADE.BAT的其余部分”不会运行呢? - Steve Cohen
@steve 当您运行原始代码时,目录不是临时目录,因此会将其复制到临时目录并执行,从而将执行转移到副本。当副本运行时,%0位于临时目录中,因此它会从副本(即从ECHO开始)运行原始代码。现在 - 如果您的原始代码在%temp%中,则可能会出现错误,因为您将尝试将文件复制到自身。 - Magoo
@Peter_Wright: 一旦我理解了那个奇怪的语法,它就变得非常简单而优雅。我稍微修改了一下,在运行完之后也从temp中将其删除,然后退出。 - Steve Cohen

4

您可以使用start命令将复制作为最后一个操作运行,从另一个终端启动它。请查看此示例,特别是最后几行。

@echo off
set CUR_FILE=batman.bat
set FOUND_EQUAL="FALSE"
set FROM_DIR=c:\temp\galeria\

SETLOCAL DisableDelayedExpansion
FOR /R %FROM_DIR% %%F IN (*) DO (
  SET "p=%%F"
  SETLOCAL EnableDelayedExpansion
  SET ABC=!p:%FROM_DIR%=!

  IF NOT !ABC! == !CUR_FILE! ( 
echo copying %%F
    copy "%%F" . 
   )    
ENDLOCAL  
) 

echo trying to copy file with the same name [last operation] 
start copy "%FROM_DIR%%CUR_FILE%" .

4
@echo off
cmd /c copy "C:\somepath\upgrade.bat" "%0" & %0

这将启动一个新的cmd进程,并使用"C:\somepath\upgrade.bat"替换当前批处理文件,并重新启动批处理文件。


2
if NOT "%1"=="RUN" (
copy "%~0" temp_update.cmd
temp_update.cmd RUN
)

只需将以下代码放置在您的cmd文件顶部,可以使用任何名称作为临时文件。


2
是否可以给每个新版本的批处理文件打上版本后缀,并使用第二个批处理文件作为启动器?例如,假设您的有效负载批处理文件名为"upgrade.bat",随后的版本将命名为"upgrade_001.bat"、"upgrade_002.bat"、"upgrade_003.bat"等,或者是"upgrade_201305122134"(后缀为yyyymmddHHMM),新批处理文件"launcher.bat"将通过查找后缀最高的批处理升级文件并执行它来寻找最新的批处理升级文件。请注意保留HTML标签。

不错的想法,我认为你的启动器批处理文件的想法很好。虽然 upgrade.bat 已经在源代码控制中了,所以不太愿意每次更改时都重命名它。 - Moe Sisko

0

让批处理文件将自己复制到updateNow.bat,然后运行updateNow.bat: 因此,update.bat的内容如下:

if %0 == update (
  copy update.bat updateNow.bat
  updateNow.bat
) else (
  copy newUpdate.bat update.bat
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接