Windows批处理文件的隐藏功能

232

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

指南:

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

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

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

91个回答

185

行继续:

call C:\WINDOWS\system32\ntbackup.exe ^
    backup ^
    /V:yes ^
    /R:no ^
    /RS:no ^
    /HC:off ^
    /M normal ^
    /L:s ^
    @daily.bks ^
    /F daily.bkf

21
^ 实际上是一种引号字符。使用它,您可以引用 < 和 >,以便它们不会重定向输出。在行尾处使用 ^ 还允许进行行连续。 - Cheeso
你能解释一下这个小脚本吗? - guerda
2
@furtelwart:这与您将所有内容写入单个行相同:call C:\WINDOWS\system32\ntbackup.exe backup /V:yes /R:no /RS:no /HC:off /M normal /L:s @daily.bks /F daily.bkf。要了解该行的所有参数,只需运行C:\WINDOWS\system32\ntbackup.exe /?即可。 - Kurt Pfeifle
这个主题的深入讨论,请参见长命令分割成多行 - jeb

150
PUSHD path

将您带到指定的目录 path

POPD

返回你之前“push”命令所在的目录。


5
这也可以作为一个完整的栈来使用,所以你可以将许多目录推入栈中,然后不断弹出以返回之前的位置。 - Kibbee
4
打开“cmd.exe”,然后输入“help”,接着输入“help pushd”或“pushd /?”。 - paxdiablo
86
如果你使用pushd切换到一个UNC路径,它会自动为你映射一个驱动器,而popd会取消这个映射。 - Ferruccio
cd命令不同的是,您也可以使用pushd在不同驱动器之间切换目录。 (如果您在C:\ Windows中,并且想要进入D:\ Games,则pushd D:\ Games将带您到那里,而无需键入D:cd D:\ Games。) - idbrii
@CharlesB:这是多余的。看看Ferruccio的评论,解释了那个功能。它有77个赞和明亮的橙色。此外,我认为人们应该知道他指出了这一点。当我发布这篇文章时,我并不知道这一点。 - raven
显示剩余2条评论

109

不确定在批处理文件中这会有多大用处,但在命令提示符中使用这个命令非常方便:

不确定在批处理文件中这会有多大用处,但在命令提示符中使用这个命令非常方便:

C:\some_directory> start .

这将在“some_directory”文件夹中打开Windows资源管理器。

我发现这是一个非常省时的方法。


5
"explorer"和"start"做的事情是一样的。 C:\some_directory>explorer . - Ray
1
如果你使用的是Mac电脑,那么open .也可以实现同样的功能。 - Grant Paul
1
start 做的不仅仅是打开当前文件夹。你可以传递任何文件给它,它会使用配置的查看器打开它。如果给它一个 URL,你的默认浏览器就会打开它,等等... - idbrii
1
"explorer /select,"file"" 将为文件夹打开一个窗口并突出显示该文件(如果文件夹窗口尚未打开) - bart
1
@HalilÖzgür- 你在 explorer 命令后面加了句号(“.”)吗?它需要这个来打开当前目录。C:\some_directory>explorer . - Ray
显示剩余9条评论

87

我一直觉得阅读每行都用关键词标记的注释很困难:

REM blah blah blah

更易读:

:: blah blah blah

8
我听说双冒号(::)比REM命令更高效,因为REM命令会尝试在其后面的内容上执行环境变量扩展,而双冒号则不会。 - Scott Langham
47
实际上,:: 只是一个有趣名称的标签;因此,如果你在括号内使用它(在块内),:: 将不起作用,因为标签也不允许在那里。当然,REM 在那里可以使用。 - mihi
请注意,rem是一个已记录的关键字,而::只是一种实现细节。虽然::停止工作的可能性很小,但通常建议不要依赖未记录的行为。 - Joey
你甚至可以使用 goto: 跳转到标签。使用 :-)goto -) 同样也有效。 - Sven Marnach

79

变量子字符串:

> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456

1
@furtelwart听起来好像可以成为批处理的座右铭。 - rzrgenesys187
这对于格式化日期非常有用(但我发现的问题是Vista/7和XP以不同的方式输出日期)。 - Daniel
请注意,这不能与FOR循环的本地变量(for%a in ...)组合使用,因为它们不需要像环境变量一样需要关闭百分号;您必须首先将它们分配给环境变量(使用延迟扩展!),然后提取子字符串。 - RolKau
如果你想要处理日期(例如备份文件名),这是一个不可避免的丑陋问题。在目睹了Windows命令行的普遍丑陋之后,我明白为什么Windows大多数时候都是点和点击的 :) 尽管他们说新的PowerShell更好。 - Halil Özgür

72

FOR命令!虽然我不喜欢写批处理文件,但我还是感激它。

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

would parse each line in myfile.txt, ignoring lines that begin with a semicolon, passing the 2nd and 3rd token from each line to the for body, with tokens delimited by commas and/or spaces. Notice the for body statements reference %i to get the 2nd token, %j to get the 3rd token, and %k to get all remaining tokens after the 3rd.

您可以使用此方法迭代目录、目录内容等等...


4
我发现批处理文件的FOR循环非常有限且难以编写,但有时它们确实很有用。 - ya23
2
请原谅我的困惑,但这怎么可能被低估呢?我认为如果你不懂FOR循环,你就不懂批处理脚本。 - Coding With Style
11
无论是否被充分利用,这都是折磨。(有人会认为这是必要的恶。) - harpo
我不得不使用它几次,创造这种语法的人应该被解雇。从大炮里射出去。射向太阳。它实在是太糟糕了。 - VitalyB
1
@CodingWithStyle:每次我需要在批处理文件中编写for循环时,脚本就会变成Bash启动器,并重写为Bash(或Python)脚本 :) - idbrii

60

我不会在脚本中乱用REM或::注释符号,而是在每个脚本的顶部执行以下操作:

@echo OFF
goto :START

Description of the script.

Usage:
   myscript -parm1|parm2 > result.txt

:START

请注意,您可以在不转义它们的情况下使用管道符和重定向字符。


3
如果你检查 "/?" 的 %1,那就更酷了,然后你可以将该部分作为帮助文本输出。 - demoncodemonkey
6
嗯,有文化的批处理编程?! - Muhammad Alkarouri

54

脚本所在的路径(带有驱动器)为:~dp0

set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%

我通常用%CD%来解决这个问题。也许它并不在所有的DOS Shell版本中都可用? - Saul Dolgin
11
%CD% 表示当前目录,而 %~dp0 则表示正在运行的脚本所在的目录。 - RealHowTo
此外,我认为 %CD% 以前不存在...可能只有XP。我知道一些旧版本的Windows没有它。 - Thomas Owens
1
如果批处理文件位于另一个驱动器中,您应该使用cd /d %BAT_HOME%。尽管如此,如果我没记错的话,这在旧版DOS中是行不通的。 - ketorin
如果在网络路径上运行批处理程序,则 cd 或 pushd %BATH_HOME% 将无法工作。 - Benoit
在使用像CD或COPY这样的命令时,如果路径中包含空格,则应该用一对双引号将%dp0括起来。对于基于%dp0的环境变量也是如此。例如,在本答案的示例代码中,第三行应该是cd /d "%BAT_HOME%" - MikeOnline

49

%~dp0 这个变量之前已经提到过了,但实际上它还有更多的用法: 在 ~ 后面加上一个或多个字符,就可以提取不同的信息。
如果没有任何字符,则返回批处理文件的路径和名称。
d - 返回驱动器号。
p - 返回路径。
s - 返回短路径名。
x - 返回文件扩展名。
因此,如果你从 c:\Temp\long dir name\ 文件夹中执行下面的 test.bat 脚本:

@echo off
echo %0
echo %~d0
echo %~p0
echo %~dp0
echo %~x0
echo %~s0
echo %~sp0

你会得到以下输出

test
c:
\Temp\long dir name\
c:\Temp\long dir name\
.bat
c:\Temp\LONGDI~1\test.bat
\Temp\LONGDI~1\

如果将参数传递到您的脚本中,例如
test c:\temp\mysrc\test.cpp
可以使用%1变量进行相同的操作。

但是,%0扩展的结果取决于位置!
在批处理文件的“顶部级别”中,它会扩展为当前批处理文件名。
在函数(调用)中,它扩展为函数名称。

@echo off
echo %0
call :test
goto :eof

:test
echo %0
echo %~0
echo %~n0

输出结果是(使用 myBatch.bat 启动批处理文件)

myBatch.bat
:test
:test
myBatch

43

使用CALL、EXIT /B、SETLOCAL和ENDLOCAL可以实现具有局部变量的子例程。

示例:

@echo off

set x=xxxxx
call :sub 10
echo %x%
exit /b

:sub
setlocal
set /a x=%1 + 1
echo %x%
endlocal
exit /b

这将打印

11
xxxxx

即使 :sub 修改了 x。


6
你应该使用 "goto :eof" 而不是 "exit /b",它们的效果相同,但前者是更标准的做法。 - Philibert Perusse
2
这个有标准吗?O_o - Paulius
不是“更标准”,而是更常见,因为大多数人不知道exit命令的/b选项。 - Ferruccio
3
我的错误,我以为你是指明确定义一个 :eof 标签并执行跳转。我没有意识到每个批处理文件的末尾都有一个隐含的 :eof 标签。 - Ferruccio
3
然而,如果你想让一个子程序设置一个错误级别,你就需要使用 exit /b 命令。例如:exit /b 3。 - Chris Noe
6
在返回子程序时,我发现最好使用 "exit /B" 而不是 "goto :eof"。 "goto :eof" 的问题在于,当你想要吞噬错误代码时,它可能会返回一个错误代码。例如,如果你使用 "if exist someFile echo it's here",如果 someFile 不存在,这将设置错误级别,但这并不是错误代码,也不是你想要返回的错误代码(这是 "goto :eof" 做的事情)。 - Scott Langham

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