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 |
输出"
1 | <nul set /p any-variable-name=Hello |
问题是:不可能输出前导等号,在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 |
在
echo test >"%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"
我使用
color XY
其中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:\> |
关于使用
有关完整说明,请参阅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几次;你看得越多,你死得越快。
(您也可以从用户界面设置,这可能是更好的方法。有关说明,请参见注释。还有一个很好的单行脚本来完成这项工作。)
现在,要复制,只需左键单击并拖动即可选择,右键单击即可复制。要粘贴,只需右键单击。
不再有^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 |
将输出重定向到控制台,即使批的输出已经通过
例子:CMED:
1 2 | echo a echo b > con |
打电话:
1 | foo.cmd > output.txt |
这将导致
可以使用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% |
有关更多详细信息,请访问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 |
这是怎么工作的?这是一个丑陋的黑客。
这并不是在所有情况下都有效,但它的工作频率足以成为一个有用的黑客。
/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是批处理文件的一个非常有用和未充分使用的特性。
调试命令允许您…
简而言之,这个工具非常强大。现在可能不再使用它了,但是从批处理脚本调用和控制此功能的能力为批处理脚本增加了惊人的能力。
注意:微软已经从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 |
使用
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->沙盒
用
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 /? |
这将列出用于启动命令提示的所有命令行参数以及更改系统范围行为的注册表调整。
您可以在批处理文件运行时对其进行修改。例如,如果希望在批处理文件退出之前看到结果,可以在文件运行时在文件末尾添加一个遗忘的
请参见在批处理文件运行时更改它
我个人认为这更像是一个"抓住机会"而不是一个功能。
当向批处理文件传递未知数量的参数时,例如,当将多个文件拖放到批处理文件中以启动它时,可以按名称引用每个参数变量,例如。
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= |