~percona-toolkit-dev/percona-toolkit/release-2.2.2

« back to all changes in this revision

Viewing changes to bin/pt-mext

Merge version-in-all-bash-tools-bug-821502.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
# See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal
5
5
# notices and disclaimers.
6
6
 
7
 
usage() {
8
 
   if [ "${OPT_ERR}" ]; then
9
 
      echo "${OPT_ERR}" >&2
10
 
   fi
11
 
   echo "Usage: pt-mext [OPTIONS] -- COMMAND" >&2
12
 
   echo "For more information, 'man pt-mext' or 'perldoc $0'" >&2
 
7
# ###########################################################################
 
8
# log_warn_die package
 
9
# This package is a copy without comments from the original.  The original
 
10
# with comments and its test file can be found in the Bazaar repository at,
 
11
#   lib/bash/log_warn_die.sh
 
12
#   t/lib/bash/log_warn_die.sh
 
13
# See https://launchpad.net/percona-toolkit for more information.
 
14
# ###########################################################################
 
15
 
 
16
 
 
17
set -u
 
18
 
 
19
PTFUNCNAME=""
 
20
PTDEBUG="${PTDEBUG:-""}"
 
21
EXIT_STATUS=0
 
22
 
 
23
ts() {
 
24
   TS=$(date +%F-%T | tr ':-' '_')
 
25
   echo "$TS $*"
 
26
}
 
27
 
 
28
info() {
 
29
   [ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*"
 
30
}
 
31
 
 
32
log() {
 
33
   [ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*"
 
34
}
 
35
 
 
36
warn() {
 
37
   [ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2
 
38
   EXIT_STATUS=1
 
39
}
 
40
 
 
41
die() {
 
42
   ts "$*" >&2
 
43
   EXIT_STATUS=1
13
44
   exit 1
14
45
}
15
46
 
16
 
if [ -z "$1" ]; then
17
 
   usage;
18
 
fi
 
47
_d () {
 
48
   [ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2
 
49
}
 
50
 
 
51
# ###########################################################################
 
52
# End log_warn_die package
 
53
# ###########################################################################
19
54
 
20
55
# ###########################################################################
21
56
# tmpdir package
58
93
# End tmpdir package
59
94
# ###########################################################################
60
95
 
 
96
# ###########################################################################
 
97
# parse_options package
 
98
# This package is a copy without comments from the original.  The original
 
99
# with comments and its test file can be found in the Bazaar repository at,
 
100
#   lib/bash/parse_options.sh
 
101
#   t/lib/bash/parse_options.sh
 
102
# See https://launchpad.net/percona-toolkit for more information.
 
103
# ###########################################################################
 
104
 
 
105
 
 
106
 
 
107
 
 
108
 
 
109
set -u
 
110
 
 
111
ARGV=""           # Non-option args (probably input files)
 
112
EXT_ARGV=""       # Everything after -- (args for an external command)
 
113
HAVE_EXT_ARGV=""  # Got --, everything else is put into EXT_ARGV
 
114
OPT_ERRS=0        # How many command line option errors
 
115
OPT_VERSION=""    # If --version was specified
 
116
OPT_HELP=""       # If --help was specified
 
117
PO_DIR=""         # Directory with program option spec files
 
118
 
 
119
usage() {
 
120
   local file="$1"
 
121
 
 
122
   local usage="$(grep '^Usage: ' "$file")"
 
123
   echo $usage
 
124
   echo
 
125
   echo "For more information, 'man $TOOL' or 'perldoc $file'."
 
126
}
 
127
 
 
128
usage_or_errors() {
 
129
   local file="$1"
 
130
 
 
131
   if [ "$OPT_VERSION" ]; then
 
132
      local version=$(grep '^pt-[^ ]\+ [0-9]' "$file")
 
133
      echo "$version"
 
134
      return 1
 
135
   fi
 
136
 
 
137
   if [ "$OPT_HELP" ]; then
 
138
      usage "$file"
 
139
      echo
 
140
      echo "Command line options:"
 
141
      echo
 
142
      perl -e '
 
143
         use strict;
 
144
         use warnings FATAL => qw(all);
 
145
         my $lcol = 20;         # Allow this much space for option names.
 
146
         my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide.
 
147
         my $name;
 
148
         while ( <> ) {
 
149
            my $line = $_;
 
150
            chomp $line;
 
151
            if ( $line =~ s/^long:/  --/ ) {
 
152
               $name = $line;
 
153
            }
 
154
            elsif ( $line =~ s/^desc:// ) {
 
155
               $line =~ s/ +$//mg;
 
156
               my @lines = grep { $_      }
 
157
                           $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g;
 
158
               if ( length($name) >= $lcol ) {
 
159
                  print $name, "\n", (q{ } x $lcol);
 
160
               }
 
161
               else {
 
162
                  printf "%-${lcol}s", $name;
 
163
               }
 
164
               print join("\n" . (q{ } x $lcol), @lines);
 
165
               print "\n";
 
166
            }
 
167
         }
 
168
      ' "$PO_DIR"/*
 
169
      echo
 
170
      echo "Options and values after processing arguments:"
 
171
      echo
 
172
      (
 
173
         cd "$PO_DIR"
 
174
         for opt in *; do
 
175
            local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)"
 
176
            eval local varvalue=\$$varname
 
177
            if ! grep -q "type:" "$PO_DIR/$opt" >/dev/null; then
 
178
               if [ "$varvalue" -a "$varvalue" = "yes" ];
 
179
                  then varvalue="TRUE"
 
180
               else
 
181
                  varvalue="FALSE"
 
182
               fi
 
183
            fi
 
184
            printf -- "  --%-30s %s" "$opt" "${varvalue:-(No value)}"
 
185
            echo
 
186
         done
 
187
      )
 
188
      return 1
 
189
   fi
 
190
 
 
191
   if [ $OPT_ERRS -gt 0 ]; then
 
192
      echo
 
193
      usage "$file"
 
194
      return 1
 
195
   fi
 
196
 
 
197
   return 0
 
198
}
 
199
 
 
200
option_error() {
 
201
   local err="$1"
 
202
   OPT_ERRS=$(($OPT_ERRS + 1))
 
203
   echo "$err" >&2
 
204
}
 
205
 
 
206
parse_options() {
 
207
   local file="$1"
 
208
   shift
 
209
 
 
210
   ARGV=""
 
211
   EXT_ARGV=""
 
212
   HAVE_EXT_ARGV=""
 
213
   OPT_ERRS=0
 
214
   OPT_VERSION=""
 
215
   OPT_HELP=""
 
216
   PO_DIR="$PT_TMPDIR/po"
 
217
 
 
218
   if [ ! -d "$PO_DIR" ]; then
 
219
      mkdir "$PO_DIR"
 
220
      if [ $? -ne 0 ]; then
 
221
         echo "Cannot mkdir $PO_DIR" >&2
 
222
         exit 1
 
223
      fi
 
224
   fi
 
225
 
 
226
   rm -rf "$PO_DIR"/*
 
227
   if [ $? -ne 0 ]; then
 
228
      echo "Cannot rm -rf $PO_DIR/*" >&2
 
229
      exit 1
 
230
   fi
 
231
 
 
232
   _parse_pod "$file"  # Parse POD into program option (po) spec files
 
233
   _eval_po            # Eval po into existence with default values
 
234
 
 
235
   if [ $# -ge 2 ] &&  [ "$1" = "--config" ]; then
 
236
      shift  # --config
 
237
      local user_config_files="$1"
 
238
      shift  # that ^
 
239
      local IFS=","
 
240
      for user_config_file in $user_config_files; do
 
241
         _parse_config_files "$user_config_file"
 
242
      done
 
243
   else
 
244
      _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf"
 
245
   fi
 
246
 
 
247
   _parse_command_line "${@:-""}"
 
248
}
 
249
 
 
250
_parse_pod() {
 
251
   local file="$1"
 
252
 
 
253
   cat "$file" | PO_DIR="$PO_DIR" perl -ne '
 
254
      BEGIN { $/ = ""; }
 
255
      next unless $_ =~ m/^=head1 OPTIONS/;
 
256
      while ( defined(my $para = <>) ) {
 
257
         last if $para =~ m/^=head1/;
 
258
         chomp;
 
259
         if ( $para =~ m/^=item --(\S+)/ ) {
 
260
            my $opt  = $1;
 
261
            my $file = "$ENV{PO_DIR}/$opt";
 
262
            open my $opt_fh, ">", $file or die "Cannot open $file: $!";
 
263
            print $opt_fh "long:$opt\n";
 
264
            $para = <>;
 
265
            chomp;
 
266
            if ( $para =~ m/^[a-z ]+:/ ) {
 
267
               map {
 
268
                  chomp;
 
269
                  my ($attrib, $val) = split(/: /, $_);
 
270
                  print $opt_fh "$attrib:$val\n";
 
271
               } split(/; /, $para);
 
272
               $para = <>;
 
273
               chomp;
 
274
            }
 
275
            my ($desc) = $para =~ m/^([^?.]+)/;
 
276
            print $opt_fh "desc:$desc.\n";
 
277
            close $opt_fh;
 
278
         }
 
279
      }
 
280
      last;
 
281
   '
 
282
}
 
283
 
 
284
_eval_po() {
 
285
   local IFS=":"
 
286
   for opt_spec in "$PO_DIR"/*; do
 
287
      local opt=""
 
288
      local default_val=""
 
289
      local neg=0
 
290
      local size=0
 
291
      while read key val; do
 
292
         case "$key" in
 
293
            long)
 
294
               opt=$(echo $val | sed 's/-/_/g' | tr '[:lower:]' '[:upper:]')
 
295
               ;;
 
296
            default)
 
297
               default_val="$val"
 
298
               ;;
 
299
            "short form")
 
300
               ;;
 
301
            type)
 
302
               [ "$val" = "size" ] && size=1
 
303
               ;;
 
304
            desc)
 
305
               ;;
 
306
            negatable)
 
307
               if [ "$val" = "yes" ]; then
 
308
                  neg=1
 
309
               fi
 
310
               ;;
 
311
            *)
 
312
               echo "Invalid attribute in $opt_spec: $line" >&2
 
313
               exit 1
 
314
         esac 
 
315
      done < "$opt_spec"
 
316
 
 
317
      if [ -z "$opt" ]; then
 
318
         echo "No long attribute in option spec $opt_spec" >&2
 
319
         exit 1
 
320
      fi
 
321
 
 
322
      if [ $neg -eq 1 ]; then
 
323
         if [ -z "$default_val" ] || [ "$default_val" != "yes" ]; then
 
324
            echo "Option $opt_spec is negatable but not default: yes" >&2
 
325
            exit 1
 
326
         fi
 
327
      fi
 
328
 
 
329
      if [ $size -eq 1 -a -n "$default_val" ]; then
 
330
         default_val=$(size_to_bytes $default_val)
 
331
      fi
 
332
 
 
333
      eval "OPT_${opt}"="$default_val"
 
334
   done
 
335
}
 
336
 
 
337
_parse_config_files() {
 
338
 
 
339
   for config_file in "${@:-""}"; do
 
340
      test -f "$config_file" || continue
 
341
 
 
342
      while read config_opt; do
 
343
 
 
344
         echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue
 
345
 
 
346
         config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')"
 
347
 
 
348
         [ "$config_opt" = "" ] && continue
 
349
 
 
350
         if ! [ "$HAVE_EXT_ARGV" ]; then
 
351
            config_opt="--$config_opt"
 
352
         fi
 
353
 
 
354
         _parse_command_line "$config_opt"
 
355
 
 
356
      done < "$config_file"
 
357
 
 
358
      HAVE_EXT_ARGV=""  # reset for each file
 
359
 
 
360
   done
 
361
}
 
362
 
 
363
_parse_command_line() {
 
364
   local opt=""
 
365
   local val=""
 
366
   local next_opt_is_val=""
 
367
   local opt_is_ok=""
 
368
   local opt_is_negated=""
 
369
   local real_opt=""
 
370
   local required_arg=""
 
371
   local spec=""
 
372
 
 
373
   for opt in "${@:-""}"; do
 
374
      if [ "$opt" = "--" -o "$opt" = "----" ]; then
 
375
         HAVE_EXT_ARGV=1
 
376
         continue
 
377
      fi
 
378
      if [ "$HAVE_EXT_ARGV" ]; then
 
379
         if [ "$EXT_ARGV" ]; then
 
380
            EXT_ARGV="$EXT_ARGV $opt"
 
381
         else
 
382
            EXT_ARGV="$opt"
 
383
         fi
 
384
         continue
 
385
      fi
 
386
 
 
387
      if [ "$next_opt_is_val" ]; then
 
388
         next_opt_is_val=""
 
389
         if [ $# -eq 0 ] || [ $(expr "$opt" : "\-") -eq 1 ]; then
 
390
            option_error "$real_opt requires a $required_arg argument"
 
391
            continue
 
392
         fi
 
393
         val="$opt"
 
394
         opt_is_ok=1
 
395
      else
 
396
         if [ $(expr "$opt" : "\-") -eq 0 ]; then
 
397
            if [ -z "$ARGV" ]; then
 
398
               ARGV="$opt"
 
399
            else
 
400
               ARGV="$ARGV $opt"
 
401
            fi
 
402
            continue
 
403
         fi
 
404
 
 
405
         real_opt="$opt"
 
406
 
 
407
         if $(echo $opt | grep '^--no[^-]' >/dev/null); then
 
408
            local base_opt=$(echo $opt | sed 's/^--no//')
 
409
            if [ -f "$PT_TMPDIR/po/$base_opt" ]; then
 
410
               opt_is_negated=1
 
411
               opt="$base_opt"
 
412
            else
 
413
               opt_is_negated=""
 
414
               opt=$(echo $opt | sed 's/^-*//')
 
415
            fi
 
416
         else
 
417
            if $(echo $opt | grep '^--no-' >/dev/null); then
 
418
               opt_is_negated=1
 
419
               opt=$(echo $opt | sed 's/^--no-//')
 
420
            else
 
421
               opt_is_negated=""
 
422
               opt=$(echo $opt | sed 's/^-*//')
 
423
            fi
 
424
         fi
 
425
 
 
426
         if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then
 
427
            val="$(echo $opt | awk -F= '{print $2}')"
 
428
            opt="$(echo $opt | awk -F= '{print $1}')"
 
429
         fi
 
430
 
 
431
         if [ -f "$PT_TMPDIR/po/$opt" ]; then
 
432
            spec="$PT_TMPDIR/po/$opt"
 
433
         else
 
434
            spec=$(grep "^short form:-$opt\$" "$PT_TMPDIR"/po/* | cut -d ':' -f 1)
 
435
            if [ -z "$spec"  ]; then
 
436
               option_error "Unknown option: $real_opt"
 
437
               continue
 
438
            fi
 
439
         fi
 
440
 
 
441
         required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}')
 
442
         if [ "$required_arg" ]; then
 
443
            if [ "$val" ]; then
 
444
               opt_is_ok=1
 
445
            else
 
446
               next_opt_is_val=1
 
447
            fi
 
448
         else
 
449
            if [ "$val" ]; then
 
450
               option_error "Option $real_opt does not take a value"
 
451
               continue
 
452
            fi 
 
453
            if [ "$opt_is_negated" ]; then
 
454
               val=""
 
455
            else
 
456
               val="yes"
 
457
            fi
 
458
            opt_is_ok=1
 
459
         fi
 
460
      fi
 
461
 
 
462
      if [ "$opt_is_ok" ]; then
 
463
         opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr '[:lower:]' '[:upper:]')
 
464
 
 
465
         if grep "^type:size" "$spec" >/dev/null; then
 
466
            val=$(size_to_bytes $val)
 
467
         fi
 
468
 
 
469
         eval "OPT_$opt"="'$val'"
 
470
 
 
471
         opt=""
 
472
         val=""
 
473
         next_opt_is_val=""
 
474
         opt_is_ok=""
 
475
         opt_is_negated=""
 
476
         real_opt=""
 
477
         required_arg=""
 
478
         spec=""
 
479
      fi
 
480
   done
 
481
}
 
482
 
 
483
size_to_bytes() {
 
484
   local size="$1"
 
485
   echo $size | perl -ne '%f=(B=>1, K=>1_024, M=>1_048_576, G=>1_073_741_824, T=>1_099_511_627_776); m/^(\d+)([kMGT])?/i; print $1 * $f{uc($2 || "B")};'
 
486
}
 
487
 
 
488
# ###########################################################################
 
489
# End parse_options package
 
490
# ###########################################################################
 
491
 
 
492
# ###########################################################################
 
493
# alt_cmds package
 
494
# This package is a copy without comments from the original.  The original
 
495
# with comments and its test file can be found in the Bazaar repository at,
 
496
#   lib/bash/alt_cmds.sh
 
497
#   t/lib/bash/alt_cmds.sh
 
498
# See https://launchpad.net/percona-toolkit for more information.
 
499
# ###########################################################################
 
500
 
 
501
 
 
502
set -u
 
503
 
 
504
_seq() {
 
505
   local i="$1"
 
506
   awk "BEGIN { for(i=1; i<=$i; i++) print i; }"
 
507
}
 
508
 
 
509
_pidof() {
 
510
   local cmd="$1"
 
511
   if ! pidof "$cmd" 2>/dev/null; then
 
512
      ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }'
 
513
   fi
 
514
}
 
515
 
 
516
_lsof() {
 
517
   local pid="$1"
 
518
   if ! lsof -p $pid 2>/dev/null; then
 
519
      /bin/ls -l /proc/$pid/fd 2>/dev/null
 
520
   fi
 
521
}
 
522
 
 
523
 
 
524
 
 
525
_which() {
 
526
   if [ -x /usr/bin/which ]; then
 
527
      /usr/bin/which "$1" 2>/dev/null | awk '{print $1}'
 
528
   elif which which 1>/dev/null 2>&1; then
 
529
      which "$1" 2>/dev/null | awk '{print $1}'
 
530
   else
 
531
      echo "$1"
 
532
   fi
 
533
}
 
534
 
 
535
# ###########################################################################
 
536
# End alt_cmds package
 
537
# ###########################################################################
 
538
 
 
539
TOOL="pt-mext"
 
540
 
 
541
# Parse command line options.
61
542
mk_tmpdir
 
543
parse_options "$0" "${@:-""}"
 
544
 
 
545
if [ -z "$OPT_HELP" -a -z "$OPT_VERSION" ]; then
 
546
   if [ -z "$EXT_ARGV" ]; then
 
547
      option_error "No COMMAND was given."
 
548
   fi
 
549
fi
 
550
 
 
551
usage_or_errors "$0"
 
552
po_status=$?
 
553
 
 
554
if [ $po_status -ne 0 ]; then
 
555
   [ $OPT_ERRS -gt 0 ] && exit 1
 
556
   exit 0
 
557
fi
62
558
 
63
559
FILE="$PT_TMPDIR/mext_temp_file";
64
 
NUM=0;
65
 
REL=0;
66
 
 
67
 
# Command-line parsing.
68
 
args=`getopt -u -n mext r "$@"`;
69
 
if [ "$?" = "1" ]; then
70
 
   usage;
71
 
fi
72
 
set -- $args
73
 
for o; do
74
 
   case "$o" in
75
 
      -r)   REL="1"; shift;;
76
 
      --)   shift;   break;;
77
 
   esac
78
 
done
79
 
 
80
 
if [ -z "$1" ]; then
81
 
   usage;
82
 
fi
 
560
NUM=1;
83
561
 
84
562
# Split the output on empty lines and put each into a different file; eliminate
85
563
# lines that don't have "real" content.
86
 
$@ | grep -v '+' | grep -v Variable_name | sed 's/|//g' \
 
564
$EXT_ARGV | grep -v '+' | grep -v Variable_name | sed 's/|//g' \
87
565
   | while read line; do
88
566
   if [ "$line" = "" ]; then
89
 
      NUM=`expr $NUM + 1`;
 
567
      NUM=$(($NUM + 1))
90
568
      echo "" > "$FILE$NUM"
91
569
   fi
92
570
   echo "$line" >> "$FILE$NUM"
93
571
done
94
572
 
95
 
# Count how many files there are and prepare to format the output
96
573
SPEC="%-33s %13d"
97
574
AWKS=""
 
575
 
 
576
# Count how many files there are and prepare to format the output, but...
98
577
NUM=`ls "$FILE"* | wc -l`;
99
 
# The last file will be empty...
100
 
NUM=`expr $NUM - 3`;
 
578
 
 
579
# ... iterate through files 1..(N-2) because the last file is empty and
 
580
# we join N to N+1 so also don't read the last real file.
 
581
NUM=$((NUM - 2))
101
582
 
102
583
# Join each file with the next file, joining on the first field. Build a printf
103
584
# spec and awk spec at the same time.
104
 
for i in `seq 0 $NUM`; do
105
 
   NEXTFILE=`expr $i + 1`;
 
585
for i in `_seq $NUM`; do
 
586
   NEXTFILE=$(($i + 1))
106
587
 
107
588
   # Sort each file and eliminate empty lines, so 'join' doesn't complain.
108
589
   sort "$FILE$i" | grep . > "$FILE$i.tmp"
119
600
   MAXLEN=`awk '{print $2}' "$FILE${NEXTFILE}" | grep -v '[^0-9]' | awk '{print length($1)}' | sort -rn | head -n1`
120
601
   mv "$FILE" "$FILE${NEXTFILE}"
121
602
   SPEC="$SPEC %${MAXLEN}d";
122
 
   if [ "$REL" = "1" ]; then
123
 
      AWKS="$AWKS, \$`expr $i + 3` - \$`expr $i + 2`";
 
603
 
 
604
   # The final file will contain lines like:
 
605
   #
 
606
   #   Bytes_received 100 200 50 300
 
607
   #
 
608
   # For each such line in awk, $1 is the var name and $2 is the first value
 
609
   # of the var, so these are fixed when we build AWKCMD after this loop.
 
610
   # When i=1, we're comparing file1 to file2, and the resulting value becomes
 
611
   # awk $3.  Hence $i + 2=$3 below.  Then incr and repeat for subsequent files.
 
612
   #
 
613
   # With --relative, the $i and awk field numbers are the same, but we print
 
614
   # differences $3-$2, $4-$3, $5-$4 from the input line for awk fields $3, $4,
 
615
   # and $5 respectively.  Here's a table:
 
616
   #
 
617
   #    i  awk  Input line fields
 
618
   #   ==  ===  =================
 
619
   #    1   $3              $3-$2
 
620
   #    2   $4              $4-$3
 
621
   #    3   $5              $5-$4
 
622
   if [ "$OPT_RELATIVE" ]; then
 
623
      AWKS="$AWKS, \$`expr $i + 2` - \$`expr $i + 1`";
124
624
   else
125
 
      AWKS="$AWKS, \$`expr $i + 3`";
 
625
      AWKS="$AWKS, \$$(($i + 2))";
126
626
   fi
127
627
done
128
628
 
153
653
 
154
654
Get output from C<mysqladmin>:
155
655
 
156
 
   pt-mext -r -- mysqladmin ext -i10 -c3"
 
656
   pt-mext -r -- mysqladmin ext -i10 -c3
157
657
 
158
658
Get output from a file:
159
659
 
191
691
 
192
692
=over
193
693
 
194
 
=item -r
195
 
 
196
 
Relative: subtract each column from the previous column.
 
694
=item --help
 
695
 
 
696
Show help and exit.
 
697
 
 
698
=item --relative
 
699
 
 
700
short form: -r
 
701
 
 
702
Subtract each column from the previous column.
 
703
 
 
704
=item --version
 
705
 
 
706
Show version and exit.
197
707
 
198
708
=back
199
709