this is obsolete doc -- see http://doc.nethence.com/ instead

KSH development 

 

http://pbraun.nethence.com/doc/shells/ksh.html 

http://pbraun.nethence.com/doc/lang/kshdev.html 

 

Playing with KSH variable names 

Simply a variable inside a variable, 

# foo=bar
# myvar=foo
# eval "echo \$$myvar"
bar
# eval ok=\$$myvar
# echo $ok
bar

alternative, 

# foo=bar
# typeset -n myvar=foo
# echo $myvar
bar

 

Now a variable inside a renamed variable, 

# myvar=oo
# eval "echo \$f$myvar"
bar
# eval ok=\$f$myvar
# echo $ok
bar

 

Best practices (KSH88 compilant) 

- always use "set -e" at the start of a script, so it exits immediately if a command fails (same as -o xtrace). The drawback for this practice is that it will exit even when it fails to define a variable. For example if this variable returns empty, 

emptyvar=`grep ^something /some/file`

it will exit... A workaround is to include the test directly inside a condition to avoid exiting, 

[[ -z `grep ^something /some/file` ]] && ...

- always indented code even for small pieces. So you can always rearrange the parts when the it grows 

- KSH's "[[" style permits to ommit quotes on string conditions (quite comfortable !) 

- use print instead of echo, it's lighter and compilant with all KSH versions. Note about tailing newline omission. Instead of "echo -n" there's, 

print "text...\c"

- check for depedencies at program's startup, 

[[ -x `whence sed` ]] || die "sed command not found"

note. "whence" is built in KSH, <<hence>> faster than 'which' ! 

note. stdout isn't printed to the terminal, but into the statement itself 

note. if using 'which' against a non existant command, stderr may be printed to the terminal so, 

#[[ -x `which sed 2>/dev/null` ]] || die "sed command not found"

- define if not already defined, 

USER=${USER:-`logname`}
HOSTNAME=${HOSTNAME:-`hostname`}

- no need to unset subshell variables e.g. for 'read line' 

- use "=" for string conditions (although KSH93 understands "==" for that too) 

 

Arthmetical conditions and statements 

- don't prefix variables with "$" in arthmetical conditions and statements 

- use "=" to define a variable inside the arithmetic statement, 

(( x = x + y ))

- use "==" for arithmetical conditions, 

(( x == y )) && ...

- Increment a counter, 

(( x += 1 ))

note. "-=" works too ! 

note. "(( x++ ))" works with KSH93 only. 

 

 

Usage function 

Strip the path from $0, 

fusage() {
  cat <<EOF9

 

  usage: ${0##*/} ...

 

EOF9
}

 

 

Tips 

Print a variable's variable, 

var1=ok 

var2=var1 

eval echo \$$var2 

 

 

Getopts 

Use getopts to parse command line arguments, 

while getopts ab: opts; do
  case $opts in \
  a) print "-a ok";;
  b) print "-b $OPTARG";;
  esac
done

Note. no need to 'shift' 

Note. the backslash after 'case ... in' is KSH88 compilant 

Note. quoting the manual, 

OPTARG  The value of the last option argument processed by the getopts special command.
OPTIND  The index of the last option argument processed by the getopts special command.

Refs : 

- "man getopts" 

- "man getopt" on HP/UX : set -- `getopt abo: $*` 

 

 

Fancy titles 

Automated underline, 

ftitle() {
        titlef=$1

 

        print "$titlef"
        tmp=`echo $titlef | wc -c`
        i=1
        until (( $i == $tmp )); do
                print '=\c'
                (( i += 1 ))
        done
        print ''

 

        unset tmp i titlef
}

 

 

Select menu 

KSH 'select' for built-in menus ! 

PS3="Choice ? "
select choice in "line1 blabla" "line2 blabla"; do
    case $choice in \
    "line1 blabla") echo 1; return;;
    "line2 blabla") echo 2; return;;
    esac
done

Note. the backslash after 'case ... in' is KSH88 compilant 

 

Even simplier, KSH select's REPLY variable, 

PS3="Choice ? "
select choice in "line1 blabla" "line2 blabla"; do
    case $REPLY in \
    1) echo 1; return;;
    2) echo 2; return;;
    esac
  done

 

Now the tricky one... Use arrays instead of the case condition, 

fmenueval() {
        menurows=`fmenurows`

 

        cat <<EOF9
        select choice in \\
$menurows
        ; do
                \${acts[REPLY-1]}
                fenter
                break
        done
EOF9
        unset menurows
}

 

 

fmenurows() {
        i=0
        tmp=$(( ${#rows[*]} -1 ))
        until (( $i > $tmp )); do
                echo "\"${rows[$i]}\" \\"
                (( i += 1 ))
        done
        unset i tmp
}

 

fenter() {
        cat <<EOF9

 

                          [ Press Enter to continue ]
EOF9
        read
}

 

ftitle() {
        titlef=$1

 

        print "$titlef"
        tmp=`echo $titlef | wc -c`
        i=1
        until (( $i == $tmp )); do
                print '=\c'
                (( i += 1 ))
        done
        print ''

 

        unset tmp i titlef
}

 

fmain() {
       while true; do
               clear
               ftitle "Main title"

 

               set -A rows \
                "menu1 lala" \
                "menu2 lili" \
                "Back" \
                "Quit"

 

               set -A acts \
                "print lala" \
                "print lili" \
                "return 0" \
                "exit 0"

 

               menu=`fmenueval`
               PS3="Choice ? "
               eval "$menu"
               unset PS3 acts rows menu
       done
}

 

 

Miscellaneous 

Keep trace of subscripts execution, 

subscript 2>&1 | tee -a logfile

 

The random generator (0 - 32767), 

echo $((RANDOM))

and to range from 0 to 4, 

echo $((RANDOM%5))

hence the Korn Shell roulette, 

#((RANDOM%6)) || rm -rf ~/
((RANDOM%6)) || print "BANG !"

 

For a white prompt background, 

PS1=$'\e[7m$(hostname -s)\e[0m '

Colors, 

g=$(printf '\33[1m\33[32m')  # similar to g=$(tput bold; tput setaf 2)
n=$(printf '\33[m')    # similar to n=$(tput sgr0)
${g}Green Light$n

Ref. http://home.nyc.rr.com/computertaijutsu/ksh.html 

 

 

 

Conditional statements 

Deal with numbers, 

(( _num1_ == _num2_ ))           numbers equal
(( _num1_ != _num2_ ))           numbers not equal
(( _num1_ < _num2_ ))            num1 < num2
(( _num1_ > _num2_ ))            num1 > num2
(( _num1_ <= _num2_ ))           num1 <= num2
(( _num1_ >= _num2_ ))           num1 >= num2

 

Deal with strings, 

[[ _str1_ == _str2_ ]]           strings equal
[[ _str1_ != _str2_ ]]           strings not equal
[[ _str1_ < _str2_ ]]            str1 precedes str2
[[ _str1_ > _str2_ ]]            str1 follow str2
[[ _str1_ = _pattern_ ]]         str1 = pattern
[[ _str1_ != _pattern_ ]]        str1 != pattern
[[ -z _str_ ]]                   str is null
[[ -n _str_ ]]                   str is not null

 

OR & AND statements, 

[[ x=y || k=j ]]                   or in expression
[[ x=y && k=j ]]                   and in expression

 

 

Test objects 

object exist                    -a
readable                        -r
writable                        -w
executable                      -x
non-zero length                 -s
zero length

 

directory                       -d
plain file                      -f
symbolic link                   -h
named pipe                      -p
block special file              -b
character special file          -c
soft link                       -L
socket                          -S

 

owned by me                     -O
owned by my group              not

 

"sticky" bit set                -k
set-group-ID bit set            -g
set-user-id bit set             -u

 

opened on a terminal           not

 

 

Built-in commands 

"if-then"               if _expr_ then
                            _cmd(s)_
                        elif _expr_
                            _cmd(s)_
                        else
                            _cmd(s)_
                        fi

 

"case"                  case _word_ in \
                            _pattern1_)   _cmd(s)_
                            _pattern2_)   _cmd(s)_
                            *)            break ;;
                        esac

Note the backslash after "case _word_ in" (for KSH88) 

 

"while"                 while _expr_ do
                            _cmd(s)_
                        done

 

"for"                   for _variable_ in _list_
                            _cmd(s)_
                        done

 

"select"                   select _variable_ in _list_
                            _cmd(s)_
                        done

 

"until"                 until _expr_ do
                            _cmd(s)_
                        done

 

 

References 

KSH - Korn Shell Tutorial : http://b62.tripod.com/doc/docksh.htm 

O'Reilly, Learning the Korn Shell - 6.3 Arrays : http://docstore.mik.ua/orelly/unix/ksh/ch06_03.htm 

Ksh Scripting : http://www.well.ox.ac.uk/~johnb/comp/unix/ksh.html 

Korn shell scripts : http://www.bo.infn.it/alice/alice-doc/mll-doc/impgde/node20.html 

Resources : http://www.shelldorado.com/links/ 

Guide : http://www.dartmouth.edu/~rc/classes/ksh/ 

Le shell KSH : http://bp.noos.org/computer/ksh.html 

The New KornShell—ksh93 : http://www.linuxjournal.com/article/1273 

Nanoblogger : http://nanoblogger.sourceforge.net/ 

Variable Manipulations : http://www.well.ox.ac.uk/~johnb/comp/unix/ksh.html#varmanipulations 

 

 

Manuals 

KSH88 Manual : http://www.cs.princeton.edu/~jlk/kornshell/doc/man88.html 

KSH93 Manual : http://www.cs.princeton.edu/~jlk/kornshell/doc/man93.html 

PDKSH Manual : http://www.cs.mun.ca/~michael/pdksh/pdksh-man.html 

 

 

KSH curses menus 

Those references should work with KSH93 (donno about KSH88). 

IBM Shell Curses function library : http://www.ibm.com/developerworks/aix/library/au-shellcurses/ 

Mt Xia Functions : http://www.mtxia.com/css/Downloads/Scripts/Korn/Functions/ 

Mt Xia Shell Curses Function Library : http://www.mtxia.com/css/Downloads/Scripts/Korn/Functions/shcurses/ 

Mt Xia French Menus Korn Shell Function Library : http://www.mtxia.com/css/Downloads/Scripts/Korn/Functions/frenchMenus/