mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-10 13:11:32 +00:00
Merge Merge lp:~daniel-nichter/percona-toolkit/bash-tool-libs r135.
This commit is contained in:
101
bin/pt-stalk
101
bin/pt-stalk
@@ -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(/: /, $_);
|
||||||
|
printf $opt_fh "$attrib:$val\n";
|
||||||
|
} split(/; /, $para);
|
||||||
|
$para = <>;
|
||||||
|
chomp;
|
||||||
}
|
}
|
||||||
else {
|
my ($desc) = $para =~ m/^([^.]+)/;
|
||||||
print "long:" long_opt > spec_file
|
printf $opt_fh "desc:$desc.\n";
|
||||||
close(spec_file)
|
close $opt_fh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getline
|
last;
|
||||||
}
|
'
|
||||||
exit
|
|
||||||
}' $file
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -211,8 +218,8 @@ parse_options() {
|
|||||||
else
|
else
|
||||||
spec=$(grep "^short form:-$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"
|
||||||
|
@@ -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 = <>;
|
||||||
|
chomp;
|
||||||
}
|
}
|
||||||
else {
|
my ($desc) = $para =~ m/^([^.]+)/;
|
||||||
# no specs, should be description of option
|
printf $opt_fh "desc:$desc.\n";
|
||||||
print "long:" long_opt > spec_file
|
close $opt_fh;
|
||||||
close(spec_file)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getline
|
last;
|
||||||
}
|
'
|
||||||
exit
|
|
||||||
}' $file
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@@ -242,8 +245,8 @@ parse_options() {
|
|||||||
else
|
else
|
||||||
spec=$(grep "^short form:-$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"
|
||||||
|
@@ -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
|
||||||
|
30
t/lib/samples/bash/help001.txt
Normal file
30
t/lib/samples/bash/help001.txt
Normal 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.
|
||||||
|
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user