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
:
$ 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 battery_BAT1
.
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:
-
read
stops at specific delimitors such as whitespace or new-line, -
line
reads in the full line.
But what is our input source? Let's check the documentation of the in
-function:
(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
in NIL
- Reading from file using
in <filename>
- Reading from command with
in <list>
.
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")
.
Let's type 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 stringsany
is found, and starts subsequent reading from that point. -
(till 'any)
: Reads from the current input channel till a character contained inany
is found (or until end of file if any isNIL
). -skip ['any]
: Skips all whitespace (and comments ifany
is 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%
Let's add (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%"
Looks good!
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
"state"
- 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.
Top comments (0)