Tag Archives: bash

Write nbdkit plugins in shell script(!)

nbdkit is a pluggable NBD server and you can write plugins in C or several other scripting languages. But not shell script – until now. Shell script turns out to be a reasonably nice language for this:

case "$1" in
  get_size)
    stat -L -c '%s' $f ||
      exit 1 ;;
  pread)
    dd iflag=skip_bytes,count_bytes skip=$4 count=$3 if=$f ||
      exit 1 ;;
  pwrite)
    dd oflag=seek_bytes conv=notrunc seek=$4 of=$f ||
      exit 1 ;;

Now that coreutils util-linux provides the fallocate program we can even implement efficient trim and zeroing operations.

Advertisements

Leave a comment

Filed under Uncategorized

Cool new bash-completions of libguestfs tools

Starting in libguestfs ≥ 1.21.23-2, bash tab completions of guestfish, guestmount and virt-* tools have been rewritten and greatly improved.

Note you will need to install the libguestfs-bash-completion package to enable this feature.

You can now tab complete all long options on most tools:

$ virt-df --[tab]
--add             --domain          --human-readable  --uuid
--connect         --format          --inodes          --verbose
--csv             --help            --one-per-guest   --version
$ virt-resize --[tab]
--align-first          --help                 --no-extra-partition
--alignment            --ignore               --ntfsresize-force
--debug                --lvexpand             --output-format
--debug-gc             --lv-expand            --quiet
--delete               --LVexpand             --resize
--dryrun               --LV-expand            --resize-force
--dry-run              --machine-readable     --shrink
--expand               --no-copy-boot-loader  --version
--format               --no-expand-content    

Where appropriate, the -d option will now expand to the list of libvirt domains:

# virt-df -d [tab]
archlinux20121201x64  f19rawhidex32
f18x64                f19rawhidex64

Finally, guestfish commands are expanded on the command line:

$ guestfish add /tmp/disk : run : list-[tab]
list-9p              list-events          list-md-devices
list-devices         list-filesystems     list-partitions
list-disk-labels     list-ldm-partitions  
list-dm-devices      list-ldm-volumes     

To make this less intrusive, so you can really use it daily, I left the default readline expansions enabled. This means that filenames and so on can continue to be used in every position on the command line, and should mean that bash completions won’t try to be cleverer than the user.

Libguestfs bash completions are also demand-loaded now, so that if you’re not using them, they don’t consume any resources in the shell.

Leave a comment

Filed under Uncategorized

Implementing simple bash tab completion for guestfish

I implemented simple tab-completion for guestfish commands from the shell. For example when using the guestfish -i option it expands the current list of libvirt guests:

# guestfish -i <tab>
CentOS5x32          F13Rawhidex64       RHEL6beta1x64
Debian5x64          FreeBSD8x64         VSphere
F10x32              PCBSD8x64           Win2003x32
F12x64              RHEL55x64           Windows7x32
F12x64preview       RHEL620100329n0x64  Windows7x64
# guestfish --ro -i <tab>
CentOS5x32          FreeBSD8x64         Ubuntu910x64
Debian5x64          PCBSD8x64           VSphere
F10x32              RHEL54Betax64       Win2003x32
F12x64              RHEL55x64           Windows7x32
F12x64preview       RHEL620100329n0x64  Windows7x64
F13Rawhidex64       RHEL6beta1x64       

But notice above that it’s context sensitive. With the –ro option, it offers two extra guests. Those are the two guests that happen to be up and running: read-only is the only safe form of access permitted for those.

Furthermore, if I omit the -i option completely, instead of guests I see guestfish commands:

# guestfish add-<tab>
add-cdrom             add-drive-ro          add-drive-with-if
add-drive             add-drive-ro-with-if

I can even tab-complete the command line options:

$ guestfish --<tab>
--add            --listen         --no-sync        --verbose
--cmd-help       --mount          --remote         --version
--file           --new            --ro             
--inspector      --no-dest-paths  --selinux

So how is this done?

We start with an outline bash completion script that basically consists of a function to perform the completion (called _guestfish) and the complete command invocation. Simplified, this looks like:

_guestfish ()
{
  # ...
}

complete -o default -F _guestfish guestfish

To add completion, you can drop this file into /etc/bash_completion.d/ or you can just source it in your .bashrc file.

The complicated part is the completion function, _guestfish. This function gets several bash variables, and must set a variable with the return value. The important ones are:

  • COMP_WORDS — the array of “words” on the current command line
  • COMP_CWORD — the current word containing the cursor (an index into COMP_WORDS)
  • COMPREPLY — this must be set to the array of possible completion words on return

The first part of my _guestfish function examines each word before the current one to work out the context sensitive bit. We’re looking for the -i or –ro option which could change our behavior. The code looks like this:

_guestfish ()
{
    local flag_i=0 flag_ro=0 c=1 word

    while [ $c -lt $COMP_CWORD ]; do
        word="${COMP_WORDS[c]}"
        case "$word" in
            -i|--inspector) flag_i=1 ;;
            -r|--ro) flag_ro=1 ;;
        esac
        c=$((++c))
    done

The second part of our function generates the completions, either by calling virsh list or by calling guestfish -h, to list libvirt guests or guestfish commands respectively:

    local cmds doms

    word="${COMP_WORDS[COMP_CWORD]}"
    case "$word" in
        --*) # code to complete --options omitted ;;
        *)
            if [ "$flag_i" -eq 1 ]; then
                doms=$(_guestfish_virsh_list "$flag_ro")
                COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W "$doms" -- "$word"))
            else
                cmds=$(guestfish -h| head -n -1 | tail -n +2 | awk '{print $1}')
                COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W "$cmds" -- "$word"))
            fi ;;
    esac
}

(_guestfish_virsh_list is an auxiliary function which is just a wrapper around virsh list).

The only slightly awkward part of this is the mysterious line:

COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W "$words" -- "$word"))

What this does is to use the bash compgen command to generate completion matches for “$word”, where “$words” are all possible completions. Then this is appended to the COMPREPLY array.

That’s all I had to do to provide a reasonable first pass at bash command line tab completion for guestfish. You can see the full script here.

1 Comment

Filed under Uncategorized

Tip: ^ and ! in the shell

I’m astounded. So don’t people know about using ^ and ! in the shell?!? Last week I watched an experienced Linux user carefully hit the ↑ cursor key to get a previous line of history, then ←&→ just to make a simple edit!

Here’s my 30 second guide:

!! Repeat the previous command. Example:

$ ls
bin  d  Desktop  rpmbuild  tmp
$ !!
ls
bin  d  Desktop  rpmbuild  tmp
!-2 (etc) Repeat the command 2 previously (so !! = !-1), or for any number previously. This is the most useful I think.
^foo^bar Replace foo with bar in the previous command. eg:

$ ls -l /etx/httpd/conf.d/local.conf
ls: cannot access /etx/httpd/conf.d/local.conf: No
such file or directory
$ ^etx^etc
ls -l /etc/httpd/conf.d/local.conf 
-rw-r--r-- 1 root root 76 2009-07-16 14:59 /etc/httpd/conf.d/local.conf
!foo Run the most recent command that started foo, eg:

$ !ls
ls -l /etc/httpd/conf.d/local.conf 
-rw-r--r-- 1 root root 76 2009-07-16 14:59 /etc/httpd/conf.d/local.conf

Today I discovered that these are called event designators. When I first saw someone using them it was on an Apollo workstation circa 1991, and I pretty quickly picked these up myself.

18 Comments

Filed under Uncategorized