您的位置:首页 > 其它

Windows Batch 1-4

2016-05-09 11:30 316 查看

Guide to Windows Batch Scripting

http://steve-jansen.github.io/guides/windows-batch-scripting/index.html

Overview

batch可以帮助配置 DevOps , 提高每天的工作效率.

Part 1 – Getting Started

Launching the Command Prompt

keyboard shortcut
Windows Logo Key + R


输入
cmd.exe


Editing Batch Files

Windows Logo Key + R
输入
notepad
notepad++


batch文件是 ASCII text, 差不多所有的编辑器都可以用来编辑它;

Viewing Batch Files

可以直接在编辑器中查看batch文件;

在 DOS cmmand中可以使用下列命令查看文件:

-
TYPE myscript.cmd


-
MORE myscript.cmd


-
EDIT myscript.cmd


Batch File Names and File Extensions

建议的后缀名:
.cmd


90年代的 Windows使用
.bat


区别:

avoid some rare side effects with .bat files

The differences between .CMD and .BAT as far as CMD.EXE is concerned are: With extensions enabled, PATH/APPEND/PROMPT/SET/ASSOC in .CMD files will set

ERRORLEVEL regardless of error. .BAT sets ERRORLEVEL only on errors.

使用
.cmd
后缀名, 你可以使用任何文件名; 建议不要在文件名中加入空格, 在 shell脚本中空格只会带来麻烦;

利用Pascal cast可以帮助避免空格;

e.g. 使用
HelloWorld.cmd
代替
Hello World.cmd
; 可以使用标点如
.
or
-
or
_
(e.g.
Hello.World.cmd, Hello-World.cmd, Hello_World.cmd
)

另外要注意的是, 避免和 built-in的 command, 系统文件或者流行的程序重名.

e.g. 避免使用
ping.cmd
为已经有了一个广泛使用的系统文件
ping.exe
; 如果你无意间运行
ping
, 有可能是调用了
ping.cmd
而不是真正想要的
ping.exe
, 那么情况会变得很令人困惑.

建议使用
RemoteHeartbeat.cmd
或在脚本名字中添加一些细节, 这样还能避免和其他的可执行文件发生名字冲突.

当然, 也存在很特殊的情况: 就是你想把
ping
的默认行为修改掉, 那么可以无视这里的名字规范.

Saving Batch Files in Windows

Notepad默认会尝试将所有文件当做 plain jane text[普通]文件格式保存;

要让 Notepad将文件保存为
.cmd
格式, 需要使用 “Save as type”下拉菜单选为 “All Files(.)”;

注意, Encoding选项, 英文一般为 ASCII.

e.g. 文件名设为
%USERPROFILE%\HelloWorld.cmd


%USERPROFILE%
关键字是 Windows环境变量, 代表 user profile文件夹的目录地址; 在较新的 Windows系统中, user profile文件夹一般是:
C:\Users\<your username>
;

这个shortcut可以帮你省点时间, 因为一个新的 command prompt通常是默认是在你的 user profile文件夹的 “working directory”目录下;

有了这个shortcut, 就可以不用预先修改当前目录或者指定脚本路径了.

Running your Batch File

运行batch文件的简单方式是双击文件; 不过, 在command prompt中才有机会看到更多的output和error.

当脚本退出的时候, command prompt的窗口会立即消失. ()参见 Part 10 – Advanced Tricks)

运行一个新脚本时, 你可能向要在打开的command窗口中运行batch文件; 对于初学者, 可以直接把脚本拖到command prompt窗口中; command prompt在command line显示出脚本的全路径, 将包含空格的路径用引号括起.

Tips:

- 可以按上下箭头键. 在command line历史中查找用过的命令.

-
%COMPSPEC% /C /D "C:\Users\User\SomeScriptPath.cmd" Arg1 Arg2 Arg3


这个命令让脚本在一个新的command prompt 子进程中运行.
/C
选项表示脚本结束时子进程退出;
/D
选项将disable auto-run脚本 (可选);

这么做的原因是: 防止command prompt窗口被自动关闭 – 被
EXIT
命令退出;
EXIT
命令会自动关闭command prompt窗口, 除非它是在command prompt的子进程中调用的. 窗口被关闭挺烦人的, 因为你来不及看脚本所输出的信息.

Comments

官方定义了
REM
(Remark)关键字:

REM This is a comment!


power user方法是使用
::
, 这是利用两个lable操作符
:
的hack方式

多数人发现
::
比起
REM
来不那么容易分心; 不过要注意有几个地方’::’会造成error.

:: This is a comment too!! (usually!)


e.g.
FOR
循环会对
::
风格的注释输出error. 简单的回退方案是使用
REM


Silencing Display of Commands in Batch Files

第一行非注释的batch命令一般是关闭打印(ECHO)

@ECHO OFF


@
是个特殊操作符, 用来抑制command line中的打印; 一旦将 ECHO设为off, 就不再需要
@
操作符了.

恢复命令行打印:

ECHO ON


在退出脚本前, command prompt会自动恢复ECHO的上个状态.

Debugging Your Scripts

batch有许多的 trial和 error coding. 可惜这里没有 WIndows batch script的 debugger(调试器). 更糟的是, 这里也没有将 command processor放入 verbose state来帮助troubleshoot 的方法 (这是Unix/Linux脚本的通用技术).

使用
ECHO
打印自定义的ad-hoc(专门)信息可能是唯一的选择; 进阶的脚本开发者可以使用一些trickery来选择性地打印信息, 不过还是建议在脚本功能完善的时候把 debugging/instrumentation 代码移除掉.

Part 2 – Variables

变量, 在一个non-trivial batch程序中是必须的; 变量的语法会有些奇怪;

Variable Declaration

DOS无需声明变量. 未声明/未初始化的变量是个空的string, 或者
""
. 多数人喜欢这个, 因为可以减少代码量; 不过这样也容易出傻bug, 比如变量名有typo(type error打印错误);

Variable Assignment

SET
命令将值赋给变量

SET foo=bar


NOTE:不要在名字和之间加入whitespace;
SET foo = bar
无法工作;

/A
可以在赋值时打开arthimetic支持; 这个工具很有用, 可以检验用户输入是否是个数字值;

SET /A four=2+2
4


一般的约定是给变量使用小写名字; 系统级别的变量, 比如环境变量, 使用大写名字; 这些环境描述符指示了系统中某些东西的地址, e.g.
%TEMP%
是临时文件的目录;

DOS是大小写敏感的, 因此约定虽然没有强制, 但最好是这么配置, 让阅读和调试简单些.

WARNING:
SET
总是会覆盖(clobber)任何已有的变量; 在写脚本的时候最好先检验一下是否会将系统级别的变量覆盖; 快速方法是
ECHO %foo%
, 确认
foo
不是个已有变量;

e.g. 你可能想命名一个”temp”变量, 但并不想要改变环境变量“%TEMP%”; DOS包含一些”dynamic”环境变量, 它们更像是命令; 这些dynamic变量包含
%DATE%
,
%RANDOM%
,
%CD%
; 覆盖这些dynamic变量是个糟糕的主意;

Reading the Value of a Variable

大多数情况下你可以用
%<var>%
来读取变量的值; 下面的例子在console中打印出
foo
变量的当前值:

C:\> SET foo=bar
C:\> ECHO %foo%
bar


有些特殊情况变量不使用
%
语法;

Listing Existing Variables

不带参数的
SET
命令会将当前command prompt会话中的所有变量打印出来; 其中多数是系统级别的环境变量, 如
%PATH%
,
%TEMP%
.

NOTE: 调用
SET
会列出当前对话中所有的 regular(static)变量; 列表中包含 dynamic环境变量如
%DATE%
,
%CD%
; 可以通过SET的帮助文档来查看这些dynamic环境变量: 调用
SET /?
.

Variable Scope (Global vs Local)

默认情况下, 变量对于整个command prompt会话来说是global的; 调用
SETLOCAL
目录来使得变量对于脚本来变成local; 调用
SETLOCAL
后, 变量的赋值会在调用 `ENDLOCAL’, ‘EXIT’或者执行到达脚本的EOF(end of file)之后被revert(恢复);

下例演示了改变一个已有变量名
foo
, 脚本为
HelloWorld.cmd
; shell在
HelloWorld,cmd
退出时恢复变量
%foo%
的原始值;

>TYPE HelloWorld.cmd
SETLOCAL
SET v=Local Value
ECHO v=%v%

>SET v=Global Value
>ECHO v=%v%
v=Global Value

>HelloWorld.cmd
>SETLOCAL
>SET v=Local Value
>ECHO v=Local Value
v=Local Value

>ECHO v=%v%
v=Global Value
>


真实世界中的例子可能会是一个修改系统级别
%PATH%
环境变量的脚本,

>TYPE LocalPath.cmd
SETLOCAL
SET PATH=%SystemRoot%\system32
ECHO %PATH%

>ECHO %PATH%
REM Original %PATH%
"C:\Windows\system32;C:\Windows\System32\WindowsPowerShell\v1.0\"

>LocalPath.cmd
>SETLOCAL
>SET PATH=%SystemRoot%\system32
>ECHO %PATH%
REM %PATH% modified locally
"C:\Windows\system32"

>ECHO %PATH%
REM Original %PATH% restored
"C:\Windows\system32;C:\Windows\System32\WindowsPowerShell\v1.0\"


Special Variables

有少数特殊情况下变量工作起来有些不同; 在command line上传递给脚本的参数也是变量, 然而, 却不使用
%var%
语法;

代替的是: 使用单个
%
带数字0-9来读取每个参数, 每个数字依次代表参数的位置; 在随后的bacth脚本中可以看到一个创建function/subroutine的hack方式有一样的风格;

还有些变量语法使用
!
,
!var
; 这是个调用delayed expansion(延迟扩展)的特殊情况; 在后面讨论 condition(if/then)和looping的时候可以了解更多细节;

Command Line Arguments to Your Script

通过特殊语法可以读取传递给脚本的command line参数;

语法: 单个
%
字符后面跟参数的位置
0-9
; zero顺位参数是batch文件的名称; 因此脚本
HelloWorld.cmd
中变量
%0
代表”HelloWorld.cmd”;

command line参数:

-
%0
脚本/程序名, 总是个非空值

-
%1
第一个command line参数, 如果参数没提供, 就为空;

-
%2
第二个command line参数, 如果参数没提供, 就为空;

- …

-
%9
第九个参数

NOTE: DOS支持超过9个command line参数, 不过你无法直接读取第10个或更多的参数; 因为特殊变量语法不支持
%10
或更多; 事实上, shell会将
%10
读作
%0
的后缀 –> sting “0”;

使用
SHIFT
目录将第一个参数从参数列表中pop出去, 这样就把所有的参数左移一格; e.g. 第二个参数从位置
%2
移到
%1
, 这样第10个参数就成了
%9
;

后面会看到如何在循环中处理大量的参数;

Tricks with Command Line Arguments

Command Line Arguments也支持一些有用的可选语法, 在command line参数是文件名的时候, 对其运行quasi-macros(类似宏)的操作;

这些 marco被称为 变量substitution support, 可以从command line参数中解析path, timestamp, 或者file size;

对于这个超有用的特性来说文档有些难找 – 运行
FOR /?
来查看.

%~1
对第一个command line参数移除quotes(引号), 这个在参数作为文件路径时很有用; 你会需要在文件路径上加上引号, 但是, 在一个文件路径上两次加上引号会造成 file not found error.

SET myvar=%~1


%~f1
是第一个参数的folder(文件夹)的全路径

%~fs1
和上面的一样, 但是多出来的
s
选项 yield出 DOS 8.3 short name path到第一个参数中; (e.g.
C:\PROGRA~1
是常用的8.3 short name变体 – 对于
C:\Program Files
)

在使用第三方脚本或程序的时候这很有用, 这样就无需处理文件路径中的空格了;

%~dp1
是第一个参数的 parent folder的全路径; 在每个测试脚本文件自身位置的batch文件中都可以用这个trick;

语法
SET parent=%~dp0
会将脚本的 folder的路径放入变量 ‘%parent%’.

%~nx1
代表第一个参数中的文件名以及文件后缀名; 可以用来在运行时检测脚本的名字; 如果需要将信息打印出来, 可以给消息加上脚本名称的前缀: e.g.
ECHO %~n0: some message
代替
ECHO some message
;

这里的前缀可以帮到终端用户, 可以知道输出是从脚本打印而不是另一个脚本调用的程序所打印的; 要是花几个小时来追踪一个脚本生成的obtuse error message的话, 其实是挺蠢的. 这是从 Unix/Linux世界中学到的一招改进;

Some Final Polish

在batch脚本顶部加入

SETLOCAL ENABLEEXTENSIONS
SET me=%~n0
SET parent=%~dp0


SETLOCAL
命令确保在脚本退出时就不会再clobber已有变量;
ENABLEEXTENSIONS
参数启用一个很有用的特性, 称为 command processor extension;

%me%
中保存的是脚本名(不包括文件后缀); 可以给打印的message加上前缀 (e.g.
ECHO %me%: some message
)

%parent%
中存储的是脚本的parent path(父路径); 可以用来给当前脚本相同目录下的文件提供 fully qualified filepath.

Part 3 – Return Codes

return code是和脚本外部的执行者进行交流的正确方法; 可惜的是很多Windows开发者都忽视了它.

Return Code Conventions

根据约定, 当执行成功, command line execution应该返回 zero, 而执行失败则返回 non-zero; Warning message则不影响return code.

Checking Return Codes In Your Script Commands

环境变量
%ERRORLEVEL%
包含了最后执行的程序或脚本的 return code; 一个有用的特性: built-in的 DOS命令如
ECHO
,
IF
,
SET
会保留现有的
%ERRORLEVEL%
的值;

检查 non-zero return code的conventional technique(传统方法)是在
IF
命令中使用
NEQ
(Not-Equal-To)操作符;

IF %ERRORLEVEL% NEQ 0 (
REM do something here to address the error
)


另一个通用的方法:

IF ERRORLEVEL 1 (
REM do something here to address the error
)


当return code是任何数字–等于1或大于1时,
ERRORLEVEL 1
语句为true;

不过程序返回正数的同时也有可能返回负数, 这时候这个语句就会有问题;

大多程序不会描述每个可能的return code, 因此最好是显式地检查 non-zero:
NEQ 0
风格, 而不要假设 return code在error时会是1或大于1;

一些特殊的error code. e.g. 测试一个可执行程序或脚本是在你的PATH中, 调用程序然后检查 return code 9009;

SomeFile.exe
IF %ERRORLEVEL% EQU 9009 (
ECHO error - SomeFile.exe not found in your PATH
)


不是多查看trial和尝试error的话很难预先了解到这些特殊用法; 记住, 这是 duct tape programming. 它不总是很漂亮, 但可以完成工作;

Conditional Execution Using the Return Code

这里有个超赞的shorthand(便利方法)用来基于第一个命令的成功或失败, 执行第二个命令; 第一个程序/脚本必须遵从成功返回0, 失败返回non-0的规则;

要在成功后执行一个 follow-on(跟随)命令, 使用
&&
操作符;

SomeCommand.exe && ECHO SomeCommand.exe succeeded!


要在失败后执行一个 follow-on命令, 使用
||
操作符;

SomeCommand.exe || ECHO SomeCommand.exe failed with return code %ERRORLEVEL%


和面向对象中的 &&, || 操作符不同, 0 && xxx 竟然是执行xxx, non-0 || xxx 竟然是执行 xxx; 而且 0 || xxx不执行后面的, non-0 && xxx也不执行后面的… 简直是反过来的短路规则

这项技术可以用来在发生错误的时候halt(叫停)脚本; 默认情况下, command processor会在error出现的时候继续执行; 为了实现 halt on error(错误时中止), 不得不要进行编码;

一个halt on error的简单方案是使用 带
/B
switch(开关)的
EXIT
命令(退出当前的batch脚本内容, 而不是command prompt process);

如果是从外部执行batch脚本, 则还是会退出CMD.exe;

还要从失败的命令中返回一个特定的 non-zero return code来通知调用者:

SomeCommand.exe || EXIT /B 1


类似的方法还有使用 隐式的 GOTO lable
:EOF
(End-Of-File); 这样跳到EOF可以退出当前脚本, 并且return code为 1;

SomeCommand.exe || GOTO :EOF


Tips and Tricks for Return Codes

推荐成功的 return code都为 zero, 对于 DOS batch文件都返回正值; 正值是因为调用者可能会使用
IF ERRORLEVEL 1
语法来检测脚本;

另外建议给可能的return code都加上文档, 在脚本头上使用便于阅读的
SET
语句:

SET /A ERROR_HELP_SCREEN=1
SET /A ERROR_FILE_NOT_FOUND=2


注意这里打破了之前的约定, 使用了大写的变量名 – 这是想表示这些变量是常量, 而且在任何地方都不该被修改; DOS不像 Unix/Linux shell那样可以支持常量值, 这挺糟糕的.

Some Final Polish

一个小小的优化: 按2的幂次定义 return code;

SET /A ERROR_HELP_SCREEN=1
SET /A ERROR_FILE_NOT_FOUND=2
SET /A ERROR_FILE_READ_ONLY=4
SET /A ERROR_UNKNOWN=8


这样就可以有更多灵活性: 使用 bitwise OR(按位或)多个 error number, 可以在一个error code中记录多个问题; 这在interactive(交互式)的情况下很少用到, 但在编写脚本时, 如果你缺乏对目标系统的权限, 就会非常有用;

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS

SET /A errno=0
SET /A ERROR_HELP_SCREEN=1
SET /A ERROR_SOMECOMMAND_NOT_FOUND=2
SET /A ERROR_OTHERCOMMAND_FAILED=4

SomeCommand.exe
IF %ERRORLEVEL% NEQ 0 SET /A errno^|=%ERROR_SOMECOMMAND_NOT_FOUND%

OtherCommand.exe
IF %ERRORLEVEL% NEQ 0 (
SET /A errno^|=%ERROR_OTHERCOMMAND_FAILED%
)

EXIT /B %errno%


如果 和 都失败了, return code会是 0x2和 0x4的bitwise combination(按位组合), 或者是数字6; 这个 return code告知我们两个 error都出现了; 更进一步, 可以用相同的 error code反复地调用 bitwise OR, 仍然可以解读到哪些 error出现过.

Part 4 – stdin, stdout, stderr

DOS, 就像 Unix/Linux, 使用三个universal “”files” - keyboard input(键盘输入), printing text on screen(屏幕字符输出), printing errors on screen(屏幕错误输出);

“Standard In(标准输入)”文件–stdin, 包含程序/脚本的输入;

“Standard Out(标准输出)”文件–stdout, 用来将输入写到屏幕显示;

“Standard Err(标准错误)”文件–stderr, 包含显示到屏幕上的错误消息;

File Numbers

对于这三个标准文件, 作为standard stream(标准流), 使用数字 0,1,2来引用; stdin是 file 0, stdout是 file 1, stderr是 file 2;

Redirection

batch文件的一个任务是将程序的输出传到 log文件;
>
操作符send(传送), 或者 redirect(重定向) stdout或 stderr到另一个文件;

e.g. 可以将当前目录结构列表写入文件:

DIR > temp.txt


>
操作符会使用从 DIR命令返回的 stdout覆盖 temp.txt的内容;
>>
操作符是个slight variant(轻量级变量), 它把输出 append到目标文件, 而不是覆盖目标文件.

一个通用的技术是使用
>
来创建/覆盖 log文件, 然后使用
>>
在之后append到 log文件中.

SomeCommand.exe   > temp.txt
OtherCommand.exe >> temp.txt


默认情况下,
>
>>
操作符重定向 stdout. 可以在操作符前面使用文件编号
2
来重定向 stderr:

DIR SomeFile.txt  2>> error.txt


甚至还可以用文件编号和
&
前缀来结合stdout和stderr流:

DIR SomeFile.txt 2>&1


如果想要把 stdout和 stderr都写入同一个文件时这很有用.

DIR SomeFile.txt > output.txt 2>&1


要将文件内容作为一个程序的输入, 代替手动一个个字地从键盘输入, 可以用
<
操作符:

SORT < SomeFile.txt


—TBC—

—YCR—
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: