A Simple Option Parser in Bash

There is a builtin command in bash named getopts that can process command-line options of a shell script. It works pretty well except that it can only handle single-letter options like -a, -b, -c, etc. It cannot handle long option names like --version, --verbose, and --force. For long option names, there is an external command named getopt, however for me, it is one command that is not easy to use.

Single-letter options in a command is fine until you run out of letters or you require more user-friendly usage. For example, if you need both version and verbose options, which one should you assign -v for? If you need options like --force and --file, which one should use -f for? When this happens, you’re left with no choice but to use a different character for the other option. And most of the time, you end up with a character that doesn’t make sense with the option it pertains to.

So, I decided to just implement my own parsing. It can handle both short and long option names. Below is an example.

#!/bin/bash

while true
do
  case $1 in
    --version)  # get version
        VERSION=$2
        shift; shift
        ;;
    -v) # use verbose mode
        VERBOSE=true
        shift
        ;;
    -*)
        echo "Unknown option: $1"
        exit 1
        ;;
    *)
        break
        ;;
  esac
done

echo $VERSION
echo $VERBOSE
echo $*

The bash code above handles the options --version and -v. The idea is simple. All it has to do is loop through all the command line arguments using the while and case commands. For each argument it encounters, it checks if it’s one of the options it is expecting. If the option requires a parameter, which is usually the case for long option names, it gets the value in $2 which is the next parameter since $1 contains the name of the option itself. Before proceeding to the next option, it performs a double shift command in order to set $1 as the next option name. If the option does not require a parameter, it only executes a single shift command. You can also use long option names that does not require a parameter by simply using a single shift command.

Once the script encounters a non-option argument, it breaks out of the while-loop and the rest of the arguments get assigned to $*.

For non-expected options, you search for the -* string (after exhausting all expected options) and then generate an error. If you prefer, you may just ignore it or simply pass it to the rest of the arguments as a non-option.

The code above is short and simple. It’s also easy to read for anyone who wants to know how to run your script since it’s almost self-documenting. You don’t need to write an elaborate usage documentation on what options to use.

One limitation to this parser is that all options must be specified before the non-option arguments. It’s a small price to pay for the ease of use it offers.

Advertisements

Setting up a Raspberry Pi

This little beauty is a lot of fun.

The Raspberry Pi




Image linked from – http://www.raspberrypi.org/faqs

For only $35, you can set up a small Linux server. I’ve documented the steps below on how to set it up. This assumes a MAC OSx environment.

1. Download the Raspbian image.

The download page is located here: http://www.raspberrypi.org/downloads

Locate the latest wheezy-raspbian zip file and run wget to download:

$ wget http://files.velocix.com/c1410/images/raspbian/2013-02-09-wheezy-raspbian/2013-02-09-wheezy-raspbian.zip

$ unzip 2013-02-09-wheezy-raspbian.zip

Archive: 2013-02-09-wheezy-raspbian.zip
inflating: 2013-02-09-wheezy-raspbian.img

2. Load an SD card of at least 2GB in size into your MAC.

Identify the device the sd card is connected to using the diskutil command

$ diskutil list
/dev/disk4
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *4.0 GB disk4
1: DOS_FAT_32 NO NAME 4.0 GB disk4s1

3. Unmount the sd card

$ diskutil unmountDisk /dev/disk4
Unmount of all volumes on disk4 was successful

4. Save the raspbian image into the SD card. Grab a coffee. This takes several minutes. In my case, it took almost 15 minutes.

$ sudo dd bs=1m if=2013-02-09-wheezy-raspbian.img of=/dev/disk4
Password:
1850+0 records in
1850+0 records out
1939865600 bytes transferred in 865.345934 secs (2241723 bytes/sec)

5. Unmount and eject the sd card

$ diskutil list /dev/disk4
/dev/disk4
#: TYPE NAME SIZE IDENTIFIER
0: *4.0 GB disk4

$ diskutil unmountDisk /dev/disk4
Unmount of all volumes on disk4 was successful

$ diskutil eject /dev/disk4
Disk /dev/disk4 ejected

6. Load the sd card into the Raspberry Pi and power it up.

I used a serial connection to the raspberry pi so that I can cut-and-paste the bootup information below.

Debian GNU/Linux 7.0 raspberrypi ttyAMA0

raspberrypi login: pi
Password:
Linux raspberrypi 3.6.11+ #371 PREEMPT Thu Feb 7 16:31:35 GMT 2013 armv6lThe programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

NOTICE: the software on this Raspberry Pi has not been fully configured. Please run ‘sudo raspi-config’

7. Set initial configuration.

pi@raspberrypi:~$ sudo raspi-config

8. Change passwords

# sudo passwd root
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

# sudo passwd pi
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

9. Run update and upgrade

To run the update and upgrade commands below, you will need to have Internet connection. Connect your pi to your ethernet network that has dhcp. With dhcp, the pi is preconfigured to connect automatically and establish network connection.

If you are using the model A Raspberry Pi, it doesn’t have an ethernet connection. You will need to use a wifi usb adapter to obtain Internet connectivity. Follow step #11 below to setup wifi access.

$ sudo apt-get update
$ sudo apt-get upgrade

10. Configure time zone and reboot.

$ sudo dpkg-reconfigure tzdata
$ sudo reboot

11. Configure Wireless LAN

I recommend the Edimax-EW-7811Un wireless USB adapter for the Raspberry pi. This device is plug-and-play on the raspberry pi. You don’t need to download any drivers. The drivers are already available with the image. This device also offers a very simple configuration. I’ve seen configurations out there for other adapters that are too complex. As you’ll see below. you only need to update one file to get this working.

First, check if the raspberry pi has detected the device. The ifconfig command should show the device wlan0.

# ifconfig -a
eth0 Link encap:Ethernet HWaddr b8:27:eb:01:b6:8a
inet addr:10.0.99.19 Bcast:10.0.99.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:130 errors:0 dropped:0 overruns:0 frame:0
TX packets:95 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:12743 (12.4 KiB) TX bytes:14501 (14.1 KiB)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

wlan0 Link encap:Ethernet HWaddr 80:1f:02:9b:f0:3b
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

Update /etc/network/interfaces file

# cat /etc/network/interfaces
auto lo
iface lo inet loopback
iface eth0 inet dhcpauto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp

# WPA type
wpa-ssid “MYSSID”
wpa-psk “secret”

### WEP type
#wireless-essid MYSSID
#wireless-key secret

iface default inet dhcp

Run ifdown/ifup or simply reboot for changes to take effect.

# ifdown wlan0
# ifup wlan0

That’s it. It’s just one file to update to enable wifi access.

Run iwconfig to check the status of your wifi connection.

# iwconfig wlan0
wlan0 IEEE 802.11bgn ESSID:”MYSSID” Nickname:””
Mode:Managed Frequency:2.462 GHz Access Point: 90:E6:BA:D3:C8:68
Bit Rate:150 Mb/s Sensitivity:0/0
Retry:off RTS thr:off Fragment thr:off
Encryption key:****-****-****-****-****-****-****-**** Security mode:open
Power Management:off
Link Quality=100/100 Signal level=73/100 Noise level=0/100
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:0 Invalid misc:0 Missed beacon:0

12. Prevent your wifi adapter from sleeping

Some wifi adapters are set to sleep when there is no activity. You may want to disable this feature if you want your wifi access to be on at all times. This would be useful if you use your pi as a headless server and you only connect to it through its wifi.

Update or create the file below (or equivalent) to prevent your wifi device from sleeping. The filename may be different with your wifi adapter.

# cat /etc/modprobe.d/8192cu.conf
options 8192cu rtw_power_mgnt=0 rtw_enusbss=0 rtw_ips_mode=1