Windows批处理文件的隐藏功能

Hidden features of Windows batch files

Windows批处理文件的一些不太了解但又重要且有用的功能是什么?

指南:

  • 每个答案一个功能
  • 对特性进行简短描述,并给出一个示例,而不仅仅是指向文档的链接
  • 限制对本机功能的回答,即不需要其他软件,如Windows资源工具包

说明:这里我们指的是由cmd.exe处理的脚本,这是winnt变量的默认值。

(另请参见:Windows批处理文件:.bat与.cmd?)


行继续:

1
2
3
4
5
6
7
8
9
10
11
call C:\WINDOWS\system32
tbackup.exe ^
    backup ^
    /V:yes ^
    /R:no ^
    /RS:no ^
    /HC:off ^
    /M normal ^
    /L:s ^
    @daily.bks ^
    /F daily.bkf


1
PUSHD path

带您到路径指定的目录。

1
POPD

带您返回"推送"的目录。


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

1
C:\some_directory> start .

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

我发现这是一个很好的省时方法。


我一直觉得很难阅读每行上都有关键字标记的评论:

1
REM blah blah blah

易于阅读:

1
:: blah blah blah


变量子字符串:

1
2
3
4
5
6
7
> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456


执行命令!虽然我讨厌编写批处理文件,但我还是很感激。

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

将解析myfile.txt中的每一行,忽略以分号开头的行,将第二个和第三个标记从每一行传递到for主体,标记由逗号和/或空格分隔。注意,for body语句引用%i获取第二个令牌,引用%j获取第三个令牌,引用%k获取第三个令牌之后的所有剩余令牌。

您还可以使用它迭代目录、目录内容等…


我在每个脚本的顶部执行以下操作,而不是将脚本与REM或::行一起丢弃:

1
2
3
4
5
6
7
8
9
@echo OFF
goto :START

Description of the script.

Usage:
   myscript -parm1|parm2 > result.txt

:START

注意如何使用管道和重定向字符而不转义它们。


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

1
2
3
set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%


已经提到了%~dp0块,但实际上还有更多:~后面的字符定义提取的信息。没有字母返回修补程序文件名D-返回驱动器号P-返回路径S-返回短路径x-返回文件扩展名因此,如果从c: emplong dir namefolder执行下面的脚本test.bat,

1
2
3
4
5
6
7
8
@echo off
echo %0
echo %~d0
echo %~p0
echo %~dp0
echo %~x0
echo %~s0
echo %~sp0

你得到以下输出

1
2
3
4
5
6
7
test
c:
\Temp\long dir name\
c:\Temp\long dir name\
.bat
c:\Temp\LONGDI~1\test.bat
\Temp\LONGDI~1\

如果一个参数像中一样传递到脚本中测试C: empmysrc est.cpp可以对%1变量执行相同的操作。

但是扩展%0的结果取决于位置!在批处理的"顶层",它扩展到当前批处理文件名。在函数(调用)中,它扩展到函数名。

1
2
3
4
5
6
7
8
9
@echo off
echo %0
call :test
goto :eof

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

输出为(批处理文件以mybatch.bat启动)

1
2
3
4
myBatch.bat
:test
:test
myBatch

通过使用call、exit/b、setlocal&endlocal,可以使用局部变量实现子例程。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off

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

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

这将打印

1
2
11
xxxxx

即使:Sub修改X。


偷偷摸摸的把戏等待n秒(不是cmd.exe的一部分,但不是额外的软件,因为它与Windows一起提供),见ping行。您需要n+1 Ping,因为第一个Ping会立即退出。

1
2
3
4
5
6
7
8
9
10
    echo %time%
    call :waitfor 5
    echo %time%
    goto :eof
:waitfor
    setlocal
    set /a"t = %1 + 1"
    >nul ping 127.0.0.1 -n %t%
    endlocal
    goto :eof


逃离"管道":

1
echo ^| ^< ^> ^& ^\ ^^


能够运行命令和处理输出(比如bash中"$()"的倒计时)。

1
for /f %i in ('dir /on /b *.jpg') do echo --^> %i

如果文件名中有空格,请使用:

1
for /f"tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i


创建空文件:

1
> copy nul filename.ext


要隐藏命令的所有输出,请重定向至>nul 2>&;1。

例如,即使重定向到>nul,某些命令行程序也会显示输出。但是,如果您像下面的行那样重定向输出,所有输出都将被抑制。

1
PSKILL NOTEPAD >nul 2>&1

编辑:请参见忽略命令的输出,以了解如何工作的说明。


1
PAUSE

停止执行并显示以下提示:

1
Press any key to continue . . .

如果您想通过在Windows资源管理器中双击批处理来运行批处理,并且希望实际看到输出,而不仅仅是命令窗口的一个闪烁,那么这将非常有用。


相当于bash(和其他shell)

1
2
echo -n Hello # or
echo Hello\\c

输出"Hello",不带尾随换行符。执行此操作的命令:

1
<nul set /p any-variable-name=Hello

set /p是提示用户输入的一种方法。它发出给定的字符串,然后等待(在同一行,即没有CRLF)用户键入响应。

简单地将一个空响应管道发送到set /p命令,因此最终结果是发出的提示字符串。(由于响应为空,使用的变量保持不变。)

问题是:不可能输出前导等号,在Vista上,前导空格字符被删除,但在XP上不可能。


设置环境变量时搜索和替换:

1
> @set fname=%date:/=%

…从日期中删除"/"以用于时间戳文件名。

还有子字符串…

1
> @set dayofweek=%fname:~0,3%

整数算术:

1
2
> SET /A result=10/3 + 1
4


命令分隔符:

1
2
3
cls & dir
copy a b && echo Success
copy a b || echo Failure

在第2行,只有在第一个命令成功时才运行&;之后的命令。

在第3行,之后的命令仅在第一个命令失败时运行。


可以链接if语句以获得类似于短路布尔值'and'的效果。

1
if foo if bar baz


输出空行:

1
echo.

要快速将Unicode文本文件(16bit/char)转换为ASCII DOS文件(8bit/char)。

1
C:\> type unicodeencoded.txt > dosencoded.txt

另外,如果可能的话,字符被正确映射。


如果块结构:

1
2
3
4
if"%VS90COMNTOOLS%"=="" (
  echo: Visual Studio 2008 is not installed
  exit /b
)


虽然没有提供太多功能,但是您可以将标题命令用于多种用途,比如在任务栏中提供长脚本的状态,或者只是为了增强用户反馈。

1
2
3
4
@title Searching for ...
:: processing search
@title preparing search results
:: data processing


变量的延迟扩展(为了更好的度量,插入了子字符串):

1
2
3
4
5
6
7
8
9
10
11
    @echo off
    setlocal enableextensions enabledelayedexpansion
    set full=/u01/users/pax
:loop1
    if not"!full:~-1!" =="/" (
        set full2=!full:~-1!!full2!
        set full=!full:~,-1!
        goto :loop1
    )
    echo !full!
    endlocal

datetime上进行字符串减法以获取名为"yyyy-mm-dd hh:mm:ss.txt"的文件的示例

echo test >"%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"

我使用color来表示我的脚本是否成功、失败,或者通过更改文本和背景的颜色来需要一些输入。当你有一台机器在你的视线范围内,但离你很远的时候,它真的很有帮助。

color XY

其中x和y是从0F的十六进制值,其中x—背景,y—文本,当x=y颜色不变时。

color Z

将文本颜色更改为"Z"并设置黑色背景,"颜色0"不起作用

对于颜色名称,请致电

color ?


没有现成的编辑器,需要创建批处理文件吗?

1
copy con test.bat

只需键入远离命令,按Enter键输入新行即可。按ctrl-z和enter关闭文件。


使用空格和转义符对输出的总控制:

1
echo.    ^<resourceDir^>/%basedir%/resources^</resourceDir^>


Softwarejedi已经提到了for命令,但我将再次提到它,因为它非常强大。

以下以YYYYMMDD格式输出当前日期,我在生成备份目录时使用此格式。

1
for /f"tokens=2-4 delims=/-" %a in ('DATE/T') do echo %c%b%a


稍后可以使用调用计算名称,从而得到一些有用的属性。

1
call set SomeEnvVariable_%extension%=%%%somevalue%%%

使用调用设置名称依赖于其他变量的变量。如果与某些变量命名规则一起使用,则可以使用谨慎的命名规则来模拟数据集合,如数组或字典。在someValue周围有三个%的值,因此在调用后和调用set之前,它将计算为一个由单个%包围的变量名。这意味着一行中有两个%向下转义为一个%字符,然后它将再次展开,因此someValue实际上是一个名称指针。

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

使用它和一个临时变量来检索值,然后可以在逻辑中使用该值。这在与延迟变量扩展结合使用时最有用。

要正确使用此方法,需要启用延迟变量扩展。因为默认情况下它是关闭的,所以最好在脚本中启用它,方法是将其作为第一条指令之一:

1
setlocal EnableDelayedExpansion


搜索路径上的可执行文件(或其他路径,如有必要,如字符串):

1
2
3
4
5
6
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:\>


关于使用::而不是REM进行评论:小心!::是一种特殊情况下的调用标签,其作用类似于注释。在方括号内使用时,例如在for或if循环中,函数将过早退出。调试非常令人沮丧!

有关完整说明,请参阅http://www.ss64.com/nt/rem.html。

(在第一次提到这一点时添加一个新的答案而不是注释,因为我还不值得讨论:0)


仍然为endlocal使用的行分析局部变量。这允许使用以下技巧:

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

这是一种将结果传输到调用上下文的有用方法。具体来说,一旦endlocal完成,就有%somelocal%超出范围,但到那时,已经扩展了%somelocal%,因此在调用上下文中使用本地变量分配myglobal。

出于同样的原因,如果您决定这样做:

1
ENDLOCAL & SET MYLOCAL=%MYLOCAL%

您将发现新的mylocal变量现在实际上是一个常规环境变量,而不是您预期的本地化变量。


现在很多人使用goto:eof终止批处理文件,但也可以使用exit/b来终止批处理文件。

使用exit/b的好处是,您可以在exit/b之后添加一个错误级别,它将以该错误级别退出。


调用集-将环境变量扩展到多个层次。

在http://ss64.com/nt/call.html advanced from answer to another so question batch file variables initialized in a for loop找到了这个。

1
2
3
4
5
set VarName=Param
set Param=This

call set Answer=%%%Varname%%%
Echo %Answer%

给予

1
2
3
4
5
set VarName=Param
set Param=This
call set Answer=%Param%
Echo This
This


子程序(输出42):

1
2
3
4
5
6
    @echo off
    call :answer 42
    goto :eof
:do_something
    echo %1
    goto :eof

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    @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

我最喜欢在cmd.exe中使用快速编辑模式。这有点偏离主题,但是当与命令shell交互时,它可能是一个救命稻草。不,我不是夸张的——在你死之前,你只会看到Caret-Capitol-V几次;你看得越多,你死得越快。

  • 打开regedit(注意,不是我的故障、蓝屏等)
  • 前往HKCU/控制台
  • 将QuickEdit设置为1
  • (您也可以从用户界面设置,这可能是更好的方法。有关说明,请参见注释。还有一个很好的单行脚本来完成这项工作。)

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

    不再有^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v!!!!

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


    1
    SHIFT

    它是一种迭代通过在命令行上传递给脚本(或子例程)的变量数量的参数的方法。在其最简单的用法中,它将%2移动到%1,将%3移动到%2,依此类推。(您也可以传入一个参数来移位以跳过多个参数。)这会使命令"破坏性"(即%1永远消失),但它允许您避免对支持的最大数量的参数进行硬编码。

    下面是一个简短的示例,一次处理一个命令行参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    :ParseArgs

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

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

    shift

    goto :ParseArgs


    :DoneParsingArgs

    rem ...


    "删除目录"上的子目录选项:

    1
    rd /s /q junk

    将输出重定向到控制台,即使批的输出已经通过> con语法重定向到文件。

    例子:CMED:

    1
    2
    echo a
    echo b > con

    打电话:

    1
    foo.cmd > output.txt

    这将导致"a"转到output.txt,而"b"转到控制台。


    可以使用errorlevel检查批处理文件将运行的系统(当前目录或路径)上是否有给定的程序可用。为了使这项工作,您要测试的程序必须运行、退出并在退出时设置退出代码。在我使用的示例中-?作为一个arg到myexe,大多数cli程序都有类似的arg,如-h、-help、-v等…这样可以确保它只运行并退出离开或设置错误级别0

    1
    2
    3
    4
    5
    6
    7
    myExe -? >nul 2>&1
    Set errCode=%errorlevel%
    @if %errCode% EQU 0 (
        echo myExe -? does not return an error (exists)
    ) ELSE (
        echo myExe -? returns an error (does not exist)
    )

    是的,您可以直接测试错误级别,而不是将其分配给错误代码,但是这样您就可以在测试和条件之间拥有命令,并根据需要重复测试条件。


    当您想在分支之间复制文件时,一个方便的技巧是:

    1
    2
    3
    C:\src\branch1\mydir\mydir2\mydir3\mydir4>xcopy %cd:branch1=branch2%\foo*
    Overwrite C:\src\branch1\mydir\mydir2\mydir3\mydir4\foo.txt (Yes/No/All)? y
    C:\src\branch2\mydir\mydir2\mydir3\mydir4\foo.txt

    这将同时使用%cd%环境变量和环境变量替换。


    获取当前日期、月份和年份(独立的区域设置):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    for /f"tokens=1-4 delims=/-." %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
    goto :end_set_date

    :set_date
    if ("%1:~0,1%" gtr"9") shift
    for /f"skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
    goto :eof

    :end_set_date

    echo day in 'DD' format is %dd%; month in 'MM' format is %mm%; year in 'YYYY' format is %yy%


    要从脚本内部分析stdin,您需要使用for和find命令的技巧:

    1
    2
    3
    4
    for /f"tokens=*" %%g in ('find /V""') do (
         :: do what you want with %%g
         echo %%g
    )


    带有数字变量的循环的正确格式是

    1
    for /l %%i in (startNumber, counter, endNumber) do echo %%i

    更多详细信息>http://www.ss64.com/nt/for.html


    choice命令提示用户输入多个选项之一(通过一次按键)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @echo off
    echo Please choose one of the following options
    echo 1. Apple
    echo 2. Orange
    echo 3. Pizza
    echo a, b, c. Something else
    choice /c:123abc /m"Answer?"
    set ChoiceLevel=%ErrorLevel%
    echo Choice was: %ChoiceLevel%

    %ChoiceLevel%将是所选的第n个选项(在上面的示例中,b=5)。

    有关更多详细信息,请访问ss64.com上的choice reference页面。


    使用&::的内联注释。

    1
    2
    3
    4
    :: This is my batch file which does stuff.
    copy thisstuff thatstuff  &:: We need to make a backup in case we screw up!

    :: ... do lots of other stuff

    这是怎么工作的?这是一个丑陋的黑客。&是命令分隔符,大致与unix shell的;相似。::是另一个丑陋的黑客,有点像REM声明。最终的结果是,执行命令,然后执行"不做任何事情"命令,从而近似于注释。

    这并不是在所有情况下都有效,但它的工作频率足以成为一个有用的黑客。


    /C param命令,命令它运行然后执行这些命令。

    我发现自己经常做:

    win+r,命令返回,ping google.com返回

    但现在我只做了:

    win+r,cmd/c ping google.com返回

    快得多。如果您使用的是pstools,并且希望使用psexec在远程计算机上执行一些命令行功能,那么这也很有用。

    编辑:/k的工作原理相同,但保持提示打开。这可能更常用。


    值得一提的是,这是一个很好的WindowsCmd或批处理文件的在线参考。我从中学到了一些我不知道的东西。


    具有正则表达式支持的findstr:

    1
    findstr"^[0-9].*" c:\windows\system32\drivers\etc\hosts

    使用pushd到unc路径将创建一个临时驱动器映射(从z开始向后工作以查找下一个可用字母),并将您放入该驱动器和路径中。当弹出或退出命令提示时,临时映射将消失。

    1
    2
    3
    4
    5
       C:\>pushd \\yourmom\jukebox

       Z:\>pushd \\yourmom\business

       Y:\>

    此外,与命令行环境提示相比,批处理提示的作用不大,但是当您使用pushd、popd和network share s在命令行中工作时,使用$+(show pushd stack depth)和$m(show network share path)修改提示很有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
       C:\utils>prompt $+$m$p$g

       C:\utils>pushd m:

       +\\yourmom\pub M:\>pushd c:\

       ++c:\>pushd
       M:\
       C:\utils  

       ++c:\>popd

       +\\yourmom\pub M:\>popd

       C:\utils>

    使用管道""命令在文件夹中的文件中查找字符串:

    1
    dir /b *.* | findstr /f:/"thepattern"

    我发现将命令输出重定向到文件非常容易:

    1
    2
    DIR *.txt > tmp.txt
    DIR *.exe >> tmp.txt

    单箭头创建或覆盖文件,双箭头附加到文件。现在我可以在我的文本编辑器中打开tmp.txt并做各种各样的好事情。


    批处理文件中的数组。

    设置一个值:

    1
    2
    set count=1
    set var%count%=42

    在命令行提取值:

    1
    call echo %var%count%%

    在批处理文件中提取值:

    1
    call echo %%var%count%%%

    请注意额外的straffing%符号。

    这项技术看起来可能有点毛茸茸的,但非常有用。如上所述,将打印var1(即42)的内容。如果我们想将其他变量设置为var1中的值,也可以用set替换echo命令。这意味着以下是命令行中的有效分配:

    1
    call set x=%var%count%%

    然后查看va1的值:

    1
    echo %x%

    DOSKEY宏。

    我早就失去了这方面的参考,但我仍然认为这是一个好主意,值得分享。

    我们可以将批处理文件和doskey脚本合并到单个文件中。这可能看起来有点过于聪明,但它起作用了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ;= @echo off
    ;= rem Call DOSKEY and use this file as the macrofile
    ;= %SystemRoot%\system32\doskey /listsize=1000 /macrofile=%0%
    ;= rem In batch mode, jump to the end of the file
    ;= goto end

    ;= Doskey aliases
    h=doskey /history

    ;= File listing enhancements
    ls=dir /x $*

    ;= Directory navigation
    up=cd ..
    pd=pushd

    ;= :end
    ;= rem ******************************************************************
    ;= rem * EOF - Don't remove the following line.  It clears out the ';'
    ;= rem * macro. Were using it because there is no support for comments
    ;= rem * in a DOSKEY macro file.
    ;= rem ******************************************************************
    ;=

    它通过定义一个假doskey宏";"来工作,当它被解释为批处理文件时,它被优雅地(或无声地)忽略。

    我已经缩短了这里列出的版本,如果你想要更多,请点击这里。


    例如,for files对于递归删除超过两天的所有文件非常有用。

    1
    forfiles /D -2 /P"C:\Temp" /S /C"cmd /c del @path"

    将ErrorLevel设置为所需任何数字的方法:

    1
    CMD /C EXIT number

    与上面类似,使用call、exit/b、setlocal&endlocal可以实现带有局部变量和返回值的函数。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @echo off

    set x=xxxxx
    call :fun 10
    echo"%x%"
    echo"%y%"
    exit /b

    :fun
    setlocal
    set /a y=%1 + 1
    endlocal & set x=%y%
    exit /b

    这将打印:

    1
    2
    "11"
    ""

    Y变量从不离开本地作用域,但是由于cmd一次解析一行的方式,您可以将值提取到父作用域中的x变量中。


    我会说debug.exe是批处理文件的一个非常有用和未充分使用的特性。

    调试命令允许您…

  • 组装和拆卸16位代码
  • 读/写内存(现代受保护的内存使其用处大大降低。)
  • 从硬盘读取数据扇区,原始
  • 十六进制编辑

    简而言之,这个工具非常强大。现在可能不再使用它了,但是从批处理脚本调用和控制此功能的能力为批处理脚本增加了惊人的能力。

    注意:微软已经从64位版本的WindowsXP和Vista中删除了这个命令,并打算从我听说的Windows7中完全删除它。


    递归搜索目录树中的字符串:

    1
    findstr /S /C:"string literal" *.*

    还可以使用正则表达式:

    1
    findstr /S /R"^ERROR" *.log

    递归文件搜索:

    1
    dir /S myfile.txt

    使用copy附加文件:

    1
    copy file1.txt+file2.txt+file3.txt append.txt

    此外,要将所有CLI参数设置为单个变量,请执行以下操作:

    1
    SET MSG=%*

    这将获取由空格分隔的每个单词(或符号),并将其保存到单个批处理文件变量中。从技术上讲,每个参数都是%1、%2、$3等,但是这个set命令使用通配符来引用stdin中的每个参数。

    批处理文件:

    1
    2
    @SET MSG=%*
    @echo %MSG%

    命令行:

    1
    2
    C:\test>test.bat Hello World!
    Hello World!

    符号链接:

    1
    2
    3
    mklink /d directorylink ..
    ealdirectory
    mklink filelink realfile

    该命令是Windows Server 2008及更高版本(包括Vista和Windows 7)上的本机命令。(它也包含在一些Windows资源工具包中。)


    列出所有驱动器:

    1
    fsutil fsinfo drives


    使用SET /P文件设置环境变量

    1
    SET /P SVNVERSION=<ver.tmp


    错误保释。

    1
    2
    3
    4
    IF"%errorlevel%" NEQ"0" (
       echo"ERROR:  Something broke.  Bailing out."
       exit /B 1
    )

    Goto:EOF粘贴板

    我在脚本的末尾添加了"goto:eof"作为代码片段的方便空间。这样我就可以快速地复制/粘贴到该区域和从该区域粘贴,而无需注释/取消注释。

    1
    2
    3
    4
    5
    6
    goto :eof
    :: code scraps
    call this.bat
    call that.bat
    set TS=%DATE:~10%%DATE:~4,2%%DATE:~7,2%-%TIME:~0,2%%TIME:~3,2%%TIME:~6%%
    for /R C:\temp\ %%G in (*.bak) DO del %%G

    删除周围的引号。

    1
    for /f"useback tokens=*" %%a in ('%str%') do set str=%%~a

    我最近必须编写一个批处理文件,它由vs prebuild event调用,我想在项目目录中作为参数传递。在批处理文件中,我需要将路径与嵌套的子文件夹名称连接起来,但首先需要删除周围的引号。


    创建并开始编辑新文件

    1
    2
    3
    copy con new.txt
    This is the contents of my file
    ^Z

    ctrl+z发送ASCII EOF字符。这就像巴什的Heredocs:

    1
    2
    3
    cat <<EOF > new.txt
    This is the contents of my file
    EOF

    隐藏交互式批处理脚本的输入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
      @echo off

      echo hP1X500P[PZBBBfh#b##fXf-V@`$fPf]f3/f1/5++u5>in.com

      set /p secret_password="Enter password:"<nul

      for /f"tokens=*" %%i in ('in.com') do (set secret_password=%%i)

      del in.com


    这里介绍如何通过扫描给定目录来构建类路径。

    1
    2
    3
    4
    setlocal ENABLEDELAYEDEXPANSION
    if defined CLASSPATH (set CLASSPATH=%CLASSPATH%;.) else (set CLASSPATH=.)
    FOR /R .\lib %%G IN (*.jar) DO set CLASSPATH=!CLASSPATH!;%%G
    Echo The Classpath definition is %CLASSPATH%

    在XP中工作(或更好)。对于W2K,需要使用两个BAT文件来获得相同的结果(请参见在类路径定义中包含所有JAR)。

    1.6不需要它,因为您可以直接在类路径中指定通配符(例如-cp".lib*")。


    一行中有多个命令,在许多情况下都很有用:

    &用于组合两个命令,执行命令1,然后执行命令2(amp;U)&条件组合,如果command1成功完成,则执行command2||只有当command1未成功完成时,command2才会执行。

    实例:

    1
    2
    3
    4
    5
    6
    7
    :: ** Edit the most recent .TXT file and exit, useful in a .CMD / .BAT **
    FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT


    :: ** If exist any .TXT file, display the list in NOTEPAD, if not it
    :: ** exits without any error (note the && and the 2> error redirection)
    DIR *.TXT > TXT.LST 2> NUL && NOTEPAD TXT.LST


    允许您根据环境变量更改目录必须指定"%"指令。如果指定的变量不存在,然后尝试目录名。

    1
    @if defined %1 (call cd"%%%1%%") else (call cd %1)

    我真的很喜欢这个windows xp命令参考,以及顶部的语法链接;它涵盖了其他答案中已经找到的许多提示和技巧。


    还有Edlin命令。虽然它可能是一个旧的混蛋工具,曾经用于基于行的文本编辑,事实上它是从命令行控制,使它非常有用的批脚本编写,主要是因为,就像任何其他情况下你会使用edlin,它是唯一的工具可用。毕竟,除非你有点受虐狂,否则Edlin并不是你通常想要用来编辑文本的工具。引用TimPatterson(写这篇文章的人)的话:"当我听说IBM在使用它,而不是把它扔出窗外时,我吓了一跳。"

    注:Edlin在编辑的文件中添加了老式的eof(1a)标记。如果需要删除它们,可能需要使用调试。


    在脚本中使用命令扩展外壳选项时,强烈建议您在脚本开始时执行以下技巧。

    --从http://www.ss64.com/nt/setlocal.html粘贴的信息

    SETLOCAL will set an ERRORLEVEL if given an argument. It will be zero if one of the two valid arguments is given and one otherwise.

    You can use this in a batch file to determine if command extensions are available, using the following technique:

    1
    2
    3
    VERIFY errors 2>nul
    SETLOCAL ENABLEEXTENSIONS
    IF ERRORLEVEL 1 echo Unable to enable extensions

    This works because"VERIFY errors" sets ERRORLEVEL to 1 and then the SETLOCAL will fail to reset the ERRORLEVEL value if extensions are not available (e.g. if the script is running under command.com)

    If Command Extensions are permanently disabled then SETLOCAL ENABLEEXTENSIONS will not restore them.


    要获取用于日志文件等的当前日期/时间,我在批处理文件中使用它:

    1
    2
    for /f"usebackq tokens=1,2,3,4,5,6,7 delims=/:." %%a in (`echo %DATE% %TIME%`) do set NOW=%%d%%b%%c_%%e%%f%%g
    set LOG=output_%NOW%.log

    我使用它们作为常用目录的快捷方式。一个名为"sandbox.bat"的示例文件,它位于我路径中的一个目录中

    1
    EXPLORER"C:\Documents and Settings\myusername\Desktop\sandbox"

    调用脚本只是win+r->沙盒


    FIND代替grep。我用find为自己盗取了一个小"电话簿"。非常有用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @echo off
    :begin
    set /p term=Enter query:
    type phonebookfile.txt |find /i"%term%"
    if %errorlevel% == 0 GOTO :choose
    echo No entry found
    set /p new_entry=Add new entry:
    echo %new_entry% >> phonebookfile.txt
    :choose
    set /p action=(q)uit, (n)ew query or (e)dit? [q]
    if"%action%"=="n" GOTO anfang
    if"%action%"=="e" (
        notepad phonebookfile.txt
        goto :choose
    )

    非常快速有效。


    一个非常古老的(ca 1990)技巧来获得环境变量的总大小:

    1
    2
    3
    set > test
    dir test
    del test


    对于带数字计数器的循环(输出1到10):

    1
    for /l %i in (1,1,10) do echo %i

    基于行的执行

    在大多数情况下,这并不是一个明确的好处,但在运行时尝试更新内容时,它会有所帮助。例如:

    更新源.bat

    1
    2
    copy UpdateSource.bat Current.bat
    echo"Hi!"

    当前蝙蝠

    1
    copy UpdateSource.bat Current.bat

    现在,执行current.bat会产生这个输出。

    1
    HI!

    但是要注意,批处理执行是按行号进行的。如果基本行没有完全相同的行号,这样的更新最终可能会跳过或移回一行。


    这里有一个技巧,我使用它连续运行我的nant构建脚本,而不必反复单击批处理文件。

    1
    2
    3
    4
    :CODELINE
    NANT.EXE -buildfile:alltargets.build -l:build.log build.product
    @pause
    GOTO :CODELINE

    会发生的是,在解决方案完成构建之后,它将暂停。然后,如果您按下任何键,它将重新运行构建脚本。我得说非常方便。


    这个批处理文件既可以与简单的文件一起使用,也可以与目录作为命令行参数(可以按任意顺序混合)。循环在任何指定的文件上运行命令("echo"(本例中为"echo"),如果参数是目录,则循环在其中的每个文件上递归运行命令。

    1
    2
    @echo off
    for /f"delims=" %%f in ('dir %* /a-d /b /s') do echo %%f


    如果命令!没有它,我的批处理文件就是垃圾!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @echo off
    IF exist %windir%\system32\iexplore.exe goto end

    echo Hmm... it seems you do not have Internet Explorer.
    echo Great! You seem to understand ;)

    :end
    echo Hmm... You have Internet Explorer.
    echo That is bad :)


    1
    HELP

    当使用不同的操作系统版本时,了解本地可用的命令是很重要的。在命令提示下键入HELP将显示哪些命令可用,并简要说明它们的作用。

    1
    cmd.exe /?

    这将列出用于启动命令提示的所有命令行参数以及更改系统范围行为的注册表调整。


    您可以在批处理文件运行时对其进行修改。例如,如果希望在批处理文件退出之前看到结果,可以在文件运行时在文件末尾添加一个遗忘的pause

    请参见在批处理文件运行时更改它

    我个人认为这更像是一个"抓住机会"而不是一个功能。


    当向批处理文件传递未知数量的参数时,例如,当将多个文件拖放到批处理文件中以启动它时,可以按名称引用每个参数变量,例如。

    1
    2
    3
    4
    5
    6
    TYPE %1
    TYPE %2
    TYPE %3
    TYPE %4
    TYPE %5
    ...etc

    但当您想检查每个参数是否存在时,这会变得非常混乱:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    if [%1] NEQ [] (
    TYPE %1
    )
    if [%2] NEQ [] (
    TYPE %2
    )
    if [%3] NEQ [] (
    TYPE %3
    )
    if [%4] NEQ [] (
    TYPE %4
    )
    if [%5] NEQ [] (
    TYPE %5
    )
    ...etc

    此外,使用此方法只能接受有限数量的参数。

    相反,尝试使用shift命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    :loop
    IF [%1] NEQ [] (
    TYPE %1
    ) ELSE (
    GOTO end
    )
    SHIFT
    GOTO loop
    :end

    SHIFT将所有参数向下移动一个,因此%2变为%1,而%3变为%2等。


    要从文件的第一行设置一个enivroment变量,我使用以下方法:

    1
    2
    3
    rem a.txt contains one line: abc123
    set /p DATA=<a.txt
    echo data: %DATA%

    将输出:ABC123


    批处理脚本最常见的要求之一是记录生成的输出以供以后查看。是的,您可以将stdout和stderr重定向到一个文件,但是除非跟踪日志文件,否则您将看不到发生了什么。

    因此,考虑使用stdout/stderr日志记录工具(如logger)运行批处理脚本,该工具将使用时间戳记录输出,并且您仍然能够看到脚本的进度。

    还有另一个stdout/stderr日志记录实用程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Yet another stdout/stderr logging utility [2010-08-05]
    Copyright (C) 2010 LoRd_MuldeR <[email protected]>
    Released under the terms of the GNU General Public License (see License.txt)

    Usage:
      logger.exe [logger options] : program.exe [program arguments]
      program.exe [program arguments] | logger.exe [logger options] : -

    Options:
      -log <file name>  Name of the log file to create (default:"<program> <time>.log")
      -append           Append to the log file instead of replacing the existing file
      -mode <mode>      Write 'stdout' or 'stderr' or 'both' to log file (default: 'both')
      -format <format>  Format of log file, 'raw' or 'time' or 'full' (default: 'time')
      -filter <filter>  Don't write lines to log file that contain this string
      -invert           Invert filter, i.e. write only lines to log file that match filter
      -ignorecase       Apply filter in a case-insensitive way (default: case-sensitive)
      -nojobctrl        Don't add child process to job object (applies to Win2k and later)
      -noescape         Don't escape double quotes when forwarding command-line arguments
      -silent           Don't print additional information to the console
      -priority <flag>  Change process priority (idle/belownormal/normal/abovenormal/high)
      -inputcp <cpid>   Use the specified codepage for input processing (default: 'utf8')
      -outputcp <cpid>  Use the specified codepage for log file output (default: 'utf8')

    提取随机文本行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    @echo off

    :: Get time (alas, it's only HH:MM xM

    for /f %%a in ('time /t') do set zD1=%%a



    :: Get last digit of MM

    set zD2=%zD1:~4,1%



    :: Seed the randomizer, if needed

    if not defined zNUM1 set /a zNUM1=%zD2%


    :: Get a kinda random number

    set /a zNUM1=zNUM1 * 214013 + 2531011

    set /a zNUM2=zNUM1 ^>^> 16 ^& 0x7fff


    :: Pull off the first digit

    :: (Last digit would be better, but it's late, and I'm tired)

    set zIDX=%zNUM2:~0,1%


    :: Map it down to 0-3

    set /a zIDX=zIDX/3


    :: Finally, we can set do some proper initialization

    set /a zIIDX=0

    set zLO=

    set zLL=""


    :: Step through each line in the file, looking for line zIDX

    for /f"delims=@" %%a in (c:\lines.txt) do call :zoo  %zIDX%  %%a


    :: If line zIDX wasn't found, we'll settle for zee LastLine

    if"%zLO%"=="" set zLO=%zLL%

    goto awdun


    :: See if the current line is line zIDX

    :zoo


    :: Save string of all parms

    set zALL=%*


    :: Strip off the first parm (sure hope lines aren't longer than 254 chars)

    set zWORDS=%zALL:~2,255%


    :: Make this line zee LastLine

    set zLL=%zWORDS%


    :: If this is the line we're looking for, make it zee LineOut

    if {%1}=={%zIIDX%} set zLO=%zWORDS%


    :: Keep track of line numbers

    set /a zIIDX=%zIIDX% + 1

    goto :eof




    :awdun

    echo ==%zLO%==


    :: Be socially responsible

    set zALL=

    set zD1=

    set zD2=

    set zIDX=

    set zIIDX=

    set zLL=

    set zLO=

    :: But don't mess with seed

    ::set zNUM1=

    set zNUM2=

    set zWORDS=