For more half-baked ideas, see the ideas tag.
It’s common, perhaps increasingly common, that one program needs to consume the output of another. This is the Unix philosophy — small, single purpose programs assembled together to carry out a more complex task.
However it’s not necessarily superior to alternative ways of composing programs, like COM or D-Bus.
There are two particular problems: 1. How do you find out if a particular feature is supported by the program? 2. How do you parse the output of the program (eg. to find progress bars or error messages)?
As a concrete example, let’s consider a program I wrote called virt-resize. 1. How do I find out if the version of virt-resize I have supports btrfs? 2. If I want to drive virt-resize from a graphical program, how can I parse the text progress bars that virt-resize prints?
For question 1, typical Unix programs take several approaches:
- Ignore the problem completely. Just blindly use the feature, and fail in unpredictable ways if it doesn’t work. This is probably the most popular “strategy”. People who write shell scripts tend to do this all the time. Shell scripts are often either unportable, or end up looking like “configure” because they try to use a very conservative subset of POSIX.
- Run the program, if it fails, run the program a bit differently (and if it fails, a bit differently again, …).
- Attempt to parse the output of
program --help. This depends on help output being stable, when maybe it isn’t, so you end up chasing the upstream project.
- Parse
program --version and work out if the feature was supported in that particular version. This is not very scalable, and doesn’t work with backports.
Question 2, how to get errors and progress bars, is usually too hard, unless the program offers a special “machine readable” flag (notable examples: rpm, parted).
Here’s my half-baked idea: We should standardize the way that program capabilities, help, progress bars, and error output is done.
An additional option is added to programs that support this:
$ program --machine-readable [...]
Programs that don’t support this, and programs that didn’t support it in earlier versions, ought to give an error if this option is not available.
Firstly, the caller just runs the program with this option on its own, and no other options:
$ program --machine-readable
resize-btrfs
resize-ext3
lv-expand
progress-bars
The program just prints out a list of capabilities, one per line, but with no defined format (that is a contract between the program and the caller). The program then exits with status 0. Using this option should not cause the program to perform any other action.
Secondly, if this was successful, the caller can use this option in combination with other options to produce machine-readable output. At least one other option or command line argument is required for this to work.
I would like to suggest the following standards for version numbers, progress bars and error messages.
Machine-readable version numbers are sent to stdout and have the form “program-name version”, where “program-name” should be one word. This is no different from how most GNU programs work:
$ program --version
program 0.1.2
Machine-readable progress bars are sent to stdout and have the form (example) “[10/100]“:
$ program --machine-readable foo
[0/100]
[1/100]
[2/100]
etc.
Error messages are anything sent to stderr when the status code of the program is non-zero. This is, of course, no change from standard Unix.
$ program --machine-readable foo
foo: File not found