Command line options: using an [option: parameter] without a parameter passed to it

S

soren625

I'm using Getopt::Std, and I made a little test script to confirm that
when an option is supposed to have a parameter and is passed without
the parameter, it is completely ignored (i.e. not even pushed to the
options hash).

This is what I want:

prompt> rand.pl -f

runs script.pl and saves the output to a file at the *default* path

<OR>

prompt> rand.pl -f /path/to/file

runs the script and saves the output to the *specified* path.



This is what I have (I'm including the whole script because I'd also
like critique on style/efficiency/etc.):


#!/usr/bin/perl -w

# rand.pl is a random string generator. I created it specifically for
use with the KeePass password management
# application. When generating passwords with KeePass, the user is
asked to type random keyboard input, "...the
# more the better. The different, the better. The random the better"
[sic]. Since a large random string
# was needed immediately, I wanted a one-off commandline solution that
would stick the generated string
# in the clipboard for pasting into KeePass. KeePass allows a maximum
of 30,000 characters in the text box
# so that's the default output. I thought rand.pl could be used for
other applications, so I added the ability to
# specify the string length (indeed, rand.pl itself could be used as a
simple password generator) and the option
# to write the string to a file.
#
# To Do:
# -- when the -f option is used alone, the default path should be used,
# optionally, -f [filename] may be used to specify save location. This
will eliminate the need for further
# prompting after the initial command is given
# -- add option to select character classes to include or exclude
(alpha caps, numbers, brackets, whitespace, etc.)
# -- add option to include or exclude specific characters
#




use Env; # This module allows us to use system environment variables
use Getopt::Std; # This module allows us to grab commandline options
use Win32::Clipboard; # This module allows us to manipulate the Windows
clipboard
use strict;



my %opt;
my $random_string;
my $homepath = $ENV{HOME};
my $chars = 30000; # We initialize this one with a default value
(in case there are no commandline options)
my $file;
my @charlist = (0 .. 9, 'A' .. 'Z', 'a' .. 'z', '!', '@', '#', '$',
'%', '^',
'&', '*', '(', ')', '-', '_', '=', '+', '\\', '|', '[', ']',
# The list of characters used
'{', '}', '`', '~', ';', ':', '\'', '"', ',', '<', '.', '>',
# to build the random string

my $opt_string = 'l:fch';


getopts( "$opt_string", \%opt );



if($opt{h}){
&print_help;
die("\n");
}


if($opt{l}){
$chars = $opt{l}
}


if($opt{c}){
&generate;
&clip_out;
die("\n\n*** Random string of $chars characters generated and placed
in the clipboard. ***\n");
}


if($opt{f}){
&generate;
&file_out;
die("\n\n*** Random string of $chars characters generated and written
to $file. ***\n");
}


else{
&generate;
&clip_out;
die("\n\n*** Random string of $chars characters generated and placed
in the clipboard. ***\n");
}





#
# *** BEGIN SUBROUTINES ***
#



#
# Generate the random string with length of the variable $chars
#

sub generate{
$random_string = join "" => map $charlist[rand @charlist] => 1 ..
$chars;
return;
}




#
# Use the Win32::Clipboard module to dump the generated string to the
clipboard.
#

sub clip_out{
Win32::Clipboard::Set($random_string);
return;
}



#
# Output to a file
#

sub file_out{
print("Type the path and name of the file\n");
print("(Use UNIX-style forward slashes and DOS-style\n");
print("8 character names -- example: c:/docume~1/dir/file.txt\n");
print("Default is \"random.txt\" saved on your Windows desktop) >>");
$file = <STDIN>;
chomp $file;
if($file eq ""){
$file = "$homepath\\desktop\\random.txt";
}
open(OUTFILE, "> $file") or die "Could not access output file $file:
$!\n";
print(OUTFILE $random_string);
close(OUTFILE);
return;
}





#
# Print the help statement
#

sub print_help{
print <<"END";

*********************************************************************

Help for the Random String Generator

Usage: perl rand.pl -l [length] -f
perl rand.pl -l [length] -c

rand.pl is a Perl script that generates a random string
and writes it to a file or places it on the Windows clipboard
to be pasted later. If no command line options are specified,
a 30,000 character string is placed in the clipboard.

Command line options:
-h Get help using randgen.pl (this file)
-l [length] Set the length of the generated string
(default is 30000)
-c Place the generated string in the Windows clipboard
-f Write the generated string to a file (you'll be
prompted for a path -- default is
c:/docume~1/\%HOME%/desktop/random.txt (your home
directory))

*********************************************************************
END

return;
}






As you can see, if the -f option is passed, I am prompting for save
path in the file_out subroutine. I'd like to do away with that and only
have to run one command and not have to interact with the program
anymore.

So, if I have told perl that f will have a parameter (by adding the
colon in the getopts function), is it true that the -f option gets
completely ignored if there is no parameter passed with it from the
commandline?

How can I check to see if the -f option was passed without a parameter
then?

Thanks for your help.
 
B

Brian McCauley

soren625 said:
I'm using Getopt::Std, and I made a little test script to confirm that
when an option is supposed to have a parameter and is passed without
the parameter, it is completely ignored (i.e. not even pushed to the
options hash).

That is correct. Getopt::Long on the other hand has the concept of
switches with optional arguments.
 
A

Anno Siegel

soren625 said:
I'm using Getopt::Std, and I made a little test script to confirm that
when an option is supposed to have a parameter and is passed without
the parameter, it is completely ignored (i.e. not even pushed to the
options hash).

This is what I want:

prompt> rand.pl -f

runs script.pl and saves the output to a file at the *default* path

<OR>

prompt> rand.pl -f /path/to/file

runs the script and saves the output to the *specified* path.



This is what I have (I'm including the whole script because I'd also
like critique on style/efficiency/etc.):

At the same time you make it hard to even find the pertinent code among
almost 200 lines. Bad move.

As for efficiency, parameter evaluation is no place to worry about that,
and program development in general is not the time to worry about it.

As for style, there's a lot wrong with the program but I'll restrict myself
to the concrete question.

[code snipped]
As you can see, if the -f option is passed, I am prompting for save
path in the file_out subroutine. I'd like to do away with that and only
have to run one command and not have to interact with the program
anymore.

So your program doesn't even make an attempt at behaving the way you
describe. Why did you post it?
So, if I have told perl that f will have a parameter (by adding the
colon in the getopts function), is it true that the -f option gets
completely ignored if there is no parameter passed with it from the
commandline?

What makes you think Getopt::Std behaves that way? This is pure phantasy.
Read the documentation.
How can I check to see if the -f option was passed without a parameter
then?

You couldn't if the behavior was as you assume, but it isn't. Here is
how to model the behavior you want:

use Getopt::Std;

getopts( 'f:', \ my %opt);
if ( exists $opt{ f} ) {
my $file = defined $opt{ f} ? $opt{ f} : 'default/file';
print "output to file '$file'\n";
} else {
print "output to clipboard\n";
}


Anno
 
A

Anno Siegel

Brian McCauley said:
That is correct. Getopt::Long on the other hand has the concept of
switches with optional arguments.

In the form

getopts( 'f:', \ %opt);

specifying -f without a value causes $opt{ f} to exist with an undefined
value. That is distinguishable from not giving -f at all.

Anno
 
P

PB

Brian said:
That is correct. Getopt::Long on the other hand has the concept of
switches with optional arguments.

Thank you, that was exactly the direction I was looking for -- I knew
Getopt::Long was out there but I didn't even think to see if it had
this functionality.

Thanks again for the concise, helpful answer!
 
P

PB

Anno said:
As for efficiency, parameter evaluation is no place to worry about that,
and program development in general is not the time to worry about it.
[.....]

As for style, there's a lot wrong with the program but I'll restrict myself
to the concrete question.

I was asking for a more general, high level critique of the overall
structure of the script -- I'm aware that I haven't really honed my
"style" much as a programmer, so I encourage any comments anyone might
have (Go ahead -- I have a thick skin).
So your program doesn't even make an attempt at behaving the way you
describe. Why did you post it?

I'm not sure what you mean. The reason it doesn't behave the way I
*want* it to is the reason I am asking the question in the first place.

I posted the code for two reasons: the first is outlined above in this
reply, and the second is to give context to the explanation of my
problem and my question. It's a very simple program and 200 lines of
code is not that difficult to digest.
What makes you think Getopt::Std behaves that way? This is pure phantasy.
Read the documentation.

I wasn't really phantasizing -- I never demanded that anything *should*
work a certain way. I was explicitly asking *if* it was possible to do
what I wanted. (Now I know that I need to look to Getopt::Long for the
desired functionality).
You couldn't if the behavior was as you assume, but it isn't. Here is
how to model the behavior you want:

[code snipped]

Again, I did not make assumptions, nor did I intend to convey that I
was doing so -- I merely meant to sort of confirm something that I
suspected after playing around with this for a while.

Thanks for the code sample; I will dissect and see what I can glean
from it.
 
K

Kevin Collins

In the form

getopts( 'f:', \ %opt);

specifying -f without a value causes $opt{ f} to exist with an undefined
value. That is distinguishable from not giving -f at all.

But if you test the results from getopts (which I always use to trigger a usage
message), it returns an error! Additionally, what if you decided to add another
option? You would have to make sure that the '-f' option was specified last on
the command line.

Kevin
 
A

Anno Siegel

PB said:
Anno said:
As for efficiency, parameter evaluation is no place to worry about that,
and program development in general is not the time to worry about it.
[.....]

As for style, there's a lot wrong with the program but I'll restrict myself
to the concrete question.

I was asking for a more general, high level critique of the overall
structure of the script -- I'm aware that I haven't really honed my
"style" much as a programmer, so I encourage any comments anyone might
have (Go ahead -- I have a thick skin).

Quite. I chose to reply only to one of the two questions you packed
into one posting.
I'm not sure what you mean. The reason it doesn't behave the way I
*want* it to is the reason I am asking the question in the first place.

It is customary to post the best attempt so far. Your code didn't even
allow for -f to *have* a value, far less attempt to make that value
the output file. That can't be your best attempt. Instead my impression
is you dumped the code as is without making an attempt at a solution
at all. That is not well received around here.
I posted the code for two reasons: the first is outlined above in this
reply, and the second is to give context to the explanation of my
problem and my question. It's a very simple program and 200 lines of
code is not that difficult to digest.

If you say so. If the problem can be demonstrated in 15 or fewer lines
and I'm supposed to glean the necessary info from 200, I start to wonder
if I'm doing someone else's job.

Two reasons for posting is one reason too much. If you want a code revision,
post a whole program and say so. If you have a specific question, post
minimal code that demonstrates the problem. It is lucky this thread
has stuck to only one part.
I wasn't really phantasizing -- I never demanded that anything *should*
work a certain way. I was explicitly asking *if* it was possible to do
what I wanted.

That is a question for the documentation of the module you're using, not
a newsgroup.
(Now I know that I need to look to Getopt::Long for the
desired functionality).

Not true, at least with my version of Getopt::Std, but go ahead and
use Getopt::Long anyway, it makes the distinction more explicit.
You couldn't if the behavior was as you assume, but it isn't. Here is
how to model the behavior you want:

[code snipped]

Again, I did not make assumptions, nor did I intend to convey that I
was doing so -- I merely meant to sort of confirm something that I
suspected after playing around with this for a while.

Then why don't you show the experimental code and ask for an explanation.
This code should quickly clear up the behavior of Getopt::Std in all
cases (untested):

use Getopt::Std;
use Data::Dumper;

getopts( 'f:', my $opt = {});
print Dumper $opt;

Anno
 
P

PB

Anno said:
It is customary to post the best attempt so far. Your code didn't even
allow for -f to *have* a value, far less attempt to make that value
the output file. That can't be your best attempt. Instead my impression
is you dumped the code as is without making an attempt at a solution
at all. That is not well received around here.

Exactly. As stated in the OP, I did not allow for -f to have a value. I
could make -f a toggle or make it take a parameter (with f: in the
getopt function), but I wanted it to do either one, depending on input
from the command line, and that was my question. What was posted *was*
my best attempt so far -- just sort of a workaround really. It
successfully demonstrates the functionality that I am looking for, but
the entire reason for posting a question here was because that was the
best I was able to come up with and I want to make it more streamlined.

Incidentally, I apologize if I confused the original question with a
request for "additional" comments. I had intended it to be more
marginal than it apparently was.
 
A

Anno Siegel

Kevin Collins said:
But if you test the results from getopts (which I always use to trigger a usage
message), it returns an error!

Ah, it doesn't consider -f without a value kosher. I didn't check that.
Anyway, as mentioned elsewhere, I'd go with Getopt::Long for this. Code
that must rely on the existence of a hash key is always slightly obscure.
Additionally, what if you decided to add another
option? You would have to make sure that the '-f' option was specified last on
the command line.

How so? If you ignore the error, the method works for as many options
as you care to specify.

Anno
 
K

Kevin Collins

Ah, it doesn't consider -f without a value kosher. I didn't check that.
Anyway, as mentioned elsewhere, I'd go with Getopt::Long for this. Code
that must rely on the existence of a hash key is always slightly obscure.


How so? If you ignore the error, the method works for as many options
as you care to specify.

Anno

Nope, if I run this code (./p):

#!/usr/bin/perl

use Getopt::Std;

getopts('f:h', \%opt);

print "opt{f} = $opt{f}\n";
print "opt{h} = $opt{h}\n";

I get:

$ ./p -hf
opt{f} =
opt{h} = 1

$ ./p -fh
opt{f} = h
opt{h} =

$ ./p -f -h
opt{f} = -h
opt{h} =

In other words, you told getopts that -f has an option-argument, so it better
have one!

Kevin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top