Source code in KshOptionParsing.sh

To see how this is used return to this tutorial index.

#!/bin/ksh -u
# How to process short & long options in the shell.
#
# This works with:
# * the Korn shell:
#	pdksh -- a clone of the Korn shell (ksh)
#	KSH-93 -- the KornShell by David Korn of AT&T Bell Laboratories.
# * bash, not sure which versions

# **** Shell Script Template ****

# SCCS: @(#)	1.6 10/21/21 13:44:39
# Author: Alain D D Williams, addw@phcomp.co.uk, 2009, 2021


PROGNAME=${0##*/}

Verbose=0
Quiet=0

# Print the message to stderr and exit.
function Die {
	echo "$PROGNAME: $*" >&2
	exit 2
}

# Generate a note that will be seen by the user, ie sent to stderr -- even if stdout redirected
# Only if Verbose
function Note {
	(( Verbose > 0 )) && echo "$PROGNAME: $*" >&2
}

# Print a usage message & exit the program.
# If an argument is given print that as an error and exit 1
function Usage {
	(( $# > 0 )) && echo "$PROGNAME: $*" >&2
	sed -e 's/^^//' <<-!
		This program does X Y Z
		Usage: $PROGNAME [-opts] [files ...]
		--na=arg
		^	A long option that needs an argument
		^	Can use space instead of '=': --na arg
		--oa[=arg]
		^	A long option that has an optional argument
		^	Here the '=' cannot be replaced by a space
		-q --quiet
		^	Quiet - say a bit less than usual about what is happening.
		-v	Verbose - say a bit more than usual about what is happening.
		^	Give this more than once for increased verbosity.
		- --	Denotes the end of the options.  Arguments after this will be handled as file names
		^	even if they start with a '-'.
		^	This may either be a '-' or '--'.

		Version: 1.6 10/21/21
		!
	exit $#
}

# Long Options, value: 0 - no argument, 1 - required argument, 2 - optional argument
typeset -A LongOpts=([help]=0 [verb]=0 [na]=1 [oa]=2)
# Single letter options, a ':' after option means it takes an argument. See: man ksh -- look for getopts.
ShortOpts=:d:qvx

# Parse options, recognise --long options
while	(( $# >= OPTIND )) && eval opt=\${$OPTIND} || break
	[[ $opt == - ]] && shift && break
	if [[ $opt == --?* ]]
	then	opt=${opt#--}
		shift

		# Argument to option ?
		[[ $opt == *=* ]] && OPTARG=${opt#*=} && opt=${opt%=$OPTARG} && hasArg=1 || typeset OPTARG= hasArg=0

		# Check if known option and if it has an argument if it must have one:
		if [[ -z ${LongOpts[$opt]:-} ]]
		then	OPTARG=$opt && opt='?'
		else	if (( hasArg == 0 && LongOpts[$opt] == 1 ))
			then	# Required argument. Can grab next arg ?
				if (( $# >= OPTIND ))
				then	OPTARG=$1   && shift
				else	OPTARG=$opt && opt=:
				fi
			fi
			(( hasArg == 1 && LongOpts[$opt] == 0 )) && OPTARG=$opt && opt=::
		fi
		true # for the while

	else	getopts $ShortOpts opt
	fi
do	case "$opt" in
	d|dee)	echo "Dee arg=$OPTARG" ;;
	v|verb)	(( Verbose++ )) ;;
	q|quiet)Quiet=1 ;;
	x|help)	Usage ;;
	na)	echo "Option --na has the argument that it needs: '$OPTARG'" ;;
	oa)	if (( hasArg == 1 ))
		then	echo "Option --oa has optional argument: '$OPTARG'"
		else	echo "Option --oa does not have an optional argument"
		fi ;;
	::)	Usage "Unexpected argument to option '$OPTARG'" ;;
	:)	Usage "Missing argument to option '$OPTARG'" ;;
	\?)	Usage "Unknown option '$OPTARG'" ;;
	*)	Usage "Internal program error, unrecognised argument '$opt'" ;;
	esac
done
shift $(( OPTIND - 1 ))

echo "Verbose=$Verbose"
echo "End opts processing - OPTIND=$OPTIND, #args: $#"
(( $# >= 1 )) && echo "Args are: $@"

# Now process the arguments as files:
for file
do	Note "Processing $file"
	# Do something
	[[ -f $file ]] && ls -l $file || Die "Cannot see file $file"
done

# end

Return to this tutorial index.