自动批处理文件

3

我正在寻找一种方法,在批处理文件执行后将其移动到已知位置。自移动 - 似乎是最合适的名称,但我确信有一个技术术语来描述它。我想要在所有其他代码运行之后移动批处理文件。

move "C:\temp\move_me.bat"  "D:\temp\move_me.bat"  

move_me.bat是放在c:\temp中要移动到d:\temp的批处理文件。

我收到了错误信息:

无法找到批处理文件。

尽管批处理文件已经移动位置。所以我有点困惑。我需要抑制错误,还是最好复制批处理文件并删除源文件?或者有更好的方法吗?

2个回答

4

Windows命令提示符cmd不会将批处理文件缓存在内存中,而是逐行从文件中读取1。因此,一旦move命令完成,就会立即收到错误提示,因为文件再也找不到了。

您可以像这样调用批处理文件来抑制错误:

"C:\temp\move_me.bat" 2> nul

但这会不经意地抑制每个其他错误消息。

无论如何,也许以下方法适合您——这是脚本C:\temp\move_me.bat

if /I "%~dp0"=="D:\temp\" exit /B
rem // (some other code here...)
copy "%~f0" "D:\temp\%~nx0"
"D:\temp\%~nx0" & del "%~f0"

首先,检查当前运行的批处理文件是否等于 D:\temp\,如果相等,则立即终止批处理文件。
最后,将原始批处理文件(通过 %~f02 访问)复制(而不是移动)到新位置 D:\temp(文件名为 %~nx0,保持不变)。
下一行从新位置运行批处理文件,但不使用 call,这需要返回调用批处理脚本,但这不是我们想要的。 & 操作符 允许下一个命令在上一个命令完成后执行。尽管未使用 call,但由于整个行已经被 cmd 读取和解析,因此仍会执行下一个命令。但是,执行控制现在位于批处理文件的新实例,因此错误消息 The batch file cannot be found. 不再出现。
上述的 if 查询立即退出复制的批处理文件的执行,因此其其他代码不会运行两次。
如果您不想跳过复制的批处理文件的执行,请删除 if 命令行并修改 copy 命令行如下:
rem // (some other code here...)
copy "%~f0" "D:\temp\%~nx0" > nul || exit /B
"D:\temp\%~nx0" & del "%~f0"
> nul 部分可以抑制显示消息(包括摘要1 file(s) copied.)。使用|| 操作符仅在复制失败时才执行下一条命令。因此,当执行原始批处理文件时,复制完成如预期。当复制后的批处理文件运行时,copy 尝试将批处理文件复制到自身,导致消息The file cannot be copied onto itself.(由> nul抑制)和错误触发exit /B命令(由于||),以退出批处理文件,因此不会尝试执行最后一行。


您也可以使用move实现相同的功能;相关代码如下:

if /I "%~dp0"=="D:\temp\" exit /B
rem // (some other code here...)
move "%~f0" "D:\temp\%~nx0" & "D:\temp\%~nx0"

或者,如果您希望移动的脚本不跳过其他代码:

rem // (some other code here...)
if /I not "%~dp0"=="D:\temp\" move "%~f0" "D:\temp\%~nx0" & "D:\temp\%~nx0"

if查询是必要的,因为与copy不同,如果源和目标相等,move不会返回错误。


这里提供了一个全面的解决方案,用于批处理文件将自己移动并在移动后将控制权交给已移动的文件。查看所有的说明备注,以找出哪个批处理文件实例运行了哪些代码:

@echo off

rem // Define constants here:
set "_TARGET=D:%~pnx0" & rem /* (this defines the movement destination;
                         rem     in your situation, the original batch file is
                         rem     `C:\temp\move_me.bat`, so the target file is
                         rem     `D:\temp\move_me.bat` (only drive changes)) */

rem // (code that runs for both batch file instances...)
echo Batch file: "%~f0"
echo   [executed by both files before the movement check]

rem // Check whether current batch file is the moved one:
if /I "%~f0"=="%_TARGET%" (

    rem // (code that runs for the moved batch file instance only...)
    echo Batch file: "%~f0"
    echo   [executed only by the moved file]

) else (

    rem // (code than runs for the original batch file instance only...)
    echo Batch file: "%~f0"
    echo   [executed only by the original file]

    rem // Actually move the batch file here, then give control to the moved one:
    > nul move "%~f0" "%_TARGET%"
    "%_TARGET%"

    rem /* (code that runs for the original batch file instance only;
    rem     this is run after the moved batch file has finished;
    rem     you must not use `goto` herein as the target label cannot be found,
    rem     because the original file does no longer exist at this point!) */
    echo Batch file: "%~f0"
    echo   [executed only by the original file, but after the moved one has finished]

)

rem // (code that runs for the moved batch file instance only...)
echo Batch file: "%~f0"
echo   [executed only by the moved file after the movement check]

exit /B

1) 注意,带括号的代码块(/)和继续的行^被视为单个命令行:

(
    echo 整个带括号的块都是
    echo 视为单个命令行。
)
echo 这个继续的行 & ^
echo 也是。

2) 注意,这样的参数引用在命令行或块被读取和解析之后立即解析,因此在实际执行之前就已经解析了。


1

批处理文件是逐行执行的,而不是一次性读入内存。在move命令之后还有其他命令吗?

然而,批处理文件还具有调用另一个批处理文件将覆盖您的批处理文件的功能。

所以,您可能可以这样做:

if "%1" == "continuation" goto :continuation

REM interesting things go here

move "C:\Temp\move_me.bat" "D:\Temp\move_me.bat"
"D:\Temp\move_me.bat" continuation

:continuation
REM more interesting things go here

或者将批处理文件复制到目标位置,然后让续部分删除原始副本。

或者让批处理文件在执行第一步时将自身复制到目标位置(并删除自身)。这可能更容易理解。


move 命令之后的行将不再被识别,我想。也许你可以将 move 改为 copy 并在 :continuation 部分使用 del 命令删除原始批处理文件... - aschipfl
...或者保留 move,但是通过 & 连接下一行,那么它就会被识别... - aschipfl

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