Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Getops wildcard expansion

179 views
Skip to first unread message

BillD

unread,
May 14, 2009, 4:14:43 PM5/14/09
to
I want to use getopts and accept a wildcard for a filename on the
command line of a script.

script_name.ksh -f /dir/dir/test*

When the files in the directory has an exact match all is good, it is
working fine. However, if there is more than one file in /dir/dir that
matches, it still works, but that is an error and I want to exit.

What happens is it all works fine except it will only set OPTARG to
the first filename it finds.

So if I have two files in /dir/dir which match the test*, I want to
fail the script.

I have the following:

function main_script
{

while getopts "h:r:f:p:c:s:" optionName; do
case "$optionName" in
h) printHelpAndExit 0;;
f) file_to_check="$OPTARG"
echo "File is set to $OPTARG ";;
p) poll_interval="$OPTARG"
echo "poll_interval is set to $OPTARG ";;
r) number_or_retries="$OPTARG"
echo "Number of tries wating for a file is set to $OPTARG ";;
c) worktoperform="$OPTARG"
echo "Work to perform is set to $OPTARG ";;
[?]) printHelpAndExit ;;
esac
done
}

main_script "$@"

Here is an output form -xv

main_script "$@"

+ main_script -c check_growth -f /dir/dir/tester /dir/dir/tester1 -p
10

and $OPTARG resolves to /dir/dir/tester

I could use some help? I keep thinking this is easy, but when I went
to check $OPTARG it only has on filename, and there for is to late to
test for two.

I would appreciate any suggestions or help.

Thanks

Marco Maggi

unread,
May 14, 2009, 5:16:52 PM5/14/09
to
BillD wrote:
> I want to use getopts and accept a wildcard for a filename
> on the command line of a script.
>
> script_name.ksh -f /dir/dir/test*

I do not think it is possible to do exactly what you want:
each command line switch can have at most one value, so if
the filename expansion of "/dir/dir/test*" produces more
than one pathname, it cannot be handled with a switch.

You have two options:

1) Use non-switch arguments to acquire the result of the
file name expansion. Here you do not use the "-f"
option.

2) Quote the pattern on the command line and expand it
later.

With option number 1 you can use the following script:

# proof.sh --

while getopts "a:b:" SWITCH
do
case "$SWITCH" in
a) echo option -a was "$OPTARG" ;;
b) echo option -b was "$OPTARG" ;;
esac
done

let --OPTIND
shift $OPTIND
declare -a ARGV=("$@")
declare -i i
ARGV=("$@")

echo number of arguments ${#ARGV[@]}
for ((i=0; $i < ${#ARGV[@]}; ++i))
do echo argument at index $i is "${ARGV[$i]}"
done

### end of file

the non-switch arguments end up into the ARGV array. You
can call the script as:

$ proof.sh -a 123 -b 456 alpha beta gamma
$ proof.sh -a 123 -b 456 /bin/d*

With option number 2 you can use the following script:

# proof.sh --

pattern=

while getopts "a:b:f:" SWITCH
do
case "$SWITCH" in
a) echo option -a was "$OPTARG" ;;
b) echo option -b was "$OPTARG" ;;
f) pattern=$OPTARG
esac
done

eval set -- $pattern

ARGV=("$@")
echo number of arguments ${#ARGV[@]}
for ((i=0; $i < ${#ARGV[@]}; ++i))
do echo argument at index $i is "${ARGV[$i]}"
done

### end of file

and invoke it as (notice the quoted pattern):

$ proof.sh -a 123 -b 456 -f '/bin/d*'

HTH
--
Marco Maggi

Bill Marcum

unread,
May 14, 2009, 6:08:25 PM5/14/09
to
On 2009-05-14, BillD <bdor...@gmail.com> wrote:
>
>
> I want to use getopts and accept a wildcard for a filename on the
> command line of a script.
>
> script_name.ksh -f /dir/dir/test*
>
> When the files in the directory has an exact match all is good, it is
> working fine. However, if there is more than one file in /dir/dir that
> matches, it still works, but that is an error and I want to exit.
>
> What happens is it all works fine except it will only set OPTARG to
> the first filename it finds.
>
> So if I have two files in /dir/dir which match the test*, I want to
> fail the script.
>
So, add a test for the value of $# to your script.

BillD

unread,
May 15, 2009, 10:32:32 AM5/15/09
to

Thanks for the reply.

I am unable to get the second solution to work. (ANd I did not try the
first)

The "" around wildcard filename and the removal from OPTARG caused the
variable to expand OPTARG in the case statement properly.

That is,

f) file_to_check="$OPTARG"
echo "File is set to $OPTARG ";;

was returning the single file name ../tester or if quoted the wildcard
itself ../test*

BUT

f) file_to_check=$OPTARG


echo "File is set to $OPTARG ";;

retruns both file names when wildcard is quoted "../../test*"

and i can now test properly

to see if OPTARG has more than on filename.

I am not sure why the syntax doesn't work.

Any extra help is always appreciated.

Also, I am not sure the getops is design right, sometimes it doesn't
fail. When the filename expands to morethan one, any switchs on the
otherside of -f do not work, not parsed. I would think that getopts
would fail. (stupid me)


Thanks.

Marco Maggi

unread,
May 15, 2009, 3:15:01 PM5/15/09
to
"BillD" wrote:
> I am unable to get the second solution to work.

I do not understand what you do not understand. We are
having a misunderstanding. First I have to apologise
because I had not noticed that you are using Ksh; all I have
written works with Bash, however I do not think that Ksh
behaves differently for this script.

Let's put it this way: I have a lot of programs under
"/bin" whose names start with the letter "d", so the
following prints as commented:

------------------------------------------------------------

# File name expansion does not occur here, in the
# assignment; it does not matter if the value is quoted or
# not.
pattern=/bin/d*

# This prints the pattern because of the double-quotes.
echo "$pattern"

# Here the file name expansion happens, so it prints the
# files.
echo $pattern

------------------------------------------------------------

in the script I sent you:

> # proof.sh --
>
> pattern=
>
> while getopts "a:b:f:" SWITCH
> do
> case "$SWITCH" in
> a) echo option -a was "$OPTARG" ;;
> b) echo option -b was "$OPTARG" ;;
> f) pattern=$OPTARG
> esac
> done
>
> eval set -- $pattern
>
> ARGV=("$@")
> echo number of arguments ${#ARGV[@]}
> for ((i=0; $i < ${#ARGV[@]}; ++i))
> do echo argument at index $i is "${ARGV[$i]}"
> done
>
> ### end of file

the variable "pattern" is set to the value of the "-f"
option; if the value is a quoted file name pattern as in:

> $ proof.sh -a 123 -b 456 -f '/bin/d*'

"pattern" is set to "/bin/d*". Then we expand the pattern
by causing file name expansion with:

> eval set -- $pattern

and the files are available in the positional parameter $1,
$2, etc. so we can store them into an array with:

> ARGV=("$@")

once they are in the array we can do what we want with them;
for example we can test how many they are and do whatever is
needed.

In truth the code:

eval set -- $pattern
ARGV=("$@")

is really ugly and wrong (shame on me), it can be replaced
with:

set -- $pattern
ARGV=("$@")

and even better with:

ARGV=($pattern)

Now try to experiment with the following script, in which I
moved the code that expands the pattern in the "-f" branch,
so that maybe it causes less confusion:

# proof.sh --

pattern=

while getopts "a:b:f:" SWITCH
do
case "$SWITCH" in
a) echo option -a was "$OPTARG" ;;
b) echo option -b was "$OPTARG" ;;
f)
pattern=$OPTARG

ARGV=($pattern)

# Let's see what is in ARGV.
echo number of pathnames ${#ARGV[@]}


for ((i=0; $i < ${#ARGV[@]}; ++i))

do echo pathname at index $i is "${ARGV[$i]}"
done
;;
esac
done

### end of file

> Also, I am not sure the getops is design right, sometimes


> it doesn't fail. When the filename expands to morethan
> one, any switchs on the otherside of -f do not work, not
> parsed. I would think that getopts would fail. (stupid me)

The way "getopts" has to signal an error is "complex", you
have to read carefully the documentation. It parses
options, so when it encounters a non-option argument it
returns an error.

HTH
--
Marco Maggi

BillD

unread,
May 18, 2009, 2:16:21 PM5/18/09
to

Marco,

Thanks for the help.

I will try your suggestion. ( work 1st tho)

Here is what I have working:

while getopts "a:b:f:" SWITCH
do
case "$SWITCH" in
a) echo option -a was "$OPTARG" ;;
b) echo option -b was "$OPTARG" ;;
f) pattern=$OPTARG

for i in $OPTARG
do var_count=$((var_count+1))
if [[ $var_count -gt 1 ]]
then
echo "File wildcard resolves to more than one file"
exit 102
fi
done
;;
esac
done

### end of file

Similar..


I am not sure when I will get to work on your suggestion.

But you were a big help.

THANKS

0 new messages