LinuxQuestions.org
Go Job Hunting at the LQ Job Marketplace
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 02-17-2008, 12:25 AM   #1
yitzle
Member
 
Registered: Aug 2006
Posts: 50

Rep: Reputation: 15
bash script - parsing optional parameters


Hi
I've been playing around with bash scripting recently.
If I want to write a script that allows optional parameters, eg:
$ script -f <filename>
or
$ script -a -c -e

What's the easiest way to parse the parameters? Is there a standard/general approach? Or do I shift through them all and throw it at a case statement?

Thanks!
 
Old 02-17-2008, 01:40 AM   #2
jlliagre
Moderator
 
Registered: Feb 2004
Location: Outside Paris
Distribution: Solaris10, Solaris 11, Mint, OL
Posts: 9,502

Rep: Reputation: 357Reputation: 357Reputation: 357Reputation: 357
You can use the POSIX getopts builtin.
 
Old 02-17-2008, 02:00 AM   #3
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,758

Rep: Reputation: 469Reputation: 469Reputation: 469Reputation: 469Reputation: 469
I've written quite a few option parsers similar to what is below. I dislike using getopts(I still can't make sense of the syntax!). So I just do something like this which works quite well for me -even when there are many options to parse. The disadvantage is that it doesn't let you pass options together like 'progname -tbe'. Each one must be separate: 'prgname -t -b -e' This could be worked around, but I've never taken the time to work out how to do it.

Code:
#!/bin/bash
# copyright 2007 Gilbert Ashley <amigo@ibiblio.org>
# minimal implementation similar to the 'cut' command

# show program usage
show_usage() {
echo
echo ${0##/*}" Usage:"
echo "  Cut FIELDS from STRING using separator delimiter SEP"
echo "${0##/} -[fFIELDS|f FIELDS|--fields=] -[dSEP|d SEP] STRING"
echo "Using --output-delimiter= replaces the delimiter in the output."
echo "Fields can be given using one of these forms: single field '2'"
echo "open range '4-', closed range '2-5', or comma-separated list '2,3,4'"
exit
}

# Minimum number of arguments needed by this program
MINARGS=2

# show usage if '-h' or  '--help' is the first argument or no argument is given
case $1 in
	""|"-h"|"--help") show_usage ;;
esac

# get the number of command-line arguments given
ARGC=$#

# check to make sure enough arguments were given or exit
if [[ $ARGC -lt $MINARGS ]] ; then
 echo "Too few arguments given (Minimum:$MINARGS)"
 echo
 show_usage
fi

# self-sorting argument types LongEquals, ShortSingle, ShortSplit, and ShortMulti
# process command-line arguments
for WORD in "$@" ; do
	case $WORD in
		-*)  true ;
			case $WORD in
				--fields=*) [[ $DEBUG ]] && echo "Long FIELD Option using '='"
					# FIELDS=${WORD##*=}
					#FIELDS=${WORD%*=}
					FIELDS=${WORD:9}
					shift ;;
				-f?) [[ $DEBUG ]] && echo "Short single FIELD Option"
					#FIELDS=${WORD##*f}
					FIELDS=${WORD:2:1}
					shift ;;
				-f) [[ $DEBUG ]] && echo "Short split FIELD Option"
					if [[ ${2:0:1} != "-" ]] ; then
					 FIELDS=$2
					 shift 2
					else
					 echo "Missing argument"
					 show_usage
					fi ;;
				-f*) [[ $DEBUG ]] && echo "Short FIELD Option range"
					#FIELDS=${WORD##*f}
					FIELDS=${WORD:2}
					shift ;;
				-d?) [[ $DEBUG ]] && echo "Short single DEL Option"
					#SEP=${WORD##*d}
					SEP=${WORD:2:1}
					shift ;;
				-d) [[ $DEBUG ]] && echo "Short split FIELD Option"
					if [[ ${2:0:1} != "-" ]] ; then
					 SEP=$2
					 shift 2
					else
					 echo "Missing argument"
					 show_usage
					fi ;;
				-d*) [[ $DEBUG ]] && echo "Short-short DEL Option"
					# SEP=${WORD##*d}
					SEP=${WORD:2}
					shift ;;
				--output-delimiter=*) [[ $DEBUG ]] && echo "Long NEW_SEP Option"
					#NEW_SEP=${WORD#*=}
					#NEW_SEP=${WORD%%*=}
					NEW_SEP=${WORD:19}
					shift ;;
				-*) [[ $DEBUG ]] && echo "Unrecognized Short Option"
					echo "Unrecognized argument"
				;;
			esac
		;;
	esac
done

# set the NEW_SEP to SEP unless  --output-delimiter was given
! [[ $NEW_SEP ]] && NEW_SEP=$SEP

### functions #####

########################
function _cat() {
ERROR=0             # error (0=no, 1=yes)
LINE=               # Line read from file

while [ $# -gt 0 ]
do
     if [ ! -r "$1" ]; then
          echo "Cannot find file $1" 1>&2
          ERROR=1
     else
          IFS=
          while read LINE
          do
               echo "$LINE"
          done <"$1"
     fi
     shift
done

exit $ERROR
}

# function _freq counts the number of matches
# of PATTERN in PARSESTRING and returns FREQ
# example usage: _freq $PATTERN $PARSESTRING
function _freq() { FREQ=0
! [[ $PATTERN ]] && PATTERN=$1
! [[ $PARSESTRING ]] && PARSESTRING=$2
while [[ $PARSESTRING != "" ]] ; do
	case $PARSESTRING in
		*$PATTERN*) (( FREQ++ )) ;
			PARSESTRING=${PARSESTRING#*${PATTERN}} ;;
		*) PARSESTRING="" ;;
	esac
done
echo $FREQ
}

# _get_one_field returns the contents of a FIELD in STRING
# fields are determined by a dividing separator SEP 
# STRING, FIELD and SEP must be supplied to this function
function _get_one_field() {
if [[ $FIELD = 1 ]] ; then
	# output the contents to the left of the first SEP
	OUTPUT=${STRING%%${SEP}*}
else
	# Chop off the first match and cheat on the count
	STUB=${STRING#*$SEP}
	COUNT=1
	# skip over any matches before the requested one
	while [[ $COUNT -lt $(( $FIELD - 1 )) ]] ; do
		# ROF Chop off leading FIELD and/or SEP
		STUB=${STUB#*$SEP}
		(( COUNT++ ))
	done
	# LOF Chop off trailing SEP and/or rest of STUB
	OUTPUT=${STUB%%${SEP}*}
fi
echo $OUTPUT
}


# _process_one_line returns the contents of the FIELD
# specified by the current field LIST
function _process_one_line() {
# process the prepared LIST of FIELDS and create LINE_OUTPUT
# read first character of FIELDS variable
HEAD=${LIST:0:1}
# increment the LIST by chopping the first 2 digits
LIST=${LIST:2}

# set the FIELD to the HEAD
FIELD=$HEAD
# read the contents of the START or HEAD FIELD
OUTPUT="$(_get_one_field)"
# start the OUTPUT_STACK with the first OUTPUT
OUTPUT_STACK="$OUTPUT"

# read the *rest* of the LIST of fields
while [[ $LIST != "" ]] ; do
	# readfirst character to get next FIELD number
	FIELD=${LIST:0:1}
	#  the OUTPUT of the current FIELD
	OUTPUT="$(_get_one_field)"
	# add the OUTPUT to OUTPUT_STACK with a NEW_SEP between
	OUTPUT_STACK=$OUTPUT_STACK$NEW_SEP$OUTPUT
	# increment LIST by 2 CHARS
	LIST=${LIST:2}
done

# this the final ouput for each line of input
LINE_OUTPUT=$OUTPUT_STACK
echo $LINE_OUTPUT
}

# the command-line  FIELDS  variable takes one of these  forms:
# single field '2', open range '4-', closed range '2-5', or comma-separated list '2,3,4'
# We examine the FIELDS variable with if's and convert everything to a LIST

# the first character is always the START or HEAD
# if the second character is a '-' then this is a range like: 2-  or  3-5
if [[ ${FIELDS:1:1} = "-" ]] ; then
	# if the third character is null, range goes to end of STRING
	if [[ ${FIELDS:2:1} = "" ]] ; then
		# this is an open START to END range like: 2-
		START=${FIELDS:0:1}
		# _freq counts the number of SEP's in STRING
		FINISH=$(_freq $SEP $STRING)
		# there are always one more TOTAL_FIELDS then SEP's
		FINISH=$(( FINISH + 1 ))
	else
		# this is a closed range like: 3-5
		START=${FIELDS:0:1}
		FINISH=${FIELDS:2:1}
	fi
	# translate the RANGE values to a comma-separated LIST
	LIST="$START,"
	COUNT=$START
	while [[ $COUNT -lt $(( $FINISH - 1 )) ]] ; do
		(( COUNT++ ))
		LIST=$LIST$COUNT","
	done
	LIST=$LIST$FINISH
elif [[ ${FIELDS:1:1} = "," ]] ; then
	# this is already a comma-separated list 
	# TODO? support a list like this:2,3,5-
	LIST=$FIELDS
elif [[ ${FIELDS:1:1} = "" ]] ; then
	# single-character argument given
	LIST=${FIELDS:0:1}
fi
# save the FULL_LIST
FULL_LIST=$LIST



if [[ $# -gt 0 ]] ; then
	
	ERROR=0             # Has there been an error (0=no, 1=yes)
	LINE=               # Line read from file

	while [ $# -gt 0 ]
	do
	if [ ! -r "$1" ]; then
          echo "Cannot find file $1" 1>&2
          ERROR=1
	else
          IFS=
          while read LINE
          do
               #echo "$LINE"
	       STRING="$LINE"
	       _process_one_line
	       # reset the FIELD LIST
		LIST=$FULL_LIST ;
		# then go back and read more input if needed (next LINE)
          done <"$1"
	  shift
	fi
	# reset the FIELD LIST
	LIST=$FULL_LIST ;
	# then go back and read more input if needed (next file)
	done
	shift
	exit $ERROR
else
	while read LINE ; do
	# be sure every line has a ';' ending for this to work
	#cat ./Notes | while read LINE ; do
	#cat ./Notes | while read LINE ; do echo $LINE ; STRING=$LINE ; _process_one_line ; LIST=$FULL_LIST ; done
	STRING=$LINE ;
	_process_one_line ;
	# done with while read?
	# reset the FIELD LIST
	LIST=$FULL_LIST ;
	# then go back and read more input if needed
	done
fi

exit 0
 
Old 02-17-2008, 03:12 AM   #4
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
You can separate them in a string with this:
Code:
sed -r "{:begin;s/-([^-])([^ \t])/-\1 -\2/g;tbegin}"
ta0kira


Edit: This one's better because it leaves -- options intact:
Code:
sed -r "{:begin;s/(^|[^-])-([^-])([^ \t])/\1-\2 -\3/g;tbegin}"

Last edited by ta0kira; 02-17-2008 at 03:15 AM.
 
Old 02-17-2008, 04:16 AM   #5
jlliagre
Moderator
 
Registered: Feb 2004
Location: Outside Paris
Distribution: Solaris10, Solaris 11, Mint, OL
Posts: 9,502

Rep: Reputation: 357Reputation: 357Reputation: 357Reputation: 357
getopts is quite easy to use, especially if you know its C library original model.

Here is its POSIX description with one example that helps understanding how to use it:
http://www.opengroup.org/onlinepubs/...s/getopts.html
 
Old 02-17-2008, 11:16 AM   #6
yitzle
Member
 
Registered: Aug 2006
Posts: 50

Original Poster
Rep: Reputation: 15
getopts sounds right, but, like gnashley, I don't understand how to use it based on that page you linked.
Google...
this [age explains it fairly well: http://aplawrence.com/Unix/getopts.html
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Parsing a File in a Bash Script TGWDNGHN Programming 4 12-02-2005 02:38 PM
Passing parameters to bash script Kamikazee Programming 4 10-01-2005 06:41 AM
Optional parameters in C++ Templates? enemorales Programming 5 04-22-2005 04:40 PM
Passing Parameters to Bash Script mooreted Linux - Software 3 04-05-2004 09:08 PM
bash script, parsing email addresses kepler Programming 6 01-26-2004 06:47 AM


All times are GMT -5. The time now is 04:11 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration