Today we will explore further input/output options in PicoLisp, specifically how to pipe input from the command line to a PicoLisp script. For the example, we will try to read the battery status of our laptop, but of course the principles apply to any kind of command line output.
This article builds up on the Input-Output functions post from the Beginner's Guide.
Getting the battery status (Debian)
In Debian systems we can get the status of your system with the
upower command. First, we need to find the path of your power source by enumerating our system with
$ upower -e /org/freedesktop/UPower/devices/line_power_ACAD /org/freedesktop/UPower/devices/battery_BAT1 /org/freedesktop/UPower/devices/mouse_hidpp_battery_0 /org/freedesktop/UPower/devices/DisplayDevice
$ stands for commands which are executed in the terminal.
We can see a number of battery powered devices. The main one which powers my laptop is
Now we let's check the status of "battery_BAT1" by typing
upower -i <path> which gives a lot of information such as vendor, path, model...:
$ upower -i /org/freedesktop/UPower/devices/battery_BAT1 native-path: BAT1 [...] battery [...] state: discharging warning-level: none energy: 23,1186 Wh energy-empty: 0 Wh energy-full: 49,7364 Wh energy-full-design: 55,2372 Wh energy-rate: 13,3242 W voltage: 15,018 V time to empty: 1,7 hours percentage: 46% capacity: 90,0415% technology: lithium-ion [...]
Let's try to pipe this output to a simple PicoLisp script.
Creating the script
As we learned in the "Input-Output"-functions blog, we have two main options to read in from an input source:
readstops at specific delimitors such as whitespace or new-line,
linereads in the full line.
But what is our input source? Let's check the documentation of the
(in 'any . prg) -> any
Opens any as input channel during the execution of prg. The current input channel will be saved and restored appropriately. If the argument is NIL, standard input is used. If the argument is a symbol, it is used as a file name (opened for reading and writing if the first character is "+"). [...] Otherwise (if it is a list), it is taken as a command with arguments, and a pipe is opened for input. [...]
According to the docs, we have three main options:
- Reading from standard input using
- Reading from file using
- Reading from command with
Let's try the third option and see if we can get the output to PicoLisp. It seems we need to transform the command to a list. What does that mean?
It's much easier than you might expect - it simply means we split up the words of the command like this:
("upower" "-i" "/org/freedesktop/UPower/devices/battery_BAT1").
pil + to open the PicoLisp REPL and try to get the first line:
: (in '("upower" "-i" "/org/freedesktop/UPower/devices/battery_BAT1") (line T)) -> " native-path: BAT1"
Note: the quote
' before the list is needed to escape evaluation.
(line T) returns the list as single string instead of a list of single characters.
We get the first line of the command line printed as expected.
Now let's do the same using piping. In this case, we use input option 1)
NIL to get the input from "the current input channel". We define a script called
battery.l and try:
# <battery.l> (in NIL (prinl (line)) ) (bye)
Now we query the battery status from the command line and pipe it to the PicoLisp script.
$ upower -i /org/freedesktop/UPower/devices/battery_BAT1 | pil battery.l native-path: BAT1
Again, we receive the first line as output.
Formatting the output
upower gives us a lot of information, but probably we will not need all of it. Of course we could pre-format output using tools like grep, but let's try to do it in PicoLisp now.
Some helpful functions:
(from 'any): Skips the current input channel until one of the strings
anyis found, and starts subsequent reading from that point.
(till 'any): Reads from the current input channel till a character contained in
anyis found (or until end of file if any is
skip ['any]: Skips all whitespace (and comments if
anyis given) in the input stream.
As an example, let's say we want to read out the battery status and percentage. As a reminder, the command line output has this format:
state: discharging percentage: 46%
(from "percentage:") to our previous script.
: (in '("upower" "-i" "/org/freedesktop/UPower/devices/battery_BAT1") (from "percentage:") (line T] -> " 70%"
] is a short form which closes all open parentheses.
The output is correct, but we have a lot of whitespace before the output. In order to prettify it a little bit, let's
(skip) the whitespace:
: (in '("upower" "-i" "/org/freedesktop/UPower/devices/battery_BAT1") (from "percentage:") (skip) (line T] -> "70%"
Finalizing the script
We are almost done, but let's make three small improvements:
- We should add a check if the
"percentage:"string exists in our piped output before we try reading it.
- Read the output of
- Add some custom formatting to the output.
A possible solution could look like this:
(in NIL (when (from "state:") (skip) (prinl "Battery status: " (line)) (when (from "percentage:") (skip) (prinl (line) " remaining") ) ) ) (bye)
Calling the script returns:
$ upower -i /org/freedesktop/UPower/devices/battery_BAT1 | pil batt.l Battery status: charging 49% remaining
The final script can be downloaded here.