Slide 1

Slide 1 text

Avoid BSDM*: Obey the command. Line, that is. Steven Lembark Workhorse Computing [email protected] *BSD Masochism

Slide 2

Slide 2 text

Following commands Lots of ways to handle the commnd line. Most of them are terrible. getopt(1) in all its varieties is the sanest. Standardize the comand line structure. But it only gets you so far.

Slide 3

Slide 3 text

Following commands This is about BASH. We’ll use: getopt while case associatiave arrays functions Tie it all up nicely, so to speak.

Slide 4

Slide 4 text

Where to find the commands. BASH stores arguments in an array. ‘Options’ have ‘-’ or ‘–’ and may have arg’s. Then there are args. In whatever order they were typed. foo --bletch=blort --bim /my/path -d bam -v1 -x;

Slide 5

Slide 5 text

Where to find the commands. BASH stores arguments in an array. ‘Options’ have ‘-’ or ‘–’ and may have arg’s. Then there are args. In whatever order they were typed. foo --bletch=blort --bim /my/path -d bam -v1 -x;

Slide 6

Slide 6 text

Where to find the commands. BASH stores arguments in an array. ‘Options’ have ‘-’ or ‘–’ and may have arg’s. Then there are args. In whatever order they were typed. foo --bletch=blort --bim /my/path -d bam -v1 -x; foo --bletch=blort --bim /my/path -d bam -v1 -x;

Slide 7

Slide 7 text

Where to find the commands. BASH stores arguments in an array. ‘Options’ have ‘-’ or ‘–’ and may have arg’s. Then there are args. In whatever order they were typed. foo --bletch=blort --bim /my/path -d bam -v1 -x; foo --bletch=blort --bim /my/path -d bam -v1 -x; foo --bletch=blort --bim /my/path -d bam -v1 -x;

Slide 8

Slide 8 text

getopt(3) to the rescue!!! C function. List of possible switches. Switches may have arguments. Switches extracted before returning arguments. Metadata describes valid switches, their args. Processes all of the switches, leaving program args.

Slide 9

Slide 9 text

getopt(3) to the rescue!!! C function. getopt_long(3) allowed for “--foo” and “--foo=bar”. Named switches!!! Every language since has been getopt_long[ish].

Slide 10

Slide 10 text

Where to find the commands. BASH stores arguments in an array... In whatever order they were typed. Two ways to access them: $* and $@. Behave differently when quoted: ”$*” works seprated by $IFS. ”$@” separate words. $* for printing, $@ is for iterating.

Slide 11

Slide 11 text

Where to find the commands. The program and sub’s all use $* and $@. Localized by context. Result: You cannot access program arg’s from a sub. Need to pass them as “$@”.

Slide 12

Slide 12 text

Guts of getopt(1) BASH added a getopt command line utility. It’s arguments define the switches. Two types: Short are dash and letter, may be combined. -v 1 -d bam -x -v1 -dbam x -vdx 1 bam Fun eh?

Slide 13

Slide 13 text

Guts of getopt(1) BASH added a getopt command line utility. It’s arguments define the switches. Two types: Long arguments are double-dash and optional ‘=’ --bletch=blort --blecth blort No stacking, argument always next item.

Slide 14

Slide 14 text

Guts of getopt(1) BASH added a getopt command line utility. In short: USE LONG ARGUMENTS!!! Only place short’s make sense: Binary (on/off) behavior without args. “-d” for debug, “-v” for verbose.

Slide 15

Slide 15 text

BASH getopt(1): First pass. Say you don’t understand arrays or functions. Simplest case: Stuff it all on one line. Options with argumens get a ‘:’. There are ways of typing the arguments also. eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 16

Slide 16 text

BASH getopt(1): First pass. Say you don’t understand arrays or functions. Simplest case: Stuff it all on one line. “$@” gives you separate words. The ‘--’ separates getopt’s args from yours. eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 17

Slide 17 text

BASH getopt(1): First pass. Say you don’t understand arrays or functions. Simplest case: Stuff it all on one line. $( … ) executes a command, returns the result. “set --” re-sets the program arguments. eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 18

Slide 18 text

BASH getopt(1): First pass. Say you don’t understand arrays or functions. Simplest case: Stuff it all on one line. eval post-processes the mess returned by getopt. eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 19

Slide 19 text

BASH getopt(1): First pass. foo --bletch=blort --bim /my/path -d bam -v1 -x; becomes: “--bletch blort --bim -d bam -v1 -x -- /my/path” eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 20

Slide 20 text

BASH getopt(1): First pass. foo --bletch=blort --bim /my/path -d bam -v1 -x; becomes: “--bletch blort --bim -d bam -v1 -x -- /my/path” The -- is a Very Good Thing(tm). Separates options from program arguments. eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 21

Slide 21 text

BASH getopt(1): First pass. foo --bletch=blort --bim /my/path -d bam -v1 -x; becomes: “--bletch blort --bim -d bam -v1 -x -- /my/path” A simple loop iterates options. Terminates on the first ‘--’. eval set -- "$( getopt -o’d:v:x’ -l’--blort:,--bim’ -- "$@" )";

Slide 22

Slide 22 text

BASH getopt(1): Second pass. A bit more readable. At least descriptive... short=’d:v:x’; long=’blort:,bim’; argv="$( getopt -o”$short” -l”$long” -- "$@" )"; eval set -- “$argv”;

Slide 23

Slide 23 text

BASH getopt(1): Second pass. A bit more readable. Places to hang error messages. short=’d:v:x’; long=’--blort:,--bim’; argv="$( getopt -o”$short” -l”$long” -- "$@" )"; [[ “$argv” =~ unrecognized ]] && usage “Bogus options: $argv”; eval set -- “$argv”;

Slide 24

Slide 24 text

BASH getopt(1): Processing argv Now what? Simplest way is iterating “$@”. where and a case. Keep going until ‘--’. eval set -- “$argv”; --bletch blort --bim -d bam -v1 -x -- /my/path

Slide 25

Slide 25 text

BASH getopt(1): Processing argv eval set -- “$argv”; while :; do case $1 # $1, $2 are elements of $@/$*. in --) shift; # discard the -- break; # program args left on $@ ;; --bletch) # this has an argument bletch=$2; shift; ;; ... esac shift; # discard $1. done

Slide 26

Slide 26 text

BASH getopt(1): Processing argv bletch=$2; Catch: This fills your code with global variables. $bletch, $verobse, $debug...

Slide 27

Slide 27 text

BASH getopt(1): Third pass Fix: Push getopt into a subroutine. command-line() { # $@/$@ are always local to the context. typeset -r short=’d:v:x’; typeset -r long=’blort:,bim’; local argv="$( getopt -o”$short” -l”$long” -- "$@" )"; eval set -- “$argv”;

Slide 28

Slide 28 text

BASH getopt(1): Third pass Fix: Push getopt into a subroutine. typeset localizes the variables. command-line() { # $@/$@ are always local to the context. typeset -r short=’d:v:x’; typeset -r long=’blort:,bim’; local argv="$( getopt -o”$short” -l”$long” -- "$@" )"; eval set -- “$argv”;

Slide 29

Slide 29 text

BASH getopt(1): Third pass Fix: Push getopt into a subroutine. typeset localizes the variables. -r marks them as read-only. command-line() { # $@/$@ are always local to the context. typeset -r short=’d:v:x’; typeset -r long=’blort:,bim’; local argv="$( getopt -o”$short” -l”$long” -- "$@" )"; eval set -- “$argv”;

Slide 30

Slide 30 text

BASH getopt(1): Third pass Fix: Push getopt into a subroutine. $*/$@ are always local to their context. command-line() { # $@/$@ are always local to the context. typeset -r short=’d:v:x’; typeset -r long=’blort:,bim’; local argv="$( getopt -o”$short” -l”$long” -- "$@" )"; eval set -- “$argv”;

Slide 31

Slide 31 text

BASH getopt(1): Third pass How do we handle the options? TMTOWTDI!!! #!/bin/bash command-line() { ... }

Slide 32

Slide 32 text

BASH getopt(1): Third pass Pre-declare the results. Simpler processing with lower-case values. #!/bin/bash typeset -l bletch; # pre-declare globals. command-line() { $bletch = $2; # “true”, not True TRue TRUE truE }

Slide 33

Slide 33 text

BASH getopt(1): Third pass How do we handle the options? Pass them back as a list. #!/bin/bash command-line() { ... ( $bletch, $bim, $verbose, $debug ) } typeset -a argv=( $(command-line “$@”) );

Slide 34

Slide 34 text

BASH getopt(1): Third pass How do we handle the options? Downside: You have to remember the order. #!/bin/bash command-line() { ... ( $bletch, $bim, $verbose, $debug ) } typeset -a argv=( $(command-line “$@”) );

Slide 35

Slide 35 text

BASH getopt(1): Third pass How do we handle the options? Better way: Associative array #!/bin/bash command-line() { --blort) $blort=$2; shift; ;; }

Slide 36

Slide 36 text

BASH getopt(1): Third pass How do we handle the options? Better way: Associative array #!/bin/bash command-line() { --blort) ${argv[blort]}=$2; shift; ;; }

Slide 37

Slide 37 text

BASH getopt(1): Third pass How do we handle the options? ${foobar[1]} indexed array ${foobar[X]} associative array (map/hash/dictionary) --blort) ${argv[blort]}=$2; shift; ;; -v) ${argv[verbose]}=1; ;;

Slide 38

Slide 38 text

BASH getopt(1): Third pass How do we handle the options? Simply declare a global $argv. typeset -A argv=(); # predeclare it, look organized! command-line() { ${argv[blort]}=$2; ${argv[verbose]}=1; }

Slide 39

Slide 39 text

BASH getopt(1): Third pass How do we handle the options? Anything that isn’t localized is global. typeset -A argv=(); command-line() { ${argv[blort]}=$2; # assigning to ${argv[X]} creates argv. ${argv[verbose]}=1; } if [[ -n ${argv[verbose]} ]]

Slide 40

Slide 40 text

BASH getopt(1): Third pass Er... What happened to the program args? Pass them back as a list! command-line() { ${argv[blort]}=$2; # assigning to ${argv[X]} creates argv. ${argv[verbose]}=1; ”$@” # hand back what’s left after shift. } # read-only variable cmd_arg has what’s left after the -- typeset -r -a cmd_arg=( $( command-line “$@” ) );

Slide 41

Slide 41 text

BASH getopt(1): Fourth pass Better error messages with -n and a program name. base0 will be used with the getopt error messages. #!/bin/bash typeset -r base0=$( basename $0 ); typeset -r dir0=$( dirname $0 ); local argv="$( getopt -o"$short" -l"$long" -n"$base0" -- "$@" )";

Slide 42

Slide 42 text

Re-usable command line handler for BASH One sub to rule them all. Or at least command them... typeset -r -a cmd_args=$( command-line “$@” ); [[ -n $cmd_args ]] || fatal “Bogus $base0: no arguments”; validate-source-file ${cmd_args[0]}; [[ -n ${argv[verbose]} ]] && echo “Verbosely...”

Slide 43

Slide 43 text

Bedside reading man 1 bash; # arrays, typeset. man 1 getopt; # BASH getopt man 3 getopt; # where it all came from