Command line reference
Every command, every switch and every exit code of bws, version 0.1.0. bws --help is the authoritative one and is longer - this page is the same surface, laid out to be read.
The commands
Eight verbs. snapshot is a noun and takes a word after it.
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|manual|disabled [--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 --help
bws --version
A mistyped verb is offered the one you probably meant, and the run ends with code 2 rather than doing something close to what you asked.
$ bws lst
There is no command lst. There is: list, show, stop, start, restart, start-type, kill, snapshot.
What each verb does
list and show
list is the whole machine, narrowed by --query. show is one entry with everything known about it - the signature, the privileges, the security descriptor and the memory, all of them read every time, because over one entry that costs about sixty milliseconds where over the whole machine it costs a second. Fields that are genuinely empty are left out unless you pass --full. A field nobody could read is printed either way, because leaving one out would look like an answer.
stop, start and restart
Each builds a plan and carries it out. --dry-run prints the plan and changes nothing, --dependents puts the services that would break into it as steps of their own, and --timeout says how long to wait for one step. Drivers are refused rather than attempted, because stopping a kernel driver is often not reversible without a restart.
kill - for a service that will not stop
It asks the service to stop and ends the process behind it only if that does not work, so an entry that stops on its own is never ended. Ending a process takes every other service living in that process with it, whether or not they stopped first, and the preview names them and names the process by number. --force skips the polite step and the preview then shows one step instead of two. --restart brings everything back afterwards.
Windows spells this idea differently. --force on Stop-Service means "even if something depends on it", which is what --dependents does here. That is why this is a verb of its own rather than a switch on stop.
start-type - a setting, not a move
It says what the manager will do with an entry at the next boot and moves nothing: an entry that is running keeps running, one that is stopped stays stopped. disabled is the one worth pausing over, because it stops the manager starting the entry at all, including on demand for something else that needs it.
snapshot create and snapshot diff
create always reads signatures and hashes, because a snapshot is kept and compared later and one without them would compare against one with them as though the machine had changed. diff says what changed going from the first file to the second, or from a file to this machine with --live. The snapshot page has the format and the reasoning.
Switches
16 of them. The third column is the verbs each one belongs to - a switch passed to a verb that has no use for it is an error with that list in it, rather than a flag quietly ignored.
| Switch | What it does | Where it works |
|---|---|---|
--query | Narrow the listing with the query language. | list |
--signatures | Read who signed each binary and whether Windows trusts it. Several seconds over a whole machine, so it is off unless asked - and a query about signatures turns it on by itself. | list |
--memory | Read what each running entry's process is using. Off by default because it is a measurement rather than a setting: it is different a second later. | list |
--required-by | Read which entries break if one is stopped, asked of Windows directly rather than worked out from what everything declares. A call per entry, so off unless asked. show and snapshot create read it every time. | list |
--follow-network | Let the tool look at a launch path that lives on another machine. Off by default for safety: one unreachable share costs twenty one seconds, and the connection authenticates as whoever ran it. | list, show, snapshot create |
--force | On kill, end the process straight away without asking politely - the preview then shows one step instead of two. On snapshot create, write over a file that is already there. | snapshot create, kill |
--restart | On kill, bring the entry back once the process is gone, along with everything that shared it. | kill |
--full | On show, print the fields that are genuinely empty as well. A field nobody could read is printed either way. | show |
--json | The same document, machine readable, on standard output. | list, show, stop, start, restart, start-type, snapshot create, snapshot diff, kill |
--note | What the snapshot was taken for, kept inside the file. | snapshot create |
--exit-code | End with code 5 when anything differs. Off by default, so a script that only wants the differences printed is not tripped by finding some. | snapshot diff |
--live | Compare the file against this machine as it is now, rather than against a second file. | snapshot diff |
--timing | How long each part of the read took, on standard error. | list, show, stop, start, restart, start-type, snapshot create, snapshot diff, kill |
--dry-run | Print the plan and change nothing. It is the same plan an execution runs - there is no second code path for the real thing. | stop, start, restart, start-type, kill |
--dependents | Put the services that would break into the plan as steps of their own. | stop, restart, kill |
--timeout | How long to wait for one step to reach the state it asked for, counted from the moment the manager accepts the request. Sixty seconds unless you say otherwise. Running out is the end of watching rather than a failure, and the report says where the entry was left. | stop, start, restart, kill |
Exit codes
6 endings, and every one of them is a different thing for a script to do. Data goes to standard output and everything else to standard error, so bws list --json | jq works and a warning never lands in your JSON.
| Code | Meaning |
|---|---|
0 | Done, and everything arrived where it was going. |
1 | The tool failed - it could not do what was asked, for a reason that is about the tool rather than about the plan. |
2 | The command line was wrong. A mistyped command is offered the one you probably meant. |
3 | The plan was good, it ran, and something in it did not get where it was going - the manager refused, or the session had no rights to it. |
4 | Somebody stopped the run by hand. Non-zero even when every step still arrived, so a wrapper does not treat an interrupted run as clean. |
5 | A comparison ran and found differences. Only with --exit-code, because drift is what this tool is for finding, and finding it is not a failure. |
Ctrl+C has three levels, each saying what the next one costs. The first stops going forward and still puts back what was taken, the second leaves things as they are and still prints the report, the third ends the process. A run stopped by hand ends with 4 even when every step still arrived.
In a scheduled task
The shape this tool was built for: freeze the machine when it is known good, then ask every night whether it still is.
# the day the machine goes into service
bws snapshot create C:\baselines\web01.json --note "after the build"
# every night, from a scheduled task
bws snapshot diff C:\baselines\web01.json --live --exit-code --json > C:\logs\drift.json
# 0 - nothing differs
# 5 - something differs, and drift.json says what
# 3 - it ran and could not read everything, so the answer is partial
The window is the other half of the same engine: BetterWindowsServices-win-x64.zip holds it, and the download page says what each archive is.
The window's one argument
BetterWindowsServices.exe knows one: --catalogue opens a developer's sheet showing every component of the window in every state, and reads nothing from your machine.