Sunday, December 9, 2012

Writing scripts with copy and paste

Sure everyone who has tried to use expect-lite knows it is easy to use if you are just using > and < in the script. But what if you want to do something a bit more tricky, say capture a value from the screen, and make a comparison.

It is a bit contrived, but say for example, you want to check the number of packets received on the eth0 interface is below 100,000 (100 thousand). In expect-lite, it is quite easy to do the comparison using a dynamic variable, a variable which is assigned based on the output of a command at run time. Sometimes it can be tricky to get the regex right to capture the info you want, but I will keep regex simple, and use copy and paste to solve this puzzle.

Setting up the environment

Using el_shell.elt (in the examples directory) makes it easy to try out expect-lite lines using copy and paste between two windows (an editor and terminal window). The first thing you want to do is setup two windows like this:
two window setup
I prefer overlapping windows, but it is up to your preference. The key is that you start an editor (I use nedit) and a terminal (or xterm) and can see them both at the same time. In this exercise, we will write lines of expect-lite code in the editor, copy them, then paste them into the terminal which is running el_shell.elt. Having overlapping windows (rather than full screen) makes it easier to see what is going on.

Now that you have your editor started, go ahead and start el_shell.elt in the terminal by typing:

el_shell.elt is pretty simple, it starts expect-lite, then does an *INTERACT to drop you into the debugger. It may look like you are just at the bash prompt. To confirm you are in the debugger, press ESC then h (in cygwin, press back-quote then h). You should see the following output:
IDE: Help
  Key          Action
  ----        ------
  <esc>s      Step
  <esc>k      sKip next step
  <esc>c      Continue
  <esc>v      show Vars
  <esc>0to9   Show N next lines of script
  <esc>-1to-9 Show N previous lines of script
  ctrl+D      Quit & Exit expect-lite
  <esc>h      this Help


Using el_shell.elt

Now that you have the environment setup, let's capture the interface RX counter. The ifconfig command will display interface counters (and other useful info). We use a simple regex, any digit,  \d, with the modifier of plus, for one or more (see Demystify Regex with 7 simple terms). This will match a contiguous run of numbers. In the editor type the following lines:
>ifconfig eth0

Select the 2 lines, copy, then switch to the terminal window, and paste the lines, the terminal output should look something like this:
[cvmiller@fedora-arm:~]$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet  netmask  broadcast
        inet6 2001:470:1d:583:246:b3ff:fe05:cbe5  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::246:b3ff:fe05:cbe5  prefixlen 64  scopeid 0x20<link>
        ether 00:46:b3:05:cb:e5  txqueuelen 1000  (Ethernet)
        RX packets 114320  bytes 24060958 (22.9 MiB)
        RX errors 2  dropped 80  overruns 0  frame 1
        TX packets 20022  bytes 4036177 (3.8 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Assigned Var:eth_rx=0

You can see that the value assigned to $eth_rx is 0 (zero). Not quite what we were looking for. Using dynamic variable assignment (aka capture), only the part inside the parenthesis will be assigned. Let's switch back to the editor and refine the dynamic assignment statement a bit more:
>ifconfig eth0
+$eth_rx=packets (\d+)

Copy and paste (into the terminal) again. Now we see at the bottom of the terminal (removing the ifconfig output for brevity):
Assigned Var:eth_rx=

That is looking better. Let's make the script a bit more versatile by using a static variable for the interface, which could later be overridden with a constant (see The Power of Constants). And suppose we want to ensure that we always assign the RX packets, we could tweak the dynamic assignment statement a bit more in the editor:
>ifconfig $iface
+$eth_rx=RX packets (\d+)

Copy and paste (into the terminal) again. With confidence, we see at the bottom of the terminal:
Assigned Var:eth_rx=

Now we have a RX packet value we can compare, so let's update the script in the editor a bit more, and another copy and paste into the terminal:
>ifconfig $iface
+$eth_rx=RX packets (\d+)
?if $eth_rx > $rx_limit? [
    ;red RX packets over limit

The result now looks like: 
Assigned Var:eth_rx=114320

If: expr { "114320" > "100000" } |then   [|result=TRUE

 RX packets over limit

expect-lite directive: *FAIL

Even though the scriptlet failed, el_shell.elt is still running. This is because while in the debugger, one has immunity from failing. After all, it would not be helpful if the debugger quit just because you pasted in some code that failed.

Copy and Paste, a simple technique to create complex solutions

As you can see copying and pasting into the debugger allows you to run arbitrary expect-lite code, and see the results instantly. The debugger is not limited to simple commands, you can paste while loops, directives (like *DEBUG), include files, or other complex code and have it execute right away.

The mantra of expect-lite is keeping it simple. What could be simpler than copy and pasting scriptlets while you automate complex solutions.

PS. to simplify things, I have artificially kept the RX packet counter constant, in real life, it will probably increment
PPS. In Cygwin, you will have use ipconfig rather than ifconfig
PPPS. In Linux/Unix/Mac you need not actually copy and paste, since X has an automatic clipboard, by just selecting the text in the editor, moving the mouse to the terminal and pressing the middle button on the mouse (which will paste the selected text). This makes the overlapping window setup very fast.
PPPPS. the debugger does have a restriction on copy and paste, the first line must not be indented (lines below the first line can be indented).
PPPPPS. el_shell.elt was added in the examples directory in release 4.3.3