1#!/usr/bin/wish
2
3# This file provides shell command procedures cmd_fnc and t_cmd_fnc.
4
5my_source [list print.tcl]
6
7
8proc cmd_fnc { cmd_buf { quiet {} } { test_mode {} } { print_output {} }\
9  { show_err {} } { ignore_err {} } { acceptable_shell_rcs {} } } {
10
11  # Run the given command in a shell and return the shell return code and the output as a 2 element list.
12
13  # Example usage:
14  # set result [cmd_fnc "date"].
15
16  # Example output:
17
18  # #(CST) 2018/01/17 16:23:28.951643 -    0.001086 - Issuing: date
19  # Mon Feb 19 10:12:10 CST 2018
20  # result:
21  #   result[0]:                                      0x00000000
22  #   result[1]:                                      Mon Feb 19 10:12:10 CST 2018
23
24  # Note: Because of the way this procedure processes parms, the user can specify blank values as a way of
25  # skipping parms.  In the following example, the caller is indicating that they wish to have quiet and
26  # test_mode take their normal defaults but have print_output be 0.:
27  # cmd_fnc "date" "" "" 0
28
29  # Description of argument(s):
30  # cmd_buf                         The command string to be run in a shell.
31  # quiet                           Indicates whether this procedure should run the print_issuing() procedure
32  #                                 which prints "Issuing: <cmd string>" to stdout.  The default value is 0.
33  # test_mode                       If test_mode is set, this procedure will not actually run the command.
34  #                                 If print_output is set, it will print "(test_mode) Issuing: <cmd string>"
35  #                                 to stdout.  The default value is 0.
36  # print_output                    If this is set, this procedure will print the stdout/stderr generated by
37  #                                 the shell command.  The default value is 1.
38  # show_err                        If show_err is set, this procedure will print a standardized error report
39  #                                 if the shell command returns non-zero.  The default value is 1.
40  # ignore_err                      If ignore_err is set, this procedure will not fail if the shell command
41  #                                 fails.  However, if ignore_err is not set, this procedure will exit 1 if
42  #                                 the shell command fails.  The default value is 1.
43  # acceptable_shell_rcs            A list of acceptable shell rcs.  If the shell return code is found in
44  #                                 this list, the shell command is considered successful.  The default value
45  #                                 is {0}.
46
47  # Set defaults.
48  set_var_default quiet [get_stack_var quiet 0 2]
49  set_var_default test_mode 0
50  set_var_default print_output 1
51  set_var_default show_err 1
52  set_var_default ignore_err 1
53  set_var_default acceptable_shell_rcs 0
54
55  qpissuing $cmd_buf $test_mode
56
57  if { $test_mode } { return [list 0 ""] }
58
59  set shell_rc 0
60
61  if { [ catch {set out_buf [eval exec bash -c {$cmd_buf}]} result ] } {
62    set out_buf $result
63    set shell_rc [lindex $::errorCode 2]
64  }
65
66  if { $print_output } { puts "${out_buf}" }
67
68  # Check whether return code is acceptable.
69  if { [lsearch -exact $acceptable_shell_rcs ${shell_rc}] == -1 } {
70    # The command failed.
71    append error_message "The prior shell command failed.\n"
72    append error_message [sprint_var shell_rc "" "" 1]
73    if { $acceptable_shell_rcs != 0 } {
74      # acceptable_shell_rcs contains more than just a single element equal to 0.
75      append error_message "\n"
76      append error_message [sprint_list acceptable_shell_rcs "" "" 1]
77    }
78    if { ! $print_output } {
79      append error_message "out_buf:\n${out_buf}"
80    }
81    if { $show_err } {
82      print_error_report $error_message
83    }
84
85    if { ! $ignore_err } {
86      exit 1
87    }
88
89  }
90
91  return [list $shell_rc $out_buf]
92
93}
94
95
96proc t_cmd_fnc { args } {
97
98  # Call cmd_fnc with test_mode equal to the test_mode setting found by searching up the call stack.  See
99  # cmd_fnc (above) for details for all other arguments.
100
101  # We wish to obtain a value for test_mode by searching up the call stack.  This value will govern whether
102  # the command specified actually gets executed.
103  set_var_default test_mode [get_stack_var test_mode 0 2]
104
105  # Since we wish to manipulate the value of test_mode, which is the third positional parm, we must make
106  # sure we have at least 3 parms.  We will now append blank values to the args list as needed to ensure that
107  # we have the minimum 3 parms.
108  set min_args 3
109  for {set ix [llength $args]} {$ix < $min_args} {incr ix} {
110    lappend args {}
111  }
112
113  # Now replace the caller's test_mode value with the value obtained from the call stack search.  It does
114  # not matter what value is specified by the caller for test_mode.  It will be replaced.  The whole point of
115  # calling t_cmd_fnc is to allow it to set the test_mode.
116  set args [lreplace $args 2 2 $test_mode]
117
118  return [cmd_fnc {*}$args]
119
120}
121