From: Scott Bass on
Hi,

I'm writing a script (ksh88) which is very modular, with a number of
function definitions, then a main section which "glues it all
together". One of these functions is to parse the command line for
arguments.

Here is an excerpt:

#===================================================================
function parse_command_line # process command line arguments
#===================================================================
{
while getopts :o:a:h optchar
do case $optchar in
"o") # specify options for the sas program
options=$OPTARG
;;
"a") # specify delayed start of job
starttime=$OPTARG
;;
"h") # display help screen
show_help "$0 command - help screen:"
exit 0
;;
"?") # illegal option specified
show_help "$OPTARG is not a valid option."
exit 1
;;
":") # Option argument is missing.
show_help "$OPTARG requires an argument."
exit 1
;;
esac
done

# Shift past option arguments
shift $(($OPTIND - 1))

# Remaining arguments are program files
jobs=$*
}

Then later, at the bottom of the script:

####################################################################
# MAIN PROGRAM
####################################################################

# If running attached to a terminal, the script is running in a shell
if [[ $usertty != "not a tty" ]]
then
initialize_variables || exit $?
parse_command_line $* || exit $?
<<<<<<<<<<<<<<<<<<<<<<
check_start_time || ( show_help "Invalid date
specified: $starttime"; exit $? )
build_submit_parameters $jobs || exit $?
submit_sas_program || exit $?
else
# Running detached from a terminal, so this script is called by bsub
runsas $jobs || exit $?
fi

The script can be called with the switches -a, -o, and -h. Arguments
following the switches are programs to execute.

The problem is, some of the options to be passed to the internal
program (sas) also look like command switches. For example, if my
main script is named "foo", here is an example invocation:

foo -o "-noautoexec -nosource -notes" -a "00:00" my_program_file.sas

If I don't quote the command line arguments (as above), the getopts
processing fails.

If I quote the command line arguments (eg. parse_command_line "$*"),
then the list of jobs are not set properly.

Questions:
1) Is there any way I can keep this modular, or do I need to move the
code in parse_command_line into open code (the "main" program)?

2) Is there a way I can shift the arguments in the parse_command_line
function and have it affect the global list of arguments? IOW so I
can substitute $* for $jobs in the above code. I've read that ksh
functions cannot pass arguments by reference, so I assume I can't do
this. Unless I can use typeset to make the $* array of arguments
global, not just the array of arguments passed to the function?

3) Is best practice to use "return <rc>" rather than "exit <rc>" in
functions?

4) Minor: are $* and $@ identical?

Thanks,
Scott
From: Scott Bass on
On Apr 27, 5:07 pm, Scott Bass <sas_l_...(a)yahoo.com.au> wrote:
> Hi,
>
> I'm writing a script (ksh88) which is very modular, with a number of
> function definitions, then a main section which "glues it all
> together".  One of these functions is to parse the command line for
> arguments.
>
> Here is an excerpt:
>
> #===================================================================
> function parse_command_line   # process command line arguments
> #===================================================================
> {
> while getopts :o:a:h optchar
> do case $optchar in
>    "o") # specify options for the sas program
>         options=$OPTARG
>         ;;
>    "a") # specify delayed start of job
>         starttime=$OPTARG
>         ;;
>    "h") # display help screen
>         show_help "$0 command - help screen:"
>         exit 0
>         ;;
>    "?") # illegal option specified
>         show_help "$OPTARG is not a valid option."
>         exit 1
>         ;;
>    ":") # Option argument is missing.
>         show_help "$OPTARG requires an argument."
>         exit 1
>         ;;
>    esac
> done
>
> # Shift past option arguments
> shift $(($OPTIND - 1))
>
> # Remaining arguments are program files
> jobs=$*
>
> }
>

<...>

> Questions:
> 1)  Is there any way I can keep this modular, or do I need to move the
> code in parse_command_line into open code (the "main" program)?

<...>

Sorry I should also add that the script that I'm enhancing/updating
has all the code in "open code" (i.e. no functions) and processes the
example scenario fine. I think the issue is the quoted argument (-o "-
noautoexec etc") "loses" its quoting when passed to the
parse_command_line function.
From: Janis Papanagnou on
Scott Bass schrieb:
> Hi,
>
> I'm writing a script (ksh88) which is very modular, with a number of
> function definitions, then a main section which "glues it all
> together". One of these functions is to parse the command line for
> arguments.
>
> Here is an excerpt:
>
> #===================================================================
> function parse_command_line # process command line arguments
> #===================================================================
> {
> while getopts :o:a:h optchar
> do case $optchar in
> "o") # specify options for the sas program
> options=$OPTARG
> ;;
> "a") # specify delayed start of job
> starttime=$OPTARG
> ;;
> "h") # display help screen
> show_help "$0 command - help screen:"
> exit 0
> ;;
> "?") # illegal option specified
> show_help "$OPTARG is not a valid option."
> exit 1
> ;;
> ":") # Option argument is missing.
> show_help "$OPTARG requires an argument."
> exit 1
> ;;
> esac
> done
>
> # Shift past option arguments
> shift $(($OPTIND - 1))
>
> # Remaining arguments are program files
> jobs=$*
> }
>
> Then later, at the bottom of the script:
>
> ####################################################################
> # MAIN PROGRAM
> ####################################################################
>
> # If running attached to a terminal, the script is running in a shell
> if [[ $usertty != "not a tty" ]]
> then
> initialize_variables || exit $?
> parse_command_line $* || exit $?

You most likely want

parse_command_line "$@" || exit $?


> <<<<<<<<<<<<<<<<<<<<<<
> check_start_time || ( show_help "Invalid date
> specified: $starttime"; exit $? )
> build_submit_parameters $jobs || exit $?
> submit_sas_program || exit $?
> else
> # Running detached from a terminal, so this script is called by bsub
> runsas $jobs || exit $?
> fi
>
> The script can be called with the switches -a, -o, and -h. Arguments
> following the switches are programs to execute.
>
> The problem is, some of the options to be passed to the internal
> program (sas) also look like command switches. For example, if my
> main script is named "foo", here is an example invocation:
>
> foo -o "-noautoexec -nosource -notes" -a "00:00" my_program_file.sas
>
> If I don't quote the command line arguments (as above), the getopts
> processing fails.
>
> If I quote the command line arguments (eg. parse_command_line "$*"),
> then the list of jobs are not set properly.
>
> Questions:
> 1) Is there any way I can keep this modular, or do I need to move the
> code in parse_command_line into open code (the "main" program)?
>
> 2) Is there a way I can shift the arguments in the parse_command_line
> function and have it affect the global list of arguments?

It's not really necessary to do so.

> IOW so I
> can substitute $* for $jobs in the above code.

(Saying that without looking into the details of your program...)
Switch to "$@" and you should be fine.

> I've read that ksh
> functions cannot pass arguments by reference, so I assume I can't do
> this.

Ksh93 supports call by name reference by typeset -n.

> Unless I can use typeset to make the $* array of arguments
> global, not just the array of arguments passed to the function?
>
> 3) Is best practice to use "return <rc>" rather than "exit <rc>" in
> functions?

Depending on the platform. There was an old, I think, AIX 3.5 ksh88
that had a bug with return/exit from functions.

Return will return from a function (or program if it's in the main
code), exit should exit the program.

>
> 4) Minor: are $* and $@ identical?

They are not the same if they are double quoted. In quite all cases
you certainly want "$@", which is different from "$*".

"$@" keeps the argument structure intact.
"$*" makes one huge argument.

Janis

>
> Thanks,
> Scott
From: Scott Bass on
On Apr 27, 5:46 pm, Janis Papanagnou <janis_papanag...(a)hotmail.com>
wrote:
> Scott Bass schrieb:
>
> > Hi,
>
> > I'm writing a script (ksh88) which is very modular, with a number of
> > function definitions, then a main section which "glues it all
> > together".  One of these functions is to parse the command line for
> > arguments.
>
> > Here is an excerpt:
>
> > #===================================================================
> > function parse_command_line   # process command line arguments
> > #===================================================================
> > {
> > while getopts :o:a:h optchar
> > do case $optchar in
> >    "o") # specify options for the sas program
> >         options=$OPTARG
> >         ;;
> >    "a") # specify delayed start of job
> >         starttime=$OPTARG
> >         ;;
> >    "h") # display help screen
> >         show_help "$0 command - help screen:"
> >         exit 0
> >         ;;
> >    "?") # illegal option specified
> >         show_help "$OPTARG is not a valid option."
> >         exit 1
> >         ;;
> >    ":") # Option argument is missing.
> >         show_help "$OPTARG requires an argument."
> >         exit 1
> >         ;;
> >    esac
> > done
>
> > # Shift past option arguments
> > shift $(($OPTIND - 1))
>
> > # Remaining arguments are program files
> > jobs=$*
> > }
>
> > Then later, at the bottom of the script:
>
> > ####################################################################
> > # MAIN PROGRAM
> > ####################################################################
>
> > # If running attached to a terminal, the script is running in a shell
> > if [[ $usertty != "not a tty" ]]
> > then
> >    initialize_variables             || exit $?
> >    parse_command_line  $*           || exit $?
>
> You most likely want
>
>       parse_command_line  "$@"         || exit $?


> > Questions:
> > 1)  Is there any way I can keep this modular, or do I need to move the
> > code in parse_command_line into open code (the "main" program)?
>
> > 2)  Is there a way I can shift the arguments in the parse_command_line
> > function and have it affect the global list of arguments?
>
> It's not really necessary to do so.
>
> > IOW so I
> > can substitute $* for $jobs in the above code.
>
> (Saying that without looking into the details of your program...)
> Switch to "$@" and you should be fine.

<rest of thread deleted>

Thanks Janis, that helped a lot. However, I still need a way to have
the command line parsing code modify the global list of arguments.

A short test script:

#!/bin/ksh

function foo
{
echo 1 $@
shift
shift
shift
echo 2 $@
}

echo 0 $@
foo "$@"
echo 3 $@

Results:

test_script a b c d e f
0 a b c d e f
1 a b c d e f
2 d e f
3 a b c d e f

I need 3 to equal 2. Reason: the outer script parses the command
line, processing arguments and shifting the results. I return the
list of jobs in $jobs. However, I also want the inner script to be
standalone, processing $@ as a list of jobs if called directly.

Thanks,
Scott
From: Janis Papanagnou on
Scott Bass schrieb:
> On Apr 27, 5:46 pm, Janis Papanagnou <janis_papanag...(a)hotmail.com>
> wrote:
>> Scott Bass schrieb:
>>
>>> Hi,
>>> I'm writing a script (ksh88) which is very modular, with a number of
>>> function definitions, then a main section which "glues it all
>>> together". One of these functions is to parse the command line for
>>> arguments.
>>> Here is an excerpt:
>>> #===================================================================
>>> function parse_command_line # process command line arguments
>>> #===================================================================
>>> {
>>> while getopts :o:a:h optchar
>>> do case $optchar in
>>> "o") # specify options for the sas program
>>> options=$OPTARG
>>> ;;
>>> "a") # specify delayed start of job
>>> starttime=$OPTARG
>>> ;;
>>> "h") # display help screen
>>> show_help "$0 command - help screen:"
>>> exit 0
>>> ;;
>>> "?") # illegal option specified
>>> show_help "$OPTARG is not a valid option."
>>> exit 1
>>> ;;
>>> ":") # Option argument is missing.
>>> show_help "$OPTARG requires an argument."
>>> exit 1
>>> ;;
>>> esac
>>> done
>>> # Shift past option arguments
>>> shift $(($OPTIND - 1))
>>> # Remaining arguments are program files
>>> jobs=$*
>>> }
>>> Then later, at the bottom of the script:
>>> ####################################################################
>>> # MAIN PROGRAM
>>> ####################################################################
>>> # If running attached to a terminal, the script is running in a shell
>>> if [[ $usertty != "not a tty" ]]
>>> then
>>> initialize_variables || exit $?
>>> parse_command_line $* || exit $?
>> You most likely want
>>
>> parse_command_line "$@" || exit $?
>
>
>>> Questions:
>>> 1) Is there any way I can keep this modular, or do I need to move the
>>> code in parse_command_line into open code (the "main" program)?
>>> 2) Is there a way I can shift the arguments in the parse_command_line
>>> function and have it affect the global list of arguments?
>> It's not really necessary to do so.
>>
>>> IOW so I
>>> can substitute $* for $jobs in the above code.
>> (Saying that without looking into the details of your program...)
>> Switch to "$@" and you should be fine.
>
> <rest of thread deleted>
>
> Thanks Janis, that helped a lot. However, I still need a way to have
> the command line parsing code modify the global list of arguments.

A call of shift in the function will shift the arguments provided to
the function, not in the global environment.

>
> A short test script:
>
> #!/bin/ksh
>
> function foo
> {
> echo 1 $@
> shift
> shift
> shift
> echo 2 $@
> }
>
> echo 0 $@
> foo "$@"
> echo 3 $@
>
> Results:
>
> test_script a b c d e f
> 0 a b c d e f
> 1 a b c d e f
> 2 d e f
> 3 a b c d e f
>
> I need 3 to equal 2. Reason: the outer script parses the command
> line, processing arguments and shifting the results. I return the
> list of jobs in $jobs. However, I also want the inner script to be
> standalone, processing $@ as a list of jobs if called directly.

As last line in your function foo add print 3 or maybe print $#
and call the function as

shiftvalue=$( foo "$@" )
shift $shiftvalue

or resp.

remainingargs=$( foo "$@" )
shift $(( $# - $remainingargs ))

(Not that this would look any better than a hack.)

Janis

> Thanks,
> Scott
 |  Next  |  Last
Pages: 1 2
Prev: alternative for ctime
Next: multiple cmnds in ssh..