参考

命令行参考

bws 版本 0.5.0 的每个命令、每个选项和每个退出代码。bws --help 才是权威的,而且更长:这个页面是同一套内容,排版成便于阅读的样子。

命令

共十一个命令。snapshot 是名词,后面要跟一个词。tool 也是名词,单独使用时会列出各个工具。

bws list [--query TEXT] [--signatures] [--memory] [--required-by] [--follow-network] [--json] [--timing]
bws show NAME [--full] [--follow-network] [--json] [--timing]
bws stop|start|restart NAME [--dry-run] [--dependents] [--timeout SECONDS] [--json] [--timing]
bws kill NAME [--force] [--restart] [--dry-run] [--dependents] [--timeout SECONDS] [--json] [--timing]
bws start-type NAME automatic|delayed|manual|disabled [--stop] [--dry-run] [--json] [--timing]
bws snapshot create [FILE] [--note TEXT] [--follow-network] [--force] [--json] [--timing]
bws snapshot diff EARLIER LATER [--exit-code] [--json] [--timing]
bws snapshot diff EARLIER --live [--exit-code] [--json] [--timing]
bws history [NAME] [--json] [--timing]
bws tool
bws tool fix
bws tool fix NAME [--dry-run] [--timeout SECONDS] [--json] [--timing]
bws license [--components]
bws --help
bws --version

拼错的命令会得到一个你可能想用的命令的提示,并以代码 2 结束,而不是去做某件与你的要求相近的事。

$ bws lst
There is no command lst. Did you mean list?

每个命令做什么

list 和 show

list 是整台计算机,由 --query 缩小范围。show 是单个条目及其所有已知信息:签名、特权、安全描述符和内存,每次都会全部读取,因为针对一个条目只需约六十毫秒,而针对整台计算机则需要一秒。真正为空的字段会被省略,除非你传入 --full。无人能读取的字段无论如何都会被打印,因为省略它会看起来像一个答案。

stop、start 和 restart

每个都会生成计划并执行。--dry-run 打印计划而不做任何更改,--dependents 把会受影响的服务作为独立的步骤放进计划,--timeout 指定一个步骤可以在没有进展的情况下持续多久。驱动程序会被拒绝而不是尝试,因为停止内核驱动程序往往不重启就无法恢复。

kill:用于停不下来的服务

它会请求服务停止,只有在无效时才结束它背后的进程,所以自己能停止的条目绝不会被强行结束。结束一个进程会带走该进程中的所有其他服务,不论它们是否事先已经停止,预览会点出它们的名称,并按编号指出该进程。--force 跳过礼貌的那一步,预览于是显示一个步骤而不是两个。--restart 随后把一切带回来。

Windows 对这个想法的表述不同。Stop-Service 上的 --force 的意思是“即使有东西依赖它”,这在这里是 --dependents 的作用。这就是它是一个独立命令、而不是 stop 上的一个选项的原因。

start-type:是一项设置,而不是一次操作

它指明服务管理器在下次启动时如何处理某个条目,没有 --stop 时它不会移动任何东西:正在运行的条目继续运行,已停止的保持停止,计划会这样说明。disabled 值得停下来想一想,因为它会让服务管理器根本不再启动该条目,包括按需为其他需要它的东西启动。delayed 是自动,比其他自动条目稍晚启动:在查询中 start:delayed 询问的就是这个词。Windows 会拒绝把它用于属于加载顺序组的条目,计划会在任何操作发生之前说明这一点。四种类型都会像 sc.exe 那样记录该条目是否延迟启动。--stop 只能与 disabled 搭配,在写入设置之后,在同一份计划中停止该条目。

snapshot create 和 snapshot diff

create 始终会读取签名和哈希,因为快照是要保存下来、之后再比较的,没有它们的快照与有它们的快照比较时,会让人觉得计算机发生了变化。diff 说明从第一个文件到第二个文件,或用 --live 从一个文件到这台计算机,发生了哪些变化。快照页面介绍了格式和理由。

history:谁改的,来自 System 日志

从 System 日志中读取启动类型更改和安装记录,最新的在前,带有 Windows 记录的账户。第一行说明日志能追溯多远、是否被清除过,因为历史记录的长度只取决于日志。带上名称时,显示的是该服务的启动类型更改:安装记录只在不带名称时列出,因为 Windows 记录安装时用的是显示名称,而从不是服务名称。账户在这台计算机能自行解析其名称时显示名称,否则显示其 SID,启动类型则使用 Windows 所写的词。

tool:针对常见问题的现成答案

与窗口在 Tools 选项卡上显示的工具相同。bws tool 列出它们,bws tool fix 列出快速修复:printer、sound、indexing 和 search,每一项都是重启一个 Windows 服务连同依赖它的内容,之所以选它们,是因为 Microsoft 自己的故障排除页面就是这样建议的。bws tool fix printer 就是 bws restart Spooler --dependents:相同的计划、相同的输出和相同的退出代码,而且和这里每个会更改内容的命令一样,它不会先询问,所以想在运行前查看,请使用 --dry-run。

license:你下载的文件里有什么

它什么也不读取,既不读服务管理器,也不读磁盘和网络,而是从编译进可执行文件的登记表中回答。--components 会把通知变成每个组件及其版本、许可证和来源。这与随下载一同发布的物料清单所含的集合相同,因为两者都由同一份登记表生成,这也是值得拥有它的原因:程序是一个自包含的文件,所以它旁边没有任何东西可供扫描程序读取。

示例

下面的计划已针对本页所描述的程序用 --dry-run 检查过。会更改内容的命令不会先询问,所以想在运行前查看,请使用 --dry-run。Spooler 代表任何服务,请换成你自己的服务名称。

查找与读取

bws list                                       这台计算机上的每个服务和驱动程序
bws list --query "start:auto !status:running"  应该在运行却没有运行的内容
bws list --query "status:running" --memory     每个运行中条目的进程正在使用什么
bws show Spooler                               关于一个条目的所有已知信息
bws show Spooler --full                        也包括真正为空的字段

先看,再更改

bws restart Spooler --dry-run                只显示计划,不做别的
bws restart Spooler --dependents             连同依赖它的内容一起重启
bws stop Spooler --dependents --timeout 120  允许每个步骤在没有进展的情况下持续两分钟
bws start-type WSearch delayed --dry-run     比其他自动条目稍晚启动
bws start-type Spooler disabled --stop       在一份计划中禁用并停止它

停不下来的服务

bws kill Spooler --dry-run          进程,以及同在其中的每个服务
bws kill Spooler --restart          只有停止失败时才结束进程,然后把一切带回来
bws kill Spooler --force --dry-run  一步而不是两步:立即结束进程

快照与漂移

bws snapshot create before.json --note "before the patch"  冻结计算机,并在其中保留一条备注
bws snapshot diff before.json after.json                   两个文件:相隔两天,或两台计算机
bws snapshot diff before.json --live                       把文件与这台计算机的当前状态对比
bws snapshot diff before.json --live --exit-code           同上,若配置发生漂移则以 5 结束

谁改的

bws history          System 日志中保存的每一次启动类型更改和安装
bws history Spooler  某一个服务的启动类型更改
bws history --json   同样的内容,作为文档,含日志和更改

快速修复

bws tool                      现有的工具
bws tool fix                  快速修复,以及每一项背后的服务
bws tool fix sound --dry-run  重启两个音频服务的计划
bws tool fix printer          连同依赖它的内容一起重启 Print Spooler

文件里有什么

bws license --components  每个组件、版本、许可证和来源
bws --version             此可执行文件的版本

在 PowerShell 脚本中

# 应该在运行却没有运行的条目,作为对象
bws list --query "start:auto !status:running" --json | ConvertFrom-Json | Select-Object serviceName, displayName, status

# 未签名的文件,写入电子表格
bws list --query "signed:no" --json | ConvertFrom-Json | Select-Object serviceName, binaryPath | Export-Csv unsigned.csv -NoTypeInformation

# 一个服务需要什么
(bws show Spooler --json | ConvertFrom-Json).dependsOn

# 根据退出代码分支的夜间检查
bws snapshot diff C:\baselines\web01.json --live --exit-code > $null
switch ($LASTEXITCODE) {
  0 { 'no configuration drift' }
  5 { 'the configuration drifted' }
  default { "no clean answer, exit code $LASTEXITCODE" }
}

在 cmd 和批处理文件中

rem 仅当第一个命令没有正常结束时才执行第二个命令
bws restart Spooler || echo the restart did not end cleanly

rem 在启用了延迟展开的批处理文件中,! 前面需要插入符号 ^
bws list --query "start:auto ^!status:running"

选项

共 18 个。第三列是每个选项所属的命令:把选项传给用不上它的命令是一个错误,错误中会带有这份列表,而不是一个被悄悄忽略的标志。

选项作用适用范围
--query用查询语言缩小列表范围。list
--signatures读取谁给每个可执行文件签了名,以及 Windows 是否信任它。在整台计算机上需要几秒钟,所以除非被要求,否则是关闭的,而关于签名的查询会自动把它打开。list
--memory读取每个运行中条目的进程正在使用什么。默认关闭,因为它是一次测量而不是一项设置:一秒钟之后它就不同了。list
--required-by读取停止某一个条目后哪些条目会出问题,直接向 Windows 询问,而不是根据各方的声明推算。每个条目需要一次调用,所以除非被要求,否则是关闭的。show 和 snapshot create 每次都会读取它。list
--follow-network让工具完全可以访问这台计算机之外的内容:位于别人共享上的启动路径,以及这台计算机尚未持有的证书。出于安全考虑默认关闭:一个无法访问的共享会耗费二十一秒,并以运行者的身份进行身份验证,而获取证书则是在一次普通列表的过程中连接第三方。没有它时,签名会根据这里已有的内容进行检查,而可能是由于没有去查看而导致的结果,会被报告为未读取,而不是给出判定。list, show, snapshot create
--force对 kill 而言,是不再礼貌地询问而直接结束进程:这样预览显示的是一个步骤,而不是两个。对 snapshot create 而言,是替换已经存在的快照:不是快照的文件绝不会被替换。snapshot create, kill
--restart对 kill 而言,是在进程消失后,把该条目连同与它共用进程的所有内容一起带回来。kill
--stop对 start-type 而言,并且只能与 disabled 搭配,在写入设置之后,在同一份计划中停止该条目。启动类型改变的是下一次启动,而不是现在,所以没有它时,设为 disabled 的运行中条目会继续运行。start-type
--full对 show 而言,是同时打印真正为空的字段。无人能读取的字段无论如何都会被打印。show
--json同一份文档,以机器可读的形式输出到标准输出。list, show, stop, start, restart, start-type, snapshot create, snapshot diff, kill, history, tool fix NAME
--note这份快照是为什么而生成的,保存在文件内部。snapshot create
--exit-code当配置不同时以代码 5 结束:条目被添加或删除,或者某个条目的设置不同。仅有运行状态的变化不计入。默认关闭,这样只想打印差异的脚本,就不会因为发现了差异而被绊住。snapshot diff
--live把文件与这台计算机的当前状态比较,而不是与第二个文件比较。snapshot diff
--timing读取的每个部分花了多长时间,输出到标准错误。list, show, stop, start, restart, start-type, snapshot create, snapshot diff, kill, history, tool fix NAME
--dry-run打印计划,不做任何更改。它与实际运行所执行的是同一份计划:真正执行时没有第二条代码路径。stop, start, restart, start-type, kill, tool fix NAME
--dependents把会出问题的服务作为独立的步骤放进计划。stop, restart, kill
--timeout一个步骤在没有进展的情况下最多可以持续多久,超过后就会被放弃,服务每次报告进展时都会重新计时。如果你不另行指定,则为六十秒。一直在报告进展的服务会被一直观察下去,直到完成为止。超时只是观察结束,而不是失败,报告会说明该条目被留在了什么状态。stop, start, restart, kill, tool fix NAME
--components对 license 而言,是把通知变成这个可执行文件内部的每个组件及其版本、许可证和来源。这与随下载一同发布的物料清单所含的集合相同,因为两者都由同一份登记表生成。license

退出代码

共 6 种结束方式,每一种对脚本来说都是不同的处理。数据输出到标准输出,其余所有内容输出到标准错误,因此 bws list --json | jq 可以正常工作,警告也绝不会混进你的 JSON。

每个 JSON 文档都是纯 ASCII:ASCII 之外的字符会被写成六个字符的转义序列,所有 JSON 读取器都会把它还原成那个字符,所以 bws list --json > file.json 是有效的 UTF-8,而 | ConvertFrom-Json 无论控制台设置为什么代码页都能正确读取。在供人阅读的文本中,会移动终端而不是被打印出来的字符,例如制表符、换行、转义序列和方向覆盖字符,会显示为它的码位 <U+001B>。

-- 表示选项到此结束,所以名称以短横线开头的服务仍然可以指定:bws show -- -odd。工具交给你的命令行是为 PowerShell 编写的:带空格的名称放在双引号中,cmd 对其读取方式相同,而 PowerShell 会在双引号内展开的名称,例如 MSSQL$SQLEXPRESS,则放在单引号中。

代码含义
0完成,一切都到了它该去的地方。
1工具失败了:由于与工具本身而不是与计划有关的原因,它无法完成被要求做的事。
2命令行有误。拼错的命令会得到一个你可能想用的命令的提示。
3计划是好的,也运行了,但其中有些内容没有到达它该去的地方:服务管理器拒绝了,或者会话没有相应的权限。
4有人手动停止了运行。即使每个步骤都已到达,结果也不是零,这样封装脚本就不会把被中断的运行当作正常结束。
5比较已运行,并发现配置发生了漂移:条目被添加或删除,或者某个条目的设置不同。仅有运行状态的变化不算漂移。仅在使用 --exit-code 时才会这样,因为漂移正是这个工具要找的东西,找到它并不是失败。

Ctrl+C 有三个级别,每一级都说明下一级的代价。第一级停止继续前进,并仍会把已取走的东西放回去,第二级让现状保持原样,并仍会打印报告,第三级结束进程。手动停止的运行即使每个步骤都已完成,也会以 4 结束。

关闭控制台窗口算作第一次 Ctrl+C。此后 Windows 会给程序五秒钟,在这段时间内它会停止前进,并把它关掉的东西放回去。注销和关机会在没有任何它能作出反应的警告的情况下结束它,断开的远程会话也可能如此。

在计划任务中

这个工具为之而生的形态:在计算机已知良好时把它冻结,然后每晚询问它是否仍然如此。

# 计算机投入使用的那一天
bws snapshot create C:\baselines\web01.json --note "after the build"

# 每晚,通过计划任务
bws snapshot diff C:\baselines\web01.json --live --exit-code --json > C:\logs\drift.json
#   0 - 没有配置漂移
#   5 - 配置发生了漂移,drift.json 会说明具体内容
#   3 - 已运行但无法读取全部内容,因此答案是部分的

窗口是同一引擎的另一半:BetterWindowsServices-win-x64.zip 中包含它,而下载页面说明了每个压缩包是什么。

窗口的参数

BetterWindowsServices.exe 接受一个供人使用的参数:--catalogue 会打开一张开发者用的表,显示窗口的每个组件在每种状态下的样子,并且不会从你的计算机读取任何内容。第二个参数 --carry 是窗口把它正在显示的内容交给它以管理员身份重新启动的自己的副本的方式。没有人会手动输入它,其中任何该窗口不可能写出的内容都会被拒绝。