A command-line tool
Read arguments, give a help text that holds up, and return exit codes other programs can rely on.
A command-line tool is often the easiest to write and the hardest to do well. This recipe shows both sides.
funksjon hjelp() {
skriv("Bruk: hilsen <navn> [--rop]")
skriv("")
skriv(" <navn> navnet som skal hilses på")
skriv(" --rop skriv hilsenen med store bokstaver")
}
funksjon start() -> heltall {
la argumenter = builtin.args()
hvis lengde(argumenter) < 2 {
hjelp()
returner 1
}
la navn = argumenter[1]
hvis navn == "--hjelp" {
hjelp()
returner 0
}
la hilsen = "Hei, " + navn + "!"
la i = 2
mens i < lengde(argumenter) {
hvis argumenter[i] == "--rop" { hilsen = builtin.upper(hilsen) }
i = i + 1
}
skriv(hilsen)
returner 0
}What happens here
The argument list starts at index zero with the program name, as it does in most languages. The actual first argument therefore lies at index one. It is a classic source of off-by-one errors, especially when you later add more arguments.
Notice that the help text lives in its own function. It is called from two places — when the user asks for it, and when they have done something wrong — and should say the same thing both times. It seems like a trifle, but help texts that have drifted out of sync with each other are a sure sign of a tool no one has used in a while.
The exit code is not decoration
This is the part people skip, and the one that matters most.
returner 0 means everything went well. Anything else means an error. It is the only way other programs can know how yours fared.
Notice the difference in the example above: missing arguments give 1, while --hjelp gives 0. That is not accidental. The user who asks for help has done nothing wrong — the command did exactly what they asked.
A tool that always returns zero cannot be used in a pipeline. The error becomes invisible to the script that calls it, and your build happily continues on a result that does not exist. If you have a build script that looks green while something is obviously wrong, this is the first place to look.
Write error messages for whoever reads them
An error message should say three things: what went wrong, why, and what the user can do about it.
Compare these two:
- Error: invalid argument
- Could not find the file konfig.toml. Provide the path with --konfig, or run from the directory where the file lives.
The first forces the user to guess. The second solves the problem. The difference is one minute when you write it, and many minutes for every user who hits it.
Write errors to the error channel
Ordinary output should be pipeable onward to another program. Error messages should not end up in the middle of that data. Separate the two, and the user can send the result onward and still see what went wrong.
Related
- A web service with multiple routesA complete program that responds to several addresses, reads the query string and returns both HTML and plain text.
- Store and retrieve dataCreate a table, write rows and read them back out — with the built-in database functions, and without one query per row.
- Hash a password safelyArgon2id from the standard library — the right tool for passwords, without a single external dependency.