Friday, May 30, 2014

Saving several slices of output for later with Pseudo Arrays

Pseudo Array of bikes
I was recently talking to a group of expect-lite users who wanted to know how to save several slices of the output of a command, and then be able to use those slices later on. A simple while loop and a pseudo array would be a good solution to this problem.

What is a pseudo array you ask? Well it acts like an array, but it isn't an array in the strictest sense. A real array would have the form $var(index), e.g. $myvar(5). 

But expect-lite doesn't support real arrays, the format for pseudo array is $var$index e.g. $myvar$i. In fact, when using pseudo arrays, new variables names are automatically being created, e.g. $myvar1, $myvar2 .. $myvar100, etc.

An example problem is to collect the block devices (like hard drives) in a pseudo array to be further examined later. In this example, I'll use the ls command to display the devices in /dev. The ones which are block devices start with a 'b'. An example output will look like:
$ ls -l /dev
crw------- 1 root wheel 1, 0 May 4 07:14 auditpipe
crw------- 1 root wheel 13, 0 May 4 07:15 autofs
crw------- 1 root wheel 18, 0 May 4 07:15 autofs_control
crw-rw-rw- 1 root wheel 17, 3 May 4 07:15 autofs_nowait
crw------- 1 root wheel 23, 0 May 27 08:11 bpf0
crw------- 1 root wheel 23, 1 May 27 08:11 bpf1
brw-r----- 1 root operator 14, 0 May 4 07:14 disk0
brw-r----- 1 root operator 14, 2 May 4 07:14 disk0s1
brw-r----- 1 root operator 14, 1 May 4 07:14 disk0s2
brw-r----- 1 root operator 14, 3 May 4 07:14 disk0s3
brw-r----- 1 root operator 14, 4 May 4 07:14 disk0s4
brw-r----- 1 root operator 14, 5 May 4 07:14 disk0s5
brw-r----- 1 root operator 14, 6 May 4 07:14 disk0s6
brw-r----- 1 root operator 14, 7 May 4 07:14 disk0s7
crw-rw-rw- 1 root wheel 4, 0 May 4 07:14 ttyp0
crw-rw-rw- 1 root wheel 4, 1 May 4 07:14 ttyp1
crw-rw-rw- 1 root wheel 4, 2 May 4 07:14 ttyp2
crw-rw-rw- 1 root wheel 4, 3 May 4 07:14 ttyp3
brw------- 1 root operator 1, 0 May 4 07:14 vn0
brw------- 1 root operator 1, 1 May 4 07:14 vn1
brw------- 1 root operator 1, 2 May 4 07:14 vn2
brw------- 1 root operator 1, 3 May 4 07:14 vn3
...

For this example, I'll use a while loop to iterate through the output, and increment the pseudo array index variable, capturing the block devices into a pseudo array of dynamic variables. If the dynamic variable can not find a block device, then the variable will be set to a special expect-lite value of __NO_STRING_CAPTURED__. The while loop will continue looping until there are no more block devices.

After capturing the device in pseudo array variable $dev$i, the script will consume the output (see consuming tables for lunch) to remove the top part of the output. That will leave the next block device to be captured near the top of the blob of text output for the next iteration of the while loop. I'll use some of the basic regex such as \n, \d, and \w (see demystify regex with 7 simple terms) to ensure the line begins with 'b' and ends with the desired device name. Only the part in parens, (\w+), is captured into the dynamic variable (see expect-lite variables).

>ls -l /dev
# initialize index variable
$i=0
# initialize the first element in the pseudo array
$dev$i=none
# while loop testing the pseudo array value is captured
[ $dev$i != __NO_STRING_CAPTURED__
    +$i
    # capture the block device
    +$dev$i=\nb.*\d\d:\d\d (\w+)
    # expect the device to consume the output
    ? $dev$i != __NO_STRING_CAPTURED__ ? <$dev$i
]

# set max devices captured
$max=$i

Now that the block devices have been captured into a pseudo array, I'll explore them a bit more using the file command. Using another while loop to iterate through the pseudo array re-using the index.

# initialize index variable
$i=1
# while loop to check the devices in the pseudo array
[ $i < $max
    # show the pseudo array value - the device
    >file $dev$i
    # check that it is a block special device
    <block special
    +$i
]

Of course you can always look at the pseudo array (and all the expect-lite variables) in the IDE (debugger) by typing <esc>v or you can print it out right from the script using the directive *SHOW VARS. Depending on your system block devices, the output would look something like:
$ DEBUG Info: Printing all expect-lite variables
Var:arg0 Value:test_pseudo_array.txt
Var:dev0 Value:none
Var:dev1 Value:vn0
Var:dev2 Value:vn1
Var:dev3 Value:vn2
Var:dev4 Value:vn3
Var:dev5 Value:__NO_STRING_CAPTURED__
Var:i Value:5
Var:max Value:5

So, not only can you eat your output for lunch, but you can save the slices in a pseudo array for a midnight snack. expect-lite, serving up automation for the rest of us.