相关阅读
Perl:正则表达式
Perl:什么是其特有的autovivafacation性质?
Perl:匿名数组嵌套的解引用相关问题
命令行参数是shell和perl交互的一个重要媒介,本文介绍了如何在Perl中对命令行参数进行处理。
首先我们给出所有的源程序,再分别对其中的各个子例程进行讲解。
sub print_and_exit {
print @_, "\n";
exit 1;
} # print_and_exit
sub read_argv {
my ($aref, $hv) = @_;
my ($opt);
for my $arg ( @$aref ) {
if ( $arg =~ /^-/ ) {
$opt = $arg;
if ( exists $hv->{$opt} ) {
print_and_exit( "Repeated option: $arg" );
}
else {
@{ $hv->{$opt} } = ();
}
}
elsif ( defined $opt ) {
$arg =~ s/^\s*// ;
push @{ $hv->{$opt} }, $arg;
}
else {
print_and_exit( "Un-support option: $arg" );
}
}
} # read_argv
sub check_argv_perl_type {
my ($hr, $hv) = @_;
for my $opt ( keys %$hv ) {
if ( exists $hr->{$opt} ) {
if ( ${$hr->{$opt}}{'perl_type'} eq 'scalar') {
if ( @{ $hv->{$opt} } != 1 ) {
print_and_exit( "Error: only one parameter is expected to '$opt'" );
}
}
elsif ( ${$hr->{$opt}}{'perl_type'} eq 'array') {
if ( @{ $hv->{$opt} } < 1 ) {
print_and_exit( "Error: one or more parameter is expected to '$opt'" );
}
}
else {
print_and_exit( "Error: unknown 'perl_type' of '$opt'" );
}
}
else {
print_and_exit( "Un-support option: '$opt'" );
}
}
} # check_argv_perl_type
sub check_argv_data_type {
my ($hr, $hv) = @_;
for my $opt ( keys %$hv ) {
if ( exists $hr->{$opt} ) {
next unless exists $hr->{$opt}{'data_type'};
if ( $hr->{$opt}{'data_type'} eq 'inputfile') {
for my $arg ( @{ $hv->{$opt} } ) {
if ( ! ( (-f $arg) and (-s $arg) ) ) {
print_and_exit( "Error: input file is expected to '$opt': $arg" );
}
}
}
elsif ( $hr->{$opt}{'data_type'} eq 'num') {
for my $arg ( @{ $hv->{$opt} } ) {
unless ( ( $arg =~ /^-?\d+$/ )
or ( $arg =~ /^-?\d+\.\d+$/ )
or ( $arg =~ /^-?\d+[eE]-?\d+$/ )
or ( $arg =~ /^-?\d+\.\d+[eE]-?\d+$/ )
) {
print_and_exit( "Error: number is expected to '$opt': $arg" );
}
}
}
elsif ( $hr->{$opt}{'data_type'} eq 'inputdir') {
for my $arg ( @{ $hv->{$opt} } ) {
if ( ! -d $arg ) {
print_and_exit( "Error: directory is expected to '$opt': $arg" );
}
}
}
}
else {
print_and_exit( "Un-support option: '$opt'" );
}
}
} # check_argv_data_type
sub get_default {
my ($hr, $hv) = @_;
for my $opt ( keys %$hr ) {
next if exists $hv->{$opt} ;
if ( exists $hr->{$opt}{'default'} ) {
### 'default' => "some_scalar", OR 'default' => ["some", "element", "of", "array"],
$hv->{$opt} = $hr->{$opt}{'default'};
}
else {
print_and_exit( "Error: no input or default for '$opt'" );
}
}
} # get_default
sub combine_scalar {
my ($hr, $hv) = @_;
for my $opt ( keys %$hv ) {
if ( ${$hr->{$opt}}{'perl_type'} eq 'scalar') {
$hv->{$opt} = $hv->{$opt}->[0];
}
}
} # combine_scalar
sub Handle_argv {
my ($aref, $hr, $hv) = @_;
read_argv($aref, $hv);
check_argv_perl_type($hr, $hv);
check_argv_data_type($hr, $hv);
get_default($hr, $hv);
combine_scalar($hr, $hv);
} # Handle_argv
sub print_argv {
my ($hv) = @_;
for my $opt ( keys %$hv ) {
print "$opt =>";
for my $pv ( @{ $hv->{$opt} } ) {
print " $pv";
}
print "\n";
}
} # print_argv
my %rule_of_opt = (
'-s' => {
'perl_type' => 'scalar',
'data_type' => 'inputfile',
},
'-a' => {
'perl_type' => 'array',
'data_type' => 'num',
'default' => '5'
}
);
my (%value_of_opt) ;
Handle_argv( \@ARGV, \%rule_of_opt, \%value_of_opt );
print_argv( \%value_of_opt );
exit 0;
该子例程用于根据参数打印信息并使用exit退出程序。
该子例程用于将命令行参数读进参数散列中。参数散列的键为命令行参数中各个选项,如"-s"和"-a",值为命令行参数中跟在该选项后的参数,在这里,规定属于一个选项的参数是该选项后至下一个选项间的参数,且选项不能重复,否则会报错"Repeated option",因为属于一个选项的参数可能有多个,需要用数组保存,所以使用$hv->{$opt}保存了一个指向数组的引用(注意,Perl中数组的值和散列的值必须是标量(scalar))。使用push将选项的参数值加入该选项对应值(数组引用)指向的数组中。
该子例程用于检查read_argv所读取的散列中,每个选项的参数数量是否符合散列%rule_of_opt所定义的规则,在代码中,"-s"选项的"perl_type"属性为标量,而"-a"选项的"perl_type"属性为数组。如果不符合,则程序会报错提示参数数量有问题,并退出。
该子例程用于检查read_argv所读取的散列中,每个选项的参数类型是否符合散列%rule_of_opt所定义的规则,在代码中,"-s"选项的"data_type"属性为输入文件,而"-a"选项的"perl_type"属性为数字。如果不符合,则程序会报错提示参数类型有问题,并退出。代码中使用了正则表达式对数字进行识别,使用文件操作符-f和-d分别对文件和目录进行识别(有关Perl中正则表达式的内容,可以看Perl:正则表达式)。
该子例程用于在未指定选项时,为其创建默认参数值,默认值可以根据规则是标量或数组,如果一个选项没有出现,且没有默认值,则会报错。
该子例程用于对只有一个参数的选项进行优化,会直接使用$hv->{$opt}保存该参数值,而不是保存指向数组的引用。
这是将子例程整合封装的例程,注意各子例程的调用顺序。
该子例程可以打印经过处理后的选项参数散列,用于观察结果。
源代码来源于《Pelr语言IC设计实践》