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/