Windows批处理文件的隐藏功能

232

Windows批处理文件中一些不太为人所知,但重要且有用的特性是什么?

指南:

  • 每个答案只涉及一个特性
  • 提供特性的简短描述示例,而不是只提供文档链接
  • 答案限于本地功能,即不需要其他软件,如Windows资源工具包

澄清:我们这里指的是由cmd.exe处理的脚本,这是WinNT变体的默认值。

(另请参见:Windows批处理文件:.bat与.cmd有何区别?

91个回答

8
你可以使用call来评估以后的名称,这将导致一些有用的属性。
call set SomeEnvVariable_%extension%=%%%somevalue%%%

使用call设置变量,其名称取决于其他变量。如果与某些变量命名规则一起使用,您可以通过使用谨慎的命名规则模拟数据集合,如数组或字典。三个%符号围绕somevalue是为了在调用之后并在set被调用之前将其评估为一个由单个%符号包围的变量名称。这意味着两个连续的%符号会转义为一个单独的%字符,然后再次扩展它,因此somevalue实际上是一个名称指针。

call set TempVar=%%SomeEnvVariable_%extension%%%

使用临时变量来检索值,然后在逻辑中使用该值。结合延迟变量扩展使用时最有用。
要正确使用此方法,需要启用延迟变量扩展。由于默认情况下它是关闭的,最好在脚本中放置以下指令来启用它:
setlocal EnableDelayedExpansion

你还可以使用延迟扩展符号!,而非%(通过适当混合)来实现延迟扩展。在处理数据数组时,我经常会使用“set foo=!array[%i%]!”这样的语句。%i% 会立即被扩展,而位于!之间的内容将在稍后扩展,使用%i% 当时的值。 - Joey
大多数PC默认情况下没有启用延迟扩展。如果需要全局变量,则通常不能依赖延迟扩展来完成任务。此外,在需要多层延迟扩展时,使用此技巧会更加方便。 - Coding With Style
没错,这确实需要延迟扩展,但你可以在脚本中启用它。我现在正在更新我的答案以包含这一点。 - Lara Dougan
据我所知,您可以使用CMD /V:ON或SETLOCAL ENABLEDELAYEDEXPANSION来在脚本中激活它。这两者都不能让您将其用于全局变量(CMD子实例化了一个命令会话,而SETLOCAL显然让您的变量成为局部变量)。如果您想要保留环境变量,那么这就变得不愉快了。 - Coding With Style

7

在路径上搜索可执行文件(或其他路径样式字符串,如果需要):

c:\> for %i in (cmd.exe) do @echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe

c:\> for %i in (python.exe) do @echo. %~$PATH:i
C:\Python25\python.exe

c:\>

1
“where”程序也可以做到这一点。我不确定在Vista之前是否存在这个功能。 - Brian

7
关于使用::代替REM进行注释:要小心!::是CALL标签的特殊情况,像注释一样起作用。当它在括号内使用时,例如在FOR或IF循环中,函数会过早退出。非常令人沮丧的调试!请参阅http://www.ss64.com/nt/rem.html获取完整说明。 (作为新答案添加,而不是作为上面首次提到此事的评论,因为我还不配评论:0)

6

现在许多人使用GOTO :EOF来终止他们的批处理文件,但你也可以使用EXIT /B来实现这个目的。

使用EXIT /B的优点是,您可以在EXIT /B后添加一个错误级别,它将以该错误级别退出。


6

本地变量仍然会被解析到使用ENDLOCAL的行。这允许使用一些技巧,例如:

ENDLOCAL & SET MYGLOBAL=%SOMELOCAL% & SET MYOTHERGLOBAL=%SOMEOTHERLOCAL%

这是一种将结果传递给调用上下文的有用方法。具体而言,%SOMELOCAL%在ENDLOCAL完成后立即超出范围,但这时%SOMELOCAL%已经被扩展,因此MYGLOBAL使用局部变量在调用上下文中赋值。
出于同样的原因,如果您决定执行以下操作:
ENDLOCAL & SET MYLOCAL=%MYLOCAL%

你会发现,你新的MYLOCAL变量实际上是一个普通的环境变量,而不是你本来想要的本地化变量。


5

5

子程序(输出42):

    @echo off
    call :answer 42
    goto :eof
:do_something
    echo %1
    goto :eof

以及返回值的子程序(输出0、1、2等):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    call :seq_init seq1
:loop1
    if not %seq1%== 10 (
        call :seq_next seq1
        echo !seq1!
        goto :loop1
    )
    endlocal
    goto :eof

:seq_init
    set /a "%1 = -1"
    goto :eof
:seq_next
    set /a "seq_next_tmp1 = %1"
    set /a "%1 = %seq_next_tmp1% + 1"
    set seq_next_tmp1=
    goto :eof

5

我最喜欢在cmd.exe中使用快速编辑模式。虽然这有点离题,但在与命令行交互时,它可以拯救你的生命。不,我没有夸张——你只会在死之前看到caret-capitol-v一定次数;你看得越多,就会死得越快。

  1. 打开regedit(注意,这不是我的错,可能会导致蓝屏等问题)
  2. 转到HKCU/Console
  3. 将QuickEdit设置为1

(您也可以从UI设置此选项,这可能是更好的方法。请参阅评论中的说明。还有一个很好的一行脚本来完成此操作。)

现在,要复制,只需左键单击并拖动以选择,然后右键单击即可复制。要粘贴,只需右键单击。

再也不用^V^V^V^V^V^V^V^V^V^V^V^V^V^V了!

糟糕,我想我刚刚杀了某个人。对不起!


5
您可以在不直接编辑注册表的情况下进行设置。点击窗口左上角的命令提示符图标,并选择“属性”。在选项卡中勾选“快速编辑模式”即可。 - aphoria
1
是的,属性设置是更人性化的方法。@Will,建议你编辑一下,把这个作为主要建议...但是对于编写脚本等情况,regedit方法仍然很有用。 - Chris Noe
使用“alt+空格 e p”将剪贴板粘贴到光标位置 从资源管理器拖动文件夹到命令行窗口会将目录名称放置在光标位置 - mr calendar
你可以使用脚本 reg add HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f 来实现。 - Shea
我不喜欢快速编辑,因为它会干扰我的鼠标行为。我只想能够按下Ctrl+V进行粘贴!我感觉到一个AHK脚本在我内心涌动... ;) - demoncodemonkey

4

即使批处理文件的输出已经通过 > con 语法重定向到文件中,仍然可以将输出重定向到控制台。

示例: foo.cmd:

echo a
echo b > con

调用:

foo.cmd > output.txt

这将导致"a"被发送到output.txt,而"b"则会显示在控制台上。

4
SHIFT

这是一种通过命令行迭代处理可变数量参数的方法。在其最简单的用法中,它将 %2 转换为 %1,%3 转换为 %2,以此类推。(你也可以传入一个参数给SHIFT来跳过多个参数)。这使得命令是“破坏性的”(即 %1 消失了),但它允许你避免硬编码一个支持的最大参数数量。
以下是一个简短的示例,逐个处理命令行参数:
:ParseArgs

if "%1"=="" (
    goto :DoneParsingArgs
)

rem ... do something with %1 ...

shift

goto :ParseArgs


:DoneParsingArgs

rem ...

遗憾的是,SHIFT 不会影响 %* 的值,它只会修改各个参数的值:%1、%2 等。 - Chris Noe
没错。在某些情况下仍然可能有用,但我同意它是不一致的... - reuben

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