Merge Merge lp:~daniel-nichter/percona-toolkit/bash-tool-libs r135.

This commit is contained in:
Daniel Nichter
2011-12-19 12:53:09 -07:00
5 changed files with 160 additions and 105 deletions

View File

@@ -51,11 +51,11 @@ die() {
set -u set -u
declare -a ARGV # non-option args (probably input files) ARGV="" # Non-option args (probably input files)
EXT_ARGV="" # everything after -- (args for an external command) EXT_ARGV="" # Everything after -- (args for an external command)
declare -a OPT_ERRS # errors while parsing options, for usage_or_errors() OPT_ERRS=0 # How many command line option errors
OPT_VERSION="no" OPT_VERSION="no" # If --version was specified
OPT_HELP="no" OPT_HELP="no" # If --help was specified
usage() { usage() {
local file=$1 local file=$1
@@ -77,18 +77,19 @@ usage_or_errors() {
if [ "$OPT_HELP" = "yes" ]; then if [ "$OPT_HELP" = "yes" ]; then
usage "$file" usage "$file"
echo >&2
echo "Command line options:" >&2
echo >&2
for opt in $(ls $TMPDIR/po/); do
local desc=$(cat $TMPDIR/po/$opt | grep '^desc:' | sed -e 's/^desc://')
echo "--$opt" >&2
echo " $desc" >&2
echo >&2
done
return 1 return 1
fi fi
local n_errs=${#OPT_ERRS[*]} if [ $OPT_ERRS -gt 0 ]; then
if [ $n_errs -gt 0 ]; then
local i=0
echo "Errors parsing command line options:" >&2
echo >&2
while [ $i -lt $n_errs ]; do
echo " * ${OPT_ERRS[$i]}" >&2
i=$(($i + 1))
done
echo >&2 echo >&2
usage $file usage $file
return 1 return 1
@@ -104,33 +105,36 @@ parse_options() {
mkdir $TMPDIR/po/ 2>/dev/null mkdir $TMPDIR/po/ 2>/dev/null
rm -rf $TMPDIR/po/* rm -rf $TMPDIR/po/*
( (
export LC_ALL="C" export PO_DIR="$TMPDIR/po"
cat $file | perl -ne '
awk -v "po_dir"="$TMPDIR/po" ' BEGIN { $/ = ""; }
/^=head1 OPTIONS/ { next unless $_ =~ m/^=head1 OPTIONS/;
getline while ( defined(my $para = <>) ) {
while ($0 !~ /^=head1/) { last if $para =~ m/^=head1/;
if ($0 ~ /^=item --.*/) { chomp;
long_opt = substr($2, 3, length($2) - 2) if ( $para =~ m/^=item --(\S+)/ ) {
spec_file = po_dir "/" long_opt my $opt = $1;
trf = "sed -e \"s/[ ]//g\" | tr \";\" \"\n\" > " spec_file my $file = "$ENV{PO_DIR}/$opt";
open my $opt_fh, ">", $file or die "Cannot open $file: $!";
getline # blank line printf $opt_fh "long:$opt\n";
getline # specs or description $para = <>;
chomp;
if ($0 ~ /^[a-z]/ ) { if ( $para =~ m/^[a-z ]+:/ ) {
print "long:" long_opt "; " $0 | trf map {
close(trf) chomp;
} my ($attrib, $val) = split(/: /, $_);
else { printf $opt_fh "$attrib:$val\n";
print "long:" long_opt > spec_file } split(/; /, $para);
close(spec_file) $para = <>;
} chomp;
} }
getline my ($desc) = $para =~ m/^([^.]+)/;
printf $opt_fh "desc:$desc.\n";
close $opt_fh;
} }
exit }
}' $file last;
'
) )
for opt_spec in $(ls $TMPDIR/po/); do for opt_spec in $(ls $TMPDIR/po/); do
@@ -147,10 +151,12 @@ parse_options() {
default) default)
default_val="$val" default_val="$val"
;; ;;
shortform) "short form")
;; ;;
type) type)
;; ;;
desc)
;;
negatable) negatable)
if [ "$val" = "yes" ]; then if [ "$val" = "yes" ]; then
neg=1 neg=1
@@ -177,8 +183,6 @@ parse_options() {
eval "OPT_${opt}"="$default_val" eval "OPT_${opt}"="$default_val"
done done
local i=0 # ARGV index
local j=0 # OPT_ERRS index
for opt; do for opt; do
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
break # no more opts break # no more opts
@@ -191,8 +195,11 @@ parse_options() {
fi fi
shift shift
if [ $(expr "$opt" : "-") -eq 0 ]; then if [ $(expr "$opt" : "-") -eq 0 ]; then
ARGV[i]="$opt" if [ -z "$ARGV" ]; then
i=$((i+1)) ARGV="$opt"
else
ARGV="$ARGV $opt"
fi
continue continue
fi fi
@@ -209,10 +216,10 @@ parse_options() {
if [ -f "$TMPDIR/po/$opt" ]; then if [ -f "$TMPDIR/po/$opt" ]; then
spec="$TMPDIR/po/$opt" spec="$TMPDIR/po/$opt"
else else
spec=$(grep "^shortform:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1) spec=$(grep "^short form:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1)
if [ -z "$spec" ]; then if [ -z "$spec" ]; then
OPT_ERRS[j]="Unknown option: $real_opt" OPT_ERRS=$(($OPT_ERRS + 1))
j=$((j+1)) echo "Unknown option: $real_opt" >&2
continue continue
fi fi
fi fi
@@ -220,8 +227,8 @@ parse_options() {
required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2) required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2)
if [ -n "$required_arg" ]; then if [ -n "$required_arg" ]; then
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
OPT_ERRS[j]="$real_opt requires a $required_arg argument" OPT_ERRS=$(($OPT_ERRS + 1))
j=$((j+1)) echo "$real_opt requires a $required_arg argument" >&2
continue continue
else else
val="$1" val="$1"

View File

@@ -26,11 +26,11 @@ set -u
# Global variables. These must be global because declare inside a # Global variables. These must be global because declare inside a
# sub will be scoped locally. # sub will be scoped locally.
declare -a ARGV # non-option args (probably input files) ARGV="" # Non-option args (probably input files)
EXT_ARGV="" # everything after -- (args for an external command) EXT_ARGV="" # Everything after -- (args for an external command)
declare -a OPT_ERRS # errors while parsing options, for usage_or_errors() OPT_ERRS=0 # How many command line option errors
OPT_VERSION="no" OPT_VERSION="no" # If --version was specified
OPT_HELP="no" OPT_HELP="no" # If --help was specified
# Sub: usage # Sub: usage
# Print usage (--help) and list the program's options. # Print usage (--help) and list the program's options.
@@ -64,18 +64,19 @@ usage_or_errors() {
if [ "$OPT_HELP" = "yes" ]; then if [ "$OPT_HELP" = "yes" ]; then
usage "$file" usage "$file"
echo >&2
echo "Command line options:" >&2
echo >&2
for opt in $(ls $TMPDIR/po/); do
local desc=$(cat $TMPDIR/po/$opt | grep '^desc:' | sed -e 's/^desc://')
echo "--$opt" >&2
echo " $desc" >&2
echo >&2
done
return 1 return 1
fi fi
local n_errs=${#OPT_ERRS[*]} if [ $OPT_ERRS -gt 0 ]; then
if [ $n_errs -gt 0 ]; then
local i=0
echo "Errors parsing command line options:" >&2
echo >&2
while [ $i -lt $n_errs ]; do
echo " * ${OPT_ERRS[$i]}" >&2
i=$(($i + 1))
done
echo >&2 echo >&2
usage $file usage $file
return 1 return 1
@@ -113,37 +114,36 @@ parse_options() {
mkdir $TMPDIR/po/ 2>/dev/null mkdir $TMPDIR/po/ 2>/dev/null
rm -rf $TMPDIR/po/* rm -rf $TMPDIR/po/*
( (
# awk is stupid on some systems (e.g. Ubuntu 10) such that export PO_DIR="$TMPDIR/po"
# /^[a-z]/ incorrectly matches "Foo". This fixes that. cat $file | perl -ne '
export LC_ALL="C" BEGIN { $/ = ""; }
next unless $_ =~ m/^=head1 OPTIONS/;
awk -v "po_dir"="$TMPDIR/po" ' while ( defined(my $para = <>) ) {
/^=head1 OPTIONS/ { last if $para =~ m/^=head1/;
getline chomp;
while ($0 !~ /^=head1/) { if ( $para =~ m/^=item --(\S+)/ ) {
if ($0 ~ /^=item --.*/) { my $opt = $1;
long_opt = substr($2, 3, length($2) - 2) my $file = "$ENV{PO_DIR}/$opt";
spec_file = po_dir "/" long_opt open my $opt_fh, ">", $file or die "Cannot open $file: $!";
trf = "sed -e \"s/[ ]//g\" | tr \";\" \"\n\" > " spec_file printf $opt_fh "long:$opt\n";
$para = <>;
getline # blank line chomp;
getline # specs or description if ( $para =~ m/^[a-z ]+:/ ) {
map {
if ($0 ~ /^[a-z]/ ) { chomp;
# spec line like "type: int; default: 100" my ($attrib, $val) = split(/: /, $_);
print "long:" long_opt "; " $0 | trf printf $opt_fh "$attrib:$val\n";
close(trf) } split(/; /, $para);
} $para = <>;
else { chomp;
# no specs, should be description of option
print "long:" long_opt > spec_file
close(spec_file)
}
} }
getline my ($desc) = $para =~ m/^([^.]+)/;
printf $opt_fh "desc:$desc.\n";
close $opt_fh;
} }
exit }
}' $file last;
'
) )
# Evaluate the program options into existence as global variables # Evaluate the program options into existence as global variables
@@ -163,10 +163,12 @@ parse_options() {
default) default)
default_val="$val" default_val="$val"
;; ;;
shortform) "short form")
;; ;;
type) type)
;; ;;
desc)
;;
negatable) negatable)
if [ "$val" = "yes" ]; then if [ "$val" = "yes" ]; then
neg=1 neg=1
@@ -203,8 +205,6 @@ parse_options() {
# a default value 100, then $OPT_FOO=100 already, but if --foo=500 is # a default value 100, then $OPT_FOO=100 already, but if --foo=500 is
# specified on the command line, then we re-eval $OPT_FOO=500 to update # specified on the command line, then we re-eval $OPT_FOO=500 to update
# $OPT_FOO. # $OPT_FOO.
local i=0 # ARGV index
local j=0 # OPT_ERRS index
for opt; do for opt; do
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
break # no more opts break # no more opts
@@ -219,8 +219,11 @@ parse_options() {
if [ $(expr "$opt" : "-") -eq 0 ]; then if [ $(expr "$opt" : "-") -eq 0 ]; then
# Option does not begin with a hyphen (-), so treat it as # Option does not begin with a hyphen (-), so treat it as
# a filename, directory, etc. # a filename, directory, etc.
ARGV[i]="$opt" if [ -z "$ARGV" ]; then
i=$((i+1)) ARGV="$opt"
else
ARGV="$ARGV $opt"
fi
continue continue
fi fi
@@ -240,10 +243,10 @@ parse_options() {
if [ -f "$TMPDIR/po/$opt" ]; then if [ -f "$TMPDIR/po/$opt" ]; then
spec="$TMPDIR/po/$opt" spec="$TMPDIR/po/$opt"
else else
spec=$(grep "^shortform:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1) spec=$(grep "^short form:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1)
if [ -z "$spec" ]; then if [ -z "$spec" ]; then
OPT_ERRS[j]="Unknown option: $real_opt" OPT_ERRS=$(($OPT_ERRS + 1))
j=$((j+1)) echo "Unknown option: $real_opt" >&2
continue continue
fi fi
fi fi
@@ -255,8 +258,8 @@ parse_options() {
required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2) required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2)
if [ -n "$required_arg" ]; then if [ -n "$required_arg" ]; then
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
OPT_ERRS[j]="$real_opt requires a $required_arg argument" OPT_ERRS=$(($OPT_ERRS + 1))
j=$((j+1)) echo "$real_opt requires a $required_arg argument" >&2
continue continue
else else
val="$1" val="$1"

View File

@@ -66,12 +66,21 @@ is "$OPT_VERSION" "yes" "Short form"
# Have to call this in a subshell because the error will cause an exit. # Have to call this in a subshell because the error will cause an exit.
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --foo >$TMPFILE 2>&1 parse_options "$T_LIB_DIR/samples/bash/po001.sh" --foo >$TMPFILE 2>&1
is "`cat $TMPFILE`" "" "No warnings or errors yet" cmd_ok "grep -q 'Unknown option: --foo' $TMPFILE" "Error on unknown option"
usage_or_errors "$T_LIB_DIR/samples/bash/po001.sh" >$TMPFILE 2>&1 usage_or_errors "$T_LIB_DIR/samples/bash/po001.sh" >$TMPFILE 2>&1
local err=$? local err=$?
is "$err" "1" "Non-zero exit on unknown option" is "$err" "1" "Non-zero exit on unknown option"
cmd_ok "grep -q 'Unknown option: --foo' $TMPFILE" "Error on unknown option"
# ###########################################################################
# --help
# ###########################################################################
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --help
usage_or_errors "$T_LIB_DIR/samples/bash/po001.sh" >$TMPFILE 2>&1
no_diff \
"$TMPFILE" \
"$T_LIB_DIR/samples/bash/help001.txt" \
"--help"
# ############################################################################ # ############################################################################
# Done # Done

View File

@@ -0,0 +1,30 @@
Usage: pt-stalk [OPTIONS] [-- MYSQL_OPTIONS]
For more information, 'man pt-stalk' or 'perldoc /Users/daniel/p/bash-tool-libs/t/lib/samples/bash/po001.sh'.
Command line options:
--help
Print help and exit.
--int-opt
Int option without a default.
--int-opt2
Int option with a default.
--noption
Negatable option.
--string-opt
String option without a default.
--string-opt2
String option with a default.
--typeless-option
Just an option.
--version
Print tool's version and exit.

View File

@@ -110,11 +110,13 @@ Just an option.
default: yes; negatable: yes default: yes; negatable: yes
Negatable option.
=item --int-opt =item --int-opt
type: int type: int
Int option without a default Int option without a default.
=item --int-opt2 =item --int-opt2
@@ -128,6 +130,10 @@ short form: -v
Print tool's version and exit. Print tool's version and exit.
=item --help
Print help and exit.
=back =back
=head1 ENVIRONMENT =head1 ENVIRONMENT