bashing ipython

iPython is a wonderful tool that avoids continuosly switching from bash to other utilities like bc, perl & co.

One of its limitation is the I/O redirection – at which bash is really good of. As iPython py-shell profile uses /bin/sh by default – thru os.system, I implemented a quick and dirty system replacement that diverts it into bash.

I added the following line here .ipython/profile_pysh/ipython_config.py

import os
def system2(cmd):
  pid = os.fork()
  if pid == 0:
    args = ['/bin/bash', '-c', cmd ]
    return os.execvp("/bin/bash", args)
  else:
    c_pid, status = os.waitpid(pid, 0)
    return status

print("Overriding os.system with bash")
os.system = system2

# or you can simply use subprocess if available
os.system = lambda cmd: subprocess.call(cmd, shell=True,executable='/bin/bash')

While py-shell profile runs commands for every call outside python globals(), other profiles use the `bang` syntax.
Eg. ! ls -l

To work it out too, I just changed the following line in
/usr/lib/python2.7/dist-packages/IPython/utils/_process_common.py
71 p = subprocess.Popen(cmd, shell=True,
72 executable='/bin/bash',
73 stdin=subprocess.PIPE,

No more shortcut troubles with bash!

As you already know, bash heavily uses the readline library to provide keyboard shortcuts, and this library is configured via the /etc/inputrc.

While playing on a remote machine, I found that the CTRL+{left,right} arrow combination to move between words wasn’t working – and was instead printing “5C” and “5D”. It was necessary to tell bash to associate that combo with left|right move!

The first thing to do is to print the raw characters associated to CTRL+left. To do this type:
CTRL+V and then CTRL+left
You’ll see
^[[1;5D

Ok: that’s the sequence to map – where “^[” corresponds to the escape key aka \e.

Now let’s tell bash to bind that sequence with backward-word, and everything will work as expected!
# bind '"\e[1;5D": backward-word';

Now we can edit the /etc/inputrc , where I added two more association to the existing ones:
# Those two were still existing
"\e[5C": forward-word
"\e[5D": backward-word
# And now two more
"\e[1;5C": forward-word
"\e[1;5D": backward-word

You can even listen all configured bindings with
#bind -p

Command line too long

When a collegue asked me how to get the full cmdline of a process, I simply replied:

#ps -fewww # as many w as lines you need

When he told me it was not enough I was confident to have the right answer:

# cat /proc/$PID/cmdline

I didn’t know  that /proc/cmdline is limited, and the limit is lower than argv length. Specifically it’s the output of

# getconf PAGE_SIZE

4096

Enjoy with this script

# cat > /tmp/fulla.sh << EOF

#!/bin/bash

sleep 10

EOF

# chmod +x /tmp/fulla.sh; /tmp/fulla.sh {0..2000} &

# cat /proc/${!}/cmdline | wc -c

4096

PAGE_SIZE is the kernel defined page size, and to grow it you have to rebuild your kernel… but you may find some drawbacks 😉

To know something more about argv length – as it depends on POSIX and linux kernel version – you can see:

# man 2 execve

An accepted value is usually 32*PAGE_SIZE = 128k, but some parameters like

#ulimit -s  # stack size

# getconf ARG_MAX

may influence the allowed size.

If you’re interested you can look at the exec() source code 😉

Bash: using fifo

I wrote two bash scripts with consumer-producer paradigm on a fifo (named pipe).

This is a simple example:

## producer.sh
...
fifo="/path/to/fifo"

consumer.sh &
while [ ! -p "${fifo}" ]; do
sleep 1
done

while [...]; do
echo "message for logging"
echo "message for fifo" >"${fifo}"
done

...

## consumer.sh
...
fifo="/path/to/fifo"

trap "rm -f ${fifo}" INT EXIT TERM

[ -p "${fifo}" ] || mkfifo "${fifo}"

while read line <"${fifo}"; do
echo ${line}
done

...

These scripts have two problems: (1) for each echo (write) in the producer, bash open the fifo, write on it and close it (but this is not a real problem…); (2)  for each read in the consumer (in the while loop), bash has the same behaviour (open, read, close). This causes a failure on the write in the producer (errno==EPIPE) when the fifo is closed for reading.

Solution: use the standard template open -> read/write (in the while loop) -> close

## producer.sh
...
fifo="/path/to/fifo"

consumer.sh &
while [ ! -p "${fifo}" ]; do
sleep 1
done

exec 10>"${fifo}"

while [...]; do
echo "message for logging"
echo "message for fifo" >&10
done

exec 10>&-
...

## consumer.sh
...
fifo="/path/to/fifo"
trap "rm -f ${fifo}" INT EXIT TERM

[ -p "${fifo}" ] || mkfifo "${fifo}"

exec 10<"${fifo}"

while read line <&10; do
echo ${line}
done

exec 10<&-
...

Min(GW) is Most

I had to monitor some machines from a windows environment. As I love to script anything I decided to play the game with bash…so my battleplan was:

  1. – install a small *nix environment on my Windows vm;
  2. – set a nice, resizable terminal  window (putty sucks);
  3. – write my script.

Since I’m an old experienced Cygwin user, I decided to use a lighter tool. Minimalistic Gnu for Windows was a nice pick. Now I love it! Install it from here http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/

After you set it up – it’s easy – open the mingw terminal window, that still uses the #cmd console window, and you’ll get a nice bash shell!

As #cmd window is really ugly, you can install the MinGW Terminal issuing

# mingw-get install mintty

Then you just have to change the MinGW link on your desktop, telling it to use the mintty terminal”–mintty”

c:\mingw\msys\1.0\msys.bat –mintty

Now you have a working gnu enviroment. You can install new software with #mingw-get and forget putty, using our old beloved ssh