Home

A Script template for ksh scripts

Some useful techniques for ksh scripts



Bernd Schemmer, 03  Dezember 2017



Contents

  1. Contents
  2. Introduction
    1. History of this document
  3. Credits
  4. Why use a script template?
  5. How to use the script template
  6. Techniques and features used in the template
    1. Inline Documentation
    2. Inline examples
    3. Naming conventions
    4. Logging
    5. Redirection of STDOUT and STDERR
    6. Builtin tee functionality
    7. Runtime Initialisation
      1. Environment checks
      2. OS and shell dependent settings
      3. Restrict a script to run only once
      4. Using RBAC in Solaris 10
      5. Predefined variables
      6. Using configuration files
        1. Implementation Details
    8. Housekeeping using a trap handler
      1. Additional features of the trap handlers
      2. User defined trap handler
    9. Crash dumps
    10. Include files
    11. Temporary files
    12. Running the script as daemon
    13. Predefined parameter
    14. Debugging
      1. Adding other extended options
    15. Environment variables
    16. Return codes
    17. Functions defined in the script template
      1. The function AskUser
      2. The function BackupFileIfNecessary
      3. String handling functions
      4. Data convertion functions
      5. UID related functions
      6. Functions to implement a FIFO Stack
      7. Misc functions
      8. Other techniques used in the script template
      9. Define a function if it's not already defined
    18. Parameter handling
    19. Variable intializing
    20. Function templates
    21. The DebugShell
      1. DebugShell online help
      2. Known aliase in the DebugShell
        1. functions - list all functions defined in the script
        2. func - view the source code of a function
        3. add_debug_code - add code to enable tracing to one or more functions
        4. view_debug - view the current value for ${__DEBUG_CODE}
        5. set_debug - add code to enable tracing for one or more functions
        6. clear_debug - clear the debug code
        7. vars help  - list all defined variable lists for the alias vars
        8. vars <variable_list> - view the contents of all variables in the list <variable_list>
        9. verbose - switch the verbose mode
        10. break - enable or disable CTRL-C
        11. exit - exit the DebugShell and continue script execution
        12. quit - exit the script using the function die
        13. abort - exit the script using "kill -9 $$"
        14. cont - continue the script execution
  7. Links
    1. Scripts based on scriptt.sh


Introduction


This article discusses and documents scriptt.sh, a script template for kornshell scripts.  I use this script template for nearly all of the scripts I write for doing the day-to-day work.

I'm pretty sure that every System Administrator who is responsible for more than a few machines running Solaris, Linux, AIX or another Unix operating system has her own bag of scripts for maintaining the machines. Nevertheless the script template and the programming techniques discussed in this article might be useful for them also.

The script template is released under the Common Development and Distribution License, Version 1.0; a link to download the script is at the bottom of this page.

This documentation is based on scriptt.sh version 2.3.0.0, 10.11.2017.

Note that the script was developed mainly for the kornshell (this is a ksh88 shell) that is part of Solaris (/usr/bin/ksh) but it also runs on other Unix platforms like AIX and Linux or cygwin. The script also run on kornshell compatible shells like bash but not all features of the script work in other shells. Therefor I recommend to always use the real ksh to execute the scripts based on the script template.

The article is a rewrite of an article I wrote for the bigadmin section on sunsolve.sun.com which is not available on the net anymore

There is now a minimal version of scriptt.sh available: scriptt_mini.sh. This is  a much smaller shell script template with only the most important functionality for shell scripts.



History of this document


Date
Release
Comment
03.12.2017 Updated documentation for scriptt.sh v2.3.0.0
16.08.2015
Updated documentation for scriptt.sh v2.1.0.7

31.05.2013
Updated documentation for scriptt.sh v2.0.0

22.04.2012
minor changes and error corrections

16.10.2011
minor changes only

15.10.2011
initial release







Credits

The script template uses code from the following sources:

Infos from WPollock about how to implement a semaphore in a shell script:

The source for the function PrintWithTimeStamp is from this web page:

http://unix.stackexchange.com/questions/26728/prepending-a-timestamp-to-each-line-of-output-from-a-command


The code in the function executeCommandAndLog is based on code on this webpage:

http://www.unix.com/unix-dummies-questions-answers/13018-exit-status-command-pipe-line.html#post47559



Why use a script template?


Simple answer: Because you don't want to invent the wheel every other day.

Writing a script template with the most frequently used functions one time and reuse it for your scripts makes life easier. Doing this as shell script instead of for example perl makes sure, that you can just copy the script to a new machine and it will work. 

Please note that the script template only uses standard Solaris (Unix) commands and does not rely on additional scripts or something special. It's done this way to make sure that it will run on any system simply by copying the script to the machine.



How to use the script template


To use the script template for your own script the following steps are necessary:

First copy the script template to a new file. Than edit the new file:

That's it..

Please note that the main function where you should add your code is at the bottom of the script

There are some marker defined in the source code that you can use with the search function of your editor while editing the script template :

# ---------------------------------------------------------------------
# ****  Note: The main code starts after the line containing "# main:" ****
#             The main code for your script should start after "# main - your code"
#
# Additional jump targets:
#
#   #main          main code
#   #mit           init main function
#   #svd           set variable defaults
#   #uds           user defined sub routines
#   #auh           add. usage help
#   #udf           user defined functions
#   #udp           user defined parameter
#

Some of the comment lines in the script use a specific format, e.g.

##EXAMPLE#

####

##C#

##T#

These are used to for the inline documentation and should not be changed.

To create the documentation for the current version of the script template use the parameter "-D create_documentation" ; this will create all files with the inline documentation, e.g.

[xtrnaw7@t540p /data/develop/scripts]$ ./scriptt.sh -D create_documentation
[03.12.2017 12:33:31] scriptt.sh v1.0.0 started at Sun Dec  3 12:33:31 CET 2017.
[03.12.2017 12:33:31] Reading the config file "/data/develop/scripts/scriptt.conf" ...
[03.12.2017 12:33:31] DEBUG: Writing the long usage documentation to scriptt.sh.long_usage.txt ...
[03.12.2017 12:33:31] DEBUG: Writing the debug switch documentation to scriptt.sh.debug_switches.txt ...
[03.12.2017 12:33:31] DEBUG: Writing the script documentation to scriptt.sh.txt ...
[03.12.2017 12:33:32] DEBUG: Writing the script usage examples to scriptt.sh.usage_examples.txt ...
[03.12.2017 12:33:32] DEBUG: Writing the function usage examples to scriptt.sh.function_examples.txt ...
[03.12.2017 12:33:32] The log file used was "/tmp/scriptt.sh.8962.TEMP" 
[03.12.2017 12:33:32] The debug messages are logged to "/tmp/scriptt.sh.8962.debug" 
[03.12.2017 12:33:32] scriptt.sh v1.0.0 started at Sun Dec  3 12:33:31 CET 2017 and ended at Sun Dec  3 12:33:32 CET 2017.
[03.12.2017 12:33:32] The time used for the script is 0 minutes and 1 seconds.
[03.12.2017 12:33:32] The RC is 0.
[xtrnaw7@t540p /data/develop/scripts]$



Techniques and features used in the template


This section describes some of the techniques used in the script template.


Note again that this is only a template for a script. Therefore to use it you should copy it to another name and edit that file. To use the features mentioned below with your script please replace "scriptt.sh" in the examples below with the name of your script.



Inline Documentation


Everyone knows that documentation is essential but no one wants to write them. Because of this it's a useful and simple technique to include the documentation inside the script as comments. To distinguish the documentation parts from the normal comments prefix the lines with documentation with a double hash '##' in column 1 and 2. Doing this extracting the documentation for a script can be done using the following code:


echo " ----------------------------------------------------------------------------------" >&2
echo "                         ${__SCRIPTNAME} ${__SCRIPT_VERSION} ">&2
echo "                                Documentation" >&2
echo " ----------------------------------------------------------------------------------" >&2

             grep "^##" "$0" | grep -v "##EXAMPLE##" | cut -c5- 1>&2
             die 0 ;;


This is implemented as action for the parameter "-H". Therefore a

./scriptt.sh -H 2>./scriptt.txt

just creates the documentation for the script.



Inline examples

Another documentation that is very useful for using scripts (especially if you want to use it years after you wrote it ...) are usage examples. For this purpose the script template contains code to extract the examples included in the script source using the parameter "-X":

echo " -----------------------------------------------------------------------------------------------------" >&2
echo "                         ${__SCRIPTNAME} ${__SCRIPT_VERSION} ">&2
echo "                          Documentation" - Examples>&2
echo " -----------------------------------------------------------------------------------------------------" >&2

             grep "^##EXAMPLE##" "$0" | cut -c12- 1>&2
             die 0 ;;


To use this feature add the examples to the script prefixed with '##EXAMPLE##', e.g.

##EXAMPLE## 
##EXAMPLE##  Mount the boot archive for the current platform
##EXAMPLE## 
##EXAMPLE##     ./view_bootarchive.sh
##EXAMPLE## 
##EXAMPLE##  Mount a specific boot archive
##EXAMPLE## 
##EXAMPLE##     ./view_bootarchive.sh mount /var/tmp/other_boot_archive
##EXAMPLE## 
##EXAMPLE##  Umount the boot archive for the current platform
##EXAMPLE## 
##EXAMPLE##     ./view_bootarchive.sh umount
##EXAMPLE## 

Than you can just issue a

./scriptt.sh -X

to get the usage examples.



Naming conventions


To distinguish between predefined global variables and script specific variables and ensure that I can replace the general routines in existing scripts with updated routines from the template if necessary, the names of all global variables are prefixed with a "__", e.g.

## __MUST_BE_ROOT (def.: false)
## set to ${__TRUE} for scripts that must be executed by root only
##
__MUST_BE_ROOT=${__FALSE}

## __REQUIRED_USERID (def.: none)
## required userid to run this script (other than root); use blanks to separate multiple userids
## e.g. "oracle dba sysdba"
## "" = no special userid required
##
__REQUIRED_USERID=""

Therefore you should not use variables beginning with "__" in scripts based on the script template.


Because it's not always clear in scripts if 0 is "okay" and 1 is "not okay" or the other way round the script template defines two constants for this purpose ${__TRUE} and ${__FALSE. Using these constants instead of 0 or 1 makes the code more human readable.


To avoid problems with duplicate variable names all local variables of a function are defined as local variables using "typeset".

To avoid difficult to find errors all variables are used in the format ${varname}.

All parts of the script template that must be filled with "real" code are marked with three question marks "???".



Logging


Logging is essential for a script; especially if the script is called non-interactive like in cronjobs. One problem here is, that you might want to change the logfile name using a parameter for the script but the script already prints messages before the parameter are processed. The solution used for this problem in the script template is to create a temporary logfile direct after the script starts. Than, after the parameter are processed the temporary logfile is either copied to the "real" logfile or deleted - depending on the value for the logfile parameter.

And to make life easier the script template defines some additional routines to log different kind of messages:


LogMsg is the standard function to log a message to STDOUT and to the logfile (if defined). LogInfo, LogWarning, and LogError just add the particular marker string (INFO, WARNING, or ERROR) to the message and use LogMsg to print and log the message. LogOnly logs only to the logfile and not to STDOUT, PrintDotToSTDOUT prints a string (default one dot) to STDOUT without a carriage return if not in verbose mode, and LogIfNotVerbose prints a log message to STDOUT and the logfile but only if the script is called in non-verbose mode. LogInfo only prints messages if the script is called in verbose mode (parameter "-v").

Another useful function for logging is StartStop_LogAll_to_logfile :

The function StartStop_LogAll_to_logfile redirects all output to STDOUT and STDERR from the script and from all executables called by the script into a logfile.

Example usage:

# log all STDOUT and STDERR to the logfile /var/tmp/output.log
#
StartStop_LogAll_to_logfile start "/var/tmp/output.log"

# do what ever you want ...
ls

# to print something to STDOUT while the redirection is in place use the file handle 3
#
echo "This goes to STDOUT" >&3


# to print something to STDERR while the redirection is in place use the file handle 4
#
echo "This goes to STDERR" >&4

# to stop the redirection use
#
StartStop_LogAll_to_logfile stop


To change the command used to print the log messages to STDOUT you may set the variable ${LOGMSG_FUNCTION} before calling the script. The default value of ${LOGMSG_FUNCTION} is "echo".

Example: To use logger instead of echo for printing the log messages use:

LOGMSG_FUNCTION="logger -s -p user.info scriptt.sh"  ./scriptt.sh

The output to STDERR from logger then looks like:

xtrnaw7: scriptt.sh [22.04.2012 12:35:10] scriptt.sh v0.0.1 started on Sun Apr 22 12:35:10 CEST 2012 
xtrnaw7: scriptt.sh [22.04.2012 12:35:10] Reading the config file "/data/develop/scripts/scriptt.conf" ...
xtrnaw7: scriptt.sh [22.04.2012 12:35:10] Using the log file "/var/tmp/scriptt.LOG" 
xtrnaw7: scriptt.sh [22.04.2012 12:35:10] The log file used was "/var/tmp/scriptt.LOG" 
xtrnaw7: scriptt.sh [22.04.2012 12:35:10] scriptt.sh v0.0.1 ended on Sun Apr 22 12:35:10 CEST 2012.
xtrnaw7: scriptt.sh [22.04.2012 12:35:10] The RC is 0.

And in the OS log file you'll find these messages:

Apr 22 12:35:10 t61p xtrnaw7: scriptt.sh [22.04.2012 12:35:10] scriptt.sh v0.0.1 started on Sun Apr 22 12:35:10 CEST 2012 
Apr 22 12:35:10 t61p xtrnaw7: scriptt.sh [22.04.2012 12:35:10] Reading the config file "/data/develop/scripts/scriptt.conf" ...
Apr 22 12:35:10 t61p xtrnaw7: scriptt.sh [22.04.2012 12:35:10] Using the log file "/var/tmp/scriptt.LOG" 
Apr 22 12:35:10 t61p xtrnaw7: scriptt.sh [22.04.2012 12:35:10] The log file used was "/var/tmp/scriptt.LOG" 
Apr 22 12:35:10 t61p xtrnaw7: scriptt.sh [22.04.2012 12:35:10] scriptt.sh v0.0.1 ended on Sun Apr 22 12:35:10 CEST 2012.
Apr 22 12:35:10 t61p xtrnaw7: scriptt.sh [22.04.2012 12:35:10] The RC is 0.


There are also some variables to change the format and type of messages written by the script:

${__NO_HEADERS} - if this variable is set to ${__TRUE} the script does not print the start and end headers

${__NO_TIME_STAMPS} - if this variable is set to ${__TRUE} the messages are not prefixed with a time stamp.

${__USE_COLORS} - if this variable is set to ${__TRUE} the color variables contain the ANSI ESC sequences to change the color of the messages:

# -----------------------------------------------------------------------------
# color variables

#### Foreground Color variables:
#### __COLOR_FG_BLACK, __COLOR_FG_RED,     __COLOR_FG_GREEN, __COLOR_FG_YELLOW
#### __COLOR_FG_BLUE,  __COLOR_FG_MAGENTA, __COLOR_FG_CYAN,  __COLOR_FG_WHITE
####
#### Background Color variables:
#### __COLOR_BG_BLACK, __COLOR_BG_RED,     __COLOR_BG_GREEN, __COLOR_BG_YELLOW
#### __COLOR_BG_BLUE,  __COLOR_BG_MAGENTA, __COLOR_BG_CYAN,  __COLOR_BG_WHITE
####
if [ ${__USE_COLORS} = ${__TRUE} ] ; then
  __COLOR_FG_BLACK="\033[30m"
  __COLOR_FG_RED="\033[31m"
  __COLOR_FG_GREEN="\033[32m"
  __COLOR_FG_YELLOW="\033[33m"
  __COLOR_FG_BLUE="\033[34m"
  __COLOR_FG_MAGENTA="\033[35m"
  __COLOR_FG_CYAN="\033[36m"
  __COLOR_FG_WHITE="\033[37m"

  __COLOR_BG_BLACK="\033[40m"
  __COLOR_BG_RED="\033[41m"
  __COLOR_BG_GREEN="\033[42m"
  __COLOR_BG_YELLOW="\033[43m"
  __COLOR_BG_BLUE="\033[44m"
  __COLOR_BG_MAGENTA="\033[45m"
  __COLOR_BG_CYAN="\033[46m"
  __COLOR_BG_WHITE="\033[47m"

####
#### Colorattributes:
#### __COLOR_OFF, __COLOR_BOLD, __COLOR_NORMAL, - normal, __COLOR_UNDERLINE
#### __COLOR_BLINK, __COLOR_REVERSE, __COLOR_INVISIBLE
####

  __COLOR_BOLD="\033[1m"
  __COLOR_NORMAL="\033[2m"
  __COLOR_UNDERLINE="\033[4m"
  __COLOR_BLINK="\033[5m"
  __COLOR_REVERSE="\033[7m"
  __COLOR_INVISIBLE="\033[8m"
  __COLOR_OFF="\033[0;m"
fi

If ${__USE_COLORS} is set to ${__FALSE} the color strings are all empty strings and therefore no ANSI ESC sequences are used.

The variables ${__INFO_PREFIX}, ${__WARNING_PREFIX}, ${__ERROR_PREFIX}, and ${__RUNTIME_INFO_PREFIX} can be used to change the marker for info messages, warnings, error messages, and runtime info messages.

And there are three variables to change the behaviour of the script regarding printing summaries for warning and error messages at script end:

${__PRINT_LIST_OF_WARNINGS_MSGS} - if this variable is set to ${__TRUE} the script prints a summary with  all warnings at program end.

${__PRINT_LIST_OF_ERROR_MSGS} - if this variable is set to ${__TRUE} the script prints a summary with all error messages at program end.

${__PRINT_SUMMARIES} is a the combined value of the two variables above.

This behaviour is configured with the parameter "-S" of the script:

     -S n  - print error/warning summaries:
              n = 0 no summariess, 1 = print error msgs,
              2 = print warning msgs, 3 = print error and warning mgs
              Current value: ${__PRINT_SUMMARIES}
              Long format: --summaries


To support multiple levels of info messages the script template implements a ${__VERBOSE_LEVEL} variable. This variable is increased each time the parameter "-v" is found. LogInfo can be called with an additional parameter with the requested verbose level for a message. In this case it compares the current ${__VERBOSE_LEVEL} with the verbose level requested for the message. LogInfo only prints the message if the current ${__VERBOSE_LEVEL} is higher than the level requested for the message:

## --------------------------------------
## LogInfo
##
## print a message to STDOUT and write it also to the logfile
## only if in verbose mode
##
## usage: LogInfo [loglevel] message
##
## returns: -
##
## Notes: Output goes to STDERR, default loglevel is 0
##
LogInfo() {
  typeset __FUNCTION="LogInfo"; ${__DEBUG_CODE}

  typeset THISLEVEL=0
 
  if [ ${__VERBOSE_MODE} -eq ${__TRUE} ] ; then
    if [ $# -gt 1 ] ; then
      isNumber $1
      if [ $? -eq ${__TRUE} ] ; then
        THISLEVEL=$1
        shift
      fi
    fi
    [ ${__VERBOSE_LEVEL} -gt ${THISLEVEL} ] && LogMsg "INFO: $*" >&2
  fi
}


for example:

LogInfo   "This message will only be printed if the parameter -v is entered one or more times"
LogInfo 1 "This message will only be printed if the parameter -v is entered two or more times"
LogInfo 2 "This message will only be printed if the parameter -v is entered three or more times"

This feature is used in the function LogRuntimeInfo:

## __RT_VERBOSE_LEVEL - level of -v for runtime messages
##
## e.g. 1 = -v -v is necessary to print info messages of the runtime system
##      2 = -v -v -v is necessary to print info messages of the runtime system
##
##
__RT_VERBOSE_LEVEL=1

# ....

LogRuntimeInfo() {
   typeset __FUNCTION="LogRuntimeInfo"; ${__DEBUG_CODE}
   LogInfo "${__RT_VERBOSE_LEVEL}" "$*"
}


All info messages of the "runtime" system (that is the predefined code in the template) are printed via LogRuntimeInfo and therefore the value of the variable ${__RT_VERBOSE_LEVEL} decides how much times "-v" is necessary before the info messages of the runtime system are printed.


In addtion there are three functions to simplify the logging of binaries or other scripts called by the script :

ExecuteCommand does no logging at all; executeCommandAndLog logs STDOUT and STDERR to the logfile, and executeCommandAndLogSTDERR only logs STDERR to the logfile (useful if you need STDOUT of a command for other purposes).

All executeCommand* functions add the command to execute to the variable ${__SYSCMDS}.  The house keeping routine running at script end will write the contents of the variable ${__SYS_CMDS} to the ${__SYSCMDS_FILE} if the variable ${__SYSCMDS_FILE} is defined.




Redirection of STDOUT and STDERR


The script template checks if the script is running in a session with a tty at startup and sets the variable ${__RUNNING_IN_TERMINAL_SESSION} to ${__FALSE} if not.

If the script is running in a session without an tty and the parameter "-q" was not used all output of the script and all executables used by the script will be redirected to the file

/var/tmp/${0##*/}.STDOUT_STDERRR.$$

This is implemented with this code:

# ----------------------------------------------------------------------
#
# redirect STDOUT and STDERR of the script and all commands executed by
# the script to a file if called in an session without tty
#
if  ! tty -s  ; then
  __RUNNING_IN_TERMINAL_SESSION=${__FALSE}

  if [[ " $* " != *\ --quiet\ * && " $* " != *\ -q\ * ]]  ; then
    __STDOUT_FILE="/var/tmp/${0##*/}.STDOUT_STDERRR.$$"
    echo "${SCRIPTNAME} -- Running in a detached session ... STDOUT/STDERR will be in ${__STDOUT_FILE}" >&2
 
    exec 3>&1
    exec 4>&2
    exec 1>>"${__STDOUT_FILE}"  2>&1
  fi
else
  __RUNNING_IN_TERMINAL_SESSION=${__TRUE}
fi

e.g.

# first execute the script in a session with tty

[xtrnaw7@t540p /data/develop/scripts]$ ./scriptt.sh
[03.12.2017 11:47:10] scriptt.sh v1.0.0 started at Sun Dec  3 11:47:10 CET 2017.
[03.12.2017 11:47:10] Reading the config file "/data/develop/scripts/scriptt.conf" ...
[03.12.2017 11:47:10] Using the log file "/var/tmp/scriptt.log" 
[03.12.2017 11:47:10] This is only sample code!!!
[03.12.2017 11:47:10] __RUNNING_IN_TERMINAL_SESSION is 0
[03.12.2017 11:47:10] Executing an OS command that writes to STDERR ...
ls: cannot access '/dasdfsf': No such file or directory
This is a messageon STDOUT
This is a messageon STDERR
[03.12.2017 11:47:10] The log file used was "/var/tmp/scriptt.log" 
[03.12.2017 11:47:10] scriptt.sh v1.0.0 started at Sun Dec  3 11:47:10 CET 2017 and ended at Sun Dec  3 11:47:10 CET 2017.
[03.12.2017 11:47:10] The time used for the script is 0 minutes and 0 seconds.
[03.12.2017 11:47:10] The RC is 0.
[xtrnaw7@t540p /data/develop/scripts]$

# and this is the same script executed in a session without tty

[xtrnaw7@t540p /data/develop/scripts]$ ./scriptt.sh </dev/null
 -- Running in a detached session ... STDOUT/STDERR will be in /var/tmp/scriptt.sh.STDOUT_STDERRR.7096

[xtrnaw7@t540p /data/develop/scripts]$ cat /var/tmp/scriptt.sh.STDOUT_STDERRR.7096
[03.12.2017 11:47:31] scriptt.sh v1.0.0 started at Sun Dec  3 11:47:31 CET 2017.
[03.12.2017 11:47:31] Reading the config file "/data/develop/scripts/scriptt.conf" ...
[03.12.2017 11:47:31] Using the log file "/var/tmp/scriptt.log" 
[03.12.2017 11:47:31] This is only sample code!!!
[03.12.2017 11:47:31] __RUNNING_IN_TERMINAL_SESSION is 1
[03.12.2017 11:47:31] Executing an OS command that writes to STDERR ...
ls: cannot access '/dasdfsf': No such file or directory
This is a messageon STDOUT
This is a messageon STDERR
[03.12.2017 11:47:31] The log file used was "/var/tmp/scriptt.log" 
[03.12.2017 11:47:31] scriptt.sh v1.0.0 started at Sun Dec  3 11:47:31 CET 2017 and ended at Sun Dec  3 11:47:31 CET 2017.
[03.12.2017 11:47:31] The time used for the script is 0 minutes and 0 seconds.
[03.12.2017 11:47:31] The RC is 0.
[xtrnaw7@t540p /data/develop/scripts]$


Another method to log all output of the script to STDOUT or STDERR is used if the parameter -T (or --tee) is used -- see the secion about the builtin tee functionality below




Builtin tee functionality

The script template also implements an extended tee functionality: that is, all output written to STDOUT and STDERR by the script and all executables called by the script are automatically logged to a file (similar to calling the script with ./scriptt.sh 2>&1 | tee ./scriptt.output ).  To use this feature just call the script with the parameter "-T".

The default file used for this purpose is 

/var/tmp/${0##*/}.$$.tee.log}

e.g. for scriptt.sh this would be

/var/tmp/scriptt.sh.<pid>.tee.log

To change the file used for this feature use

__TEE_OUTPUT_FILE=<tee_output_file> ./scriptt.sh -T


Examples

xtrnaw7@t61p:/data/develop/scripts$ ./scriptt.sh -T
Saving STDOUT and STDERR to "/var/tmp/scriptt.sh.32090.tee.log" ...
[15.10.2011 10:23:52] scriptt.sh v0.0.1 started on Sat Oct 15 10:23:52 CEST 2011 
[15.10.2011 10:23:52] Reading the config file "/data/develop/scripts/scriptt.conf" ...
[15.10.2011 10:23:52] Using the log file "/var/tmp/scriptt.LOG" 
[15.10.2011 10:23:52] The log file used was "/var/tmp/scriptt.LOG" 
[15.10.2011 10:23:52] scriptt.sh v0.0.1 ended on Sat Oct 15 10:23:52 CEST 2011.
[15.10.2011 10:23:52] The RC is 0.
STDOUT and STDERR saved in "/var/tmp/scriptt.sh.32090.tee.log".


xtrnaw7@t61p:/data/develop/scripts$ __TEE_OUTPUT_FILE=/var/tmp/mylog.txt ./scriptt.sh -T
Saving STDOUT and STDERR to "/var/tmp/mylog.txt" ...
[15.10.2011 10:25:22] scriptt.sh v0.0.1 started on Sat Oct 15 10:25:22 CEST 2011 
[15.10.2011 10:25:22] Reading the config file "/data/develop/scripts/scriptt.conf" ...
[15.10.2011 10:25:22] Using the log file "/var/tmp/scriptt.LOG" 
[15.10.2011 10:25:22] The log file used was "/var/tmp/scriptt.LOG" 
[15.10.2011 10:25:22] scriptt.sh v0.0.1 ended on Sat Oct 15 10:25:22 CEST 2011.
[15.10.2011 10:25:22] The RC is 0.
STDOUT and STDERR saved in "/var/tmp/mylog.txt".



Runtime Initialisation


Before calling the main code the script template initializes the predefined variables and executes some common code:

Environment checks


Some scripts only run on specific hardware or software versions or they must be run by root. Therefore the script template implementes the following checks


The checks are only done if the appropriate variables are defined, these are:

## __MUST_BE_ROOT (def.: false)
##   set to ${__TRUE} for scripts that must be executed by root only
##
__MUST_BE_ROOT=${__FALSE}

## __REQUIRED_USERID (def.: none)
##   required userid to run this script (other than root); use blanks to separate multiple userids
##   e.g. "oracle dba sysdba"
##   "" = no special userid required
##
__REQUIRED_USERID=""

#### __REQUIRED_ZONES - required zones (either global, non-global or local
####    or the names of the valid zones)
####   (def.: none)
####   "" = no special zone required
####
__REQUIRED_ZONES="global"


## __ONLY_ONCE (def.: false)
##   set to ${__TRUE} for scripts that can not run more than one instance at the same time
##
__ONLY_ONCE=${__FALSE}

## __REQUIRED_OS_VERSION (def.: none)
##   minimum OS version necessary, e.g. 5.10
##   "" = no special version necessary
##
__REQUIRED_OS_VERSION=""


## __REQUIRED_MACHINE_PLATFORM (def.: none)
##   required machine platform (uname -i) , e.g "i86pc"; use blanks to separate
##   the machine types if more than one entry, e.g "Sun Fire 3800 i86pc"
##   "" = no special machine type necessary
##
__REQUIRED_MACHINE_PLATFORM=""


## __REQUIRED_MACHINE_CLASS (def.: none)
##   required machine class (uname -m) , e.g "i86pc" ; use blanks to separate
##   the machine types if more than one entry, e.g "sun4u i86pc"
##   "" = no special machine class necessary
##
__REQUIRED_MACHINE_CLASS=""


## __REQUIRED_MACHINE_ARC (def.: none)
##   required machine architecture (uname -p) , e.g "i386" ; use blanks to separate
##   the machine types if more than one entry, e.g "sparc i386"
##   "" = no special machine architecture necessary
##
__REQUIRED_MACHINE_ARC=""

# .....

  if [ ${__MUST_BE_ROOT} -eq ${__TRUE} ] ; then
    UserIsRoot || die 249 "You must be root to execute this script"
  fi

  if [ "${__REQUIRED_USERID}"x != ""x ] ; then
    pos " ${__USERID} " " ${__REQUIRED_USERID} " &&
      die 242 "This script can only be executed by one of the users: ${__REQUIRED_USERID}"
  fi
       
  if [ ${__ONLY_ONCE} -eq ${__TRUE} ] ; then
    CreateLockFile
    if [ $? -ne 0 ] ; then
      cat <<EOF

  ERROR:

  Either another instance of this script is already running
  or the last execution of this script crashes.
  In the first case wait until the other instance ends;
  in the second case delete the lock file
 
      ${__LOCKFILE}

  manually and restart the script.

EOF

#...


The script template also checks which shell is running the script and sets some variables depending on the type of the running shell:

${__KSH_VERSION} is either 88 if the shell is ksh88 compatible or 93 if the shell is ksh93 compatible. Only one new feature of ksh93 is checked for this decision but that should be sufficient. Depending on the value of ${__KSH_VERSION} the variable ${__USE_ONLY_KSH88_FEATURES} is either set to ${__TRUE} for ksh88 compatible shells or ${__FALSE} for ksh93 compatible shells.

The variable ${__SHELL} holds the name of the shell running this script, e.g. ksh, sh, bash, etc.

The variable ${__TRACE_ACTIVE} is set to ${__TRUE} if the script is called with tracing enabled (ksh -x ...)

And the variable ${__SETOPTS} contains all current ksh settings.


OS and shell dependent settings

The script template sets these OS or shell dependent settings for compatibility reasons:

OS
Shell
setting
comment
cygwin
all
set +o noclobber
all
bash
shopt -s expand_aliases

all
all
LANG=C
LANG=C will only be used to initialize the runtime systems; before executing the main code the variable LANG will be restored with the original value
all
non ksh shell
the function whence is defined
only if there is no builtin function called whence





To add additional OS dependent settings search for "specific settings for the various operating systems and shells" in the source code; to add additional shell dependent settings serach for "specific settings for various shells".


Restrict a script to run only once

To ensure that the script is only running in one session at a time set the variable ${__ONLY_ONCE} to ${__TRUE}.

The code to ensure that not more than one instance of the script is running at the same time is a little bit tricky. Important here is to use a check that is known to be atomic. Currently I use code contributed by WPollock (see credits) for this purpose but the "old code" using "ln" is still available in the script and can be activated by setting the local variable ${__USE_OLD_CODE} in the function CreateLockFile to ${__TRUE}:

# --------------------------------------
#### CreateLockFile
#
# Create the lock file (which is really a symbolic link if using the "old method") if possible
#
# usage: CreateLockFile
#
# returns: 0 - lock created
#          1 - lock already exist or error creating the lock
#
# Note: The old method uses a symbolic link because this is should always be a atomic operation
#
function CreateLockFile {
  typeset __FUNCTION="CreateLockFile";    ${__FUNCTION_INIT} ; ${__DEBUG_CODE}
 
# for compatibilty reasons the old code can still be activated if necessary
  typeset __USE_OLD_CODE=${__FALSE}
 
  typeset LN_RC=""

  LogRuntimeInfo "Trying to create the lock semaphore ..."
  if [ ${__USE_OLD_CODE} = ${__TRUE} ] ; then   
# old code using ln 
    ln -s  "$0" "${__LOCKFILE}" 2>/dev/null
    LN_RC=$?
  else   
    __INSIDE_CREATE_LOCKFILE=${__TRUE}
   
# improved code from wpollock (see credits)
    set -C  # or: set -o noclobber
    : > "${__LOCKFILE}" 2>/dev/null
    LN_RC=$?
    __INSIDE_CREATE_LOCKFILE=${__FALSE}
  fi
 
  if [ ${LN_RC} = 0 ] ; then
    __LOCKFILE_CREATED=${__TRUE}
    return 0
  else
    return 1
  fi
 
}



Using RBAC in Solaris 10

RBAC is a Solaris 10 feature to configure the access rights of a user or program - in principle somehow like "sudo" but with a much better granuality.

To enable RBAC control for the script these variables can be used:

# -----------------------------------------------------------------------------
#### __USE_RBAC - set this variable to ${__TRUE} to execute this script
####   with RBAC
####   default is ${__FALSE}
####
####   Note: You can also set this environment variable before starting the script
####
: ${__USE_RBAC:=${__FALSE}}

# -----------------------------------------------------------------------------
#### __RBAC_BINARY - pfexec binary
####  
####   default is /usr/bin/pfexec
####
####   Note: You can also set this environment variable before starting the script
####
: ${__RBAC_BINARY:=/usr/bin/pfexec}

...

# -----------------------------------------------------------------------------
#
# Set the variable ${__USE_RBAC} to ${__TRUE} to activate RBAC support
#
# Allow the use of RBAC to control who can access this script. Useful for
# administrators without root permissions
#
if [ "${__USE_RBAC}" = "${__TRUE}" ] ; then
  if [ "$_" != "${__RBAC_BINARY}" -a -x "${__RBAC_BINARY}" ]; then
    __USE_RBAC=${__FALSE} "${__RBAC_BINARY}" $0 $*
    exit $?
  else
    echo "${0%%*/} ERROR: \"${__RBAC_BINARY}\" not found or not executable!" >&2
    exit 238
  fi
fi


Now if you want to activate RBAC control for a script just set the variable ${__USE_RBAC} to ${__TRUE}. Note that you can also add the automatic call via sudo instead of pfexec using this feature by changing the variable ${__RBAC_BINARY} to the fully qualified path of the sudo binary.


Predefined variables


The script template initializes the IMHO most frequently used variables:

Variable
Value
Comment
${__SCRIPTNAME}
name of the script without the path

${__SCRIPTDIR} path of the script (as entered by the user!)

${__REAL_SCRIPTDIR} path of the script (real path, maybe a link)

${__ABSOLUTE_SCRIPTDIR} path of the script (symbolic links resolved)

${__HOSTNAME} hostname (uname -n)

${__NODENAME} nodename (either /etc/nodename or hostname if the file does not exist)

${__OS} Operating system (uname -s )

${__OS_FULLNAME} Operating system name, in Solaris this is the name from the file /etc/release

${__ZONENAME} name of the current zone if running under Solaris 10 or newer

${__OS_RELEASE} Operating system release (uname -v)

${__MACHINE_CLASS} Machine class (e.g. sun4u, uname -m)

${__MACHINE_PLATFORM} Machine type (e.g. SUNW,Ultra-4, uname -i)

${__MACHINE_SUBTYPE} Machine type (e.g. Sun Fire 3800)
This variable is always empty now because prtdiag is to slow on newer machines like the M5000
${__MACHINE_ARC} Machine architecture (e.g. sparc, uname -p)

${__START_DIR} working directory at start of the script

${__LOGIN_USERID} ID of the user opening the session (who am I)

${__USERID} ID of the user executing the script  (/usr/ucb/whoami)

${__RUNLEVEL} current runlevel of the operating system

${__DEF_LOGFILE} default logfile to use

${__COLOR_FG_*} ANSI ESC sequences to change the foreground color
only set if ${__USE_COLOR} is set to ${__TRUE}
${__COLOR_BG_*} ANSI ESC sequences to change the background color
only set if ${__USE_COLOR} is set to ${__TRUE}
${__COLOR_*} ANSI ESC sequences to set the attributes bold, normal, underline, blink, reverse, and invisible
only set if ${__USE_COLOR }is set to ${__TRUE}



${__SHEBANG} The shebang line (= the first line) of the script

${__SCRIPT_SHELL} the shell/binary used in the shebang line

${__SCRIPT_SHELL_OPTIONS} the options used in the shebang line

${__SHELL} the shell/binary currently executing the script












Using configuration files


The script template also supports a config file to be as much flexible as possible. The config file is searched and read (if found) at script start. The default name of the config file is

<scriptname_without_extension>.conf

E.g. the default config file name for scriptt.sh is scriptt.conf. The script template searches the config file in this order

  1. use the config file in the current directory if it exists
  2. use the config file in the home directory of the user running the script if it exists
  3. use the config file in /etc if it exists


Only one config file is read.


Because the config file is read before processing the parameter this syntax must be used to change the name of the config file at runtime :

CONFIG_FILE=<configfile_name> ./scriptt.sh

e.g.

xtrnaw7@t61p:/data/develop/scripts$ CONFIG_FILE=./test.conf ./scriptt.sh
[15.10.2011 11:08:44] scriptt.sh v0.0.1 started on Sat Oct 15 11:08:44 CEST 2011 
[15.10.2011 11:08:44] Reading the config file "./test.conf" ...
[15.10.2011 11:08:44] Using the log file "/var/tmp/scriptt.LOG" 
[15.10.2011 11:08:44] The log file used was "/var/tmp/scriptt.LOG" 
[15.10.2011 11:08:44] scriptt.sh v0.0.1 ended on Sat Oct 15 11:08:44 CEST 2011.
[15.10.2011 11:08:44] The RC is 0.


To disable the use of the config file use

CONFIG_FILE=none ./scriptt.sh

or set the variable ${__CONFIG_FILE} in the script to "none".

To write a configuration file with the default values use

./scriptt.sh -C
 
e.g.:

[16.10.2011 10:59:02] scriptt.sh v0.0.1 started on Sun Oct 16 10:59:02 CEST 2011 
[16.10.2011 10:59:02] Using the log file "/var/tmp/scriptt.LOG" 
[16.10.2011 10:59:02] Creating the config file "scriptt.conf" ...
[16.10.2011 10:59:02] Creating a backup of "scriptt.conf" in "scriptt.conf.12057.backup" ...
[16.10.2011 10:59:02] Writing the config file "scriptt.conf" ...
[16.10.2011 10:59:03] Configfile "scriptt.conf" successfully written.
[16.10.2011 10:59:03] The log file used was "/var/tmp/scriptt.LOG" 
[16.10.2011 10:59:03] scriptt.sh v0.0.1 ended on Sun Oct 16 10:59:03 CEST 2011.
[16.10.2011 10:59:03] The RC is 0.

You can now edit the configuration file scriptt.conf to the values you need.

Implementation Details

I don't want to maintain duplicate code - one for the initialization of the variables in the script and one for the processing of the configuration file. Because of this the config file is implemented via the source-in functionality of ksh.

In detail:

The configuration variables are defined in the variable ${__CONFIG_PARAMETER}:


## __CONFIG_PARAMETER
##   The variable __CONFIG_PARAMETER contains the configuraton variables
##
# The defaults for these variables are defined here. You
# can use a config file to overwrite the defaults.
#
# Use the parameter -C to create a default configuration file
#
# Note: The config file is read and interpreted via ". configfile" -> you can add also some code her!
#
_CONFIG_PARAMETER="__CONFIG_FILE_VERSION=\"${__SCRIPT_VERSION}\"
"'

# extension for backup files

DEFAULT_BACKUP_EXTENSION=".$$.backup"

# ??? example variables for the configuration file - change to your need

# master server with the directories to synchronize
# The rsync daemon must run either on this host or on localhost
# If the rsync daemon runs on localhost the master server must export
# the directories to synchronize using NFS. In this case the directories
# on the master server must be the same as on the rsync client
#
# overwritten by the parameter -m
  DEFAULT_MASTER_SERVER="linst2"

# server with the rsync daemon. This is either the master server or
# localhost
#
# overwritten by the parameter -s
  DEFAULT_RSYNC_SERVER="localhost"


# only change the following variables if you know what you are doing #

## sample debug code:
## __DEBUG_CODE=" eval echo Entering the subroutine \$__FUNCTION ... "

## Note: Use an include script for more complicate debug code, e.g.
## __DEBUG_CODE=" eval . /var/tmp/mydebugcode"
##

# no further internal variables defined yet
'
# end of config parameters



Because there is no evaluation of variable names in strings in single quotes (' ') you don't have to think about escaping the special characters here. And you can also add code to execute in a config file if necessary.


Now, to initialize the variables in the script a simple

eval "${__CONFIG_PARAMETER}"

is necessary.


And the code to read and execute a configuration file is

. "${THIS_CONFIG_FILE}"


To write a config file with default values the code is

cat <<EOT >"${THIS_CONFIG_FILE}"
# config file for ${__SCRIPTNAME} ${__SCRIPT_VERSION}, created $( date )
${__CONFIG_PARAMETER}
EOT
THISRC=$?


This functionality is used if the script is called with the parameter "-C".


That's it - very simple and straight forward.

For more indepth information please study the functions ReadConfigFile and WriteConfigFile in the source code.


Note:

To be able to distinguish between the default value and the current value of a variable all variables in the configuration and config file are prefixed with DEFAULT_.

After reading the default configuration and the config file (if it exists) all variables from the configuration are copied into the "real" variables for further processing using this code:

# to process all variables beginning with DEFAULT_ use
#
  for CURVAR in $( set | grep "^DEFAULT_"  | cut -f1 -d"=" ) ; do
    P1="${CURVAR%%=*}"
    P2="${P1#DEFAULT_*}"

# for debugging
#    push_and_set __VERBOSE_MODE ${__TRUE}
#    push_and_set __VERBOSE_LEVEL ${__RT_VERBOSE_LEVEL}
#    LogInfo 0 "Setting variable $P2= \"$( eval "echo \"\$$P1\"")\" "
#    pop __VERBOSE_MODE
#    pop __VERBOSE_LEVEL
       
    eval "$P2="\"\$$P1\"""
  
  done




Housekeeping using a trap handler


One thing a lot of scripts are missing is the house keeping at script end, e.g. delete temporary files, directories, etc.

Therefor house keeping is already implemented in the script template. For this purpose the script template discussed her defines these variables:


Variable
Value
Comment
${__LIST_OF_TMP_MOUNTS}
mount points that should be umounted at script end

${__LIST_OF_TMP_DIRS}
directories that should be removed at script end

${__LIST_OF_TMP_FILES}
files that should be removed at script end
this variable is initialized with the names of the default temporary files
${__EXITROUTINES}
functions that should be called at script end before the mounts, directories, and files are removed
use the format

exitroutine:parameter1:parameter2[..:parameter#]

to use parameter for an exit routine.

The function can check the variable ${__INSIDE_EXIT_ROUTINE} to check if it's running as exit routine

if [ ${__INSIDE_EXIT_ROUTINE} = ${__TRUE} ] ; then
  LogMsg "Running as exit routine ..."
fi
${__FINISHROUTINES}
functions that should be called at script end after the mounts, directories, and files are removed use the format

finishroutine:parameter1:parameter2[..:parameter#]

to use parameter for a finish routine.

The function can check the variable ${__INSIDE_FINISH_ROUTINE} to check if it's running as finish routine

if [ ${__INSIDE_FINISH_ROUTINE} = ${__TRUE} ] ; then
  LogMsg "Running as finish routine ..."
fi


${__PROCS_TO_KILL} processes that should be killed at script end the format for the entries in this list is:

pid[:timeout]

pid is the PID of the process to kill; timeout is the time in seconds to wait for the process to stop until a "kill -9" is issued. Use "pid:-1" to disable the "kill -9" for a process. The default value for timeout for all processes is ${__PROCS_KILL_TIMEOUT}.


To use these variables for the house keeping simply add the new item to the list, e.g. to ensure that a temporary directory is always removed at script end use:

TMPDIR="/tmp/mydir.$$"
mkdir "${TMPDIR}"  && __LIST_OF_TMP_DIRS="${__LIST_OF_TMP_DIRS} ${TMPDIR}" || die 2 "Error creating the directory"



Please note that the variables for house keeping do not support file, directory or function names with white spaces (blanks, tabs). You should only append values to these variables and not overwrite them because they may also be used by the runtime system.

The variables for house keeping are evaluated and processed by the routine cleanup which is called in the function die.

The house keeping is done in this order:

  1. call the exit routines from ${__EXITROUTINES}
  2. kill all process from ${__PROCS_TO_KILL}
  3. remove the files from ${__LIST_OF_TMP_FILES}
  4. umount the mount points ${__LIST_OF_TMP_MOUNTS}
  5. remove the directories ${__LIST_OF_TMP_DIRS}
  6. call the finish routines from ${__FINISHROUTINES}


There are some additional variables to configure the house keeping behaviour (for example for debugging purpose):

Variable
takes care of
Default value
Comment
${__NO_CLEANUP}
general cleanup
${__FALSE}

${__NO_EXIT_ROUTINES}
exit routines
${__FALSE}
${__NO_KILL_PROCS} kill processes ${__FALSE}
${__NO_TEMPFILES_DELETE}
delete temporary files
${__FALSE}
${__NO_TEMPDIR_DELETE}
delete temporary directories
${__FALSE}
${__NO_TEMPMOUNTS_UMOUNT}
umount temporary mount points
${__FALSE}
${__NO_FINISH_ROUTINES}
finish routines
${__FALSE}
${__CLEANUP_ON_ERROR}
do cleanup in case of an error
${__FALSE}


You may set one or more of the variables __NO_* variables at runtime to ${__TRUE} to suppress one or more of the house keeping actions. To disable or enable all house keeping actions at once you can use the function SetHousekeeping:


#### --------------------------------------
#### SetHousekeeping
####
#### do or do not house keeping (remove tmp files/directories; execute exit routines/finish routines) at script end
####
#### usage: SetHousekeeping [${__TRUE}|${__FALSE}]
####
#### parameter: ${__TRUE} - do house keeping
####            ${__FALSE} - no house keeping
####
#### returns:  0 - okay
####           1 - invalid usage
####
####


To use the house keeping feature it's necessary to exit the script always using the function die. To make sure that the function die is also called in case of an error, trap handlers are used:

# install trap handler
    trap "GENERAL_SIGNAL_HANDLER ERR   \$LINENO" ERR
    trap "GENERAL_SIGNAL_HANDLER  1    \$LINENO"  1
    trap "GENERAL_SIGNAL_HANDLER  2    \$LINENO"  2
    trap "GENERAL_SIGNAL_HANDLER  3    \$LINENO"  3
    trap "GENERAL_SIGNAL_HANDLER 15    \$LINENO" 15
    trap "GENERAL_SIGNAL_HANDLER exit  \$LINENO" EXIT



This must be done in every function also. Therefore there is a variable defined with the approviate code to install the trap handler

#### __FUNCTION_INIT - code executed at start of every sub routine
####   (see the hints for __DEBUG_CODE)
####         Default init code : install the trap handlers
####
#  __FUNCTION_INIT=" eval __settrap; echo  \"Now in function \${__FUNCTION}\" "
  __FUNCTION_INIT=" eval __settrap "


and it's important for this feature to work that every function starts with this code:


function ReadConfigFile {
  typeset __FUNCTION="ReadConfigFile"; ${__FUNCTION_INIT} ; ${__DEBUG_CODE}

....

and ends with this code:

  ${__FUNCTION_EXIT}
  return ${THISRC}


Note:

${__DEBUG_CODE} is another variable that can be used to define code that should be executed at start of every function, ${__FUNCTION_EXIT} can be used to define code that should be used at end of every function.


Note 1:

The most simple method to add your own function to the script is to copy the function YourRoutine and change it (replace YourRoutine with the name of your function and add the code to the body of the function):

### --------------------------------------
#### YourRoutine
####
#### template for a user defined function
####
#### usage: YourRoutine
####
#### returns:  ${__TRUE} - ok
####           ${__FALSE} - error
####           255 - invalid usage
####         
####
function YourRoutine {
  typeset __FUNCTION="YourRoutine";   ${__FUNCTION_INIT} ; 
  ${__DEBUG_CODE}
   
# init the return code
  typset THISRC=${__FALSE}


# add code here

  ${__FUNCTION_EXIT}
  return ${THISRC}

}

Be aware that the return code of a function must be in the range from 0 to 255. To "return" other data you must either use global variables or print the results to STDOUT and read that in the calling function.


Additional features of the trap handlers


The trap handler also implements the handling of CTRL-C via the variable ${__USER_BREAK_ALLOWED}.
To suppress CTRL-C just set the variable ${__USER_BREAK_ALLOWED} to ${__FALSE}.

The other predefined trap handlers are:

The signal SIGHUP will toggle the verbose switch, the signal handler for the signal SIGUSR1 writes a dump of the current environment (see below), and the signal SIGUSR2 calls the DebugShell.


User defined trap handler

The trap handler also supports user defined trap handler. The  variables must contain the function name for the trap handler; the variables used for the user defined trap handlers are

Variable
SIGNAL
default behaviour
comment
${__GENERAL_SIGNAL_FUNCTION}
(all)

If this signal handler returns 0 a signal specific signal handler is called if defined. If this handler returns a value not equal zero no other signal handler is called.
${__SIGNAL_SIGUSR1_FUNCTION}
USR1
write a dump of the current environment

${__SIGNAL_SIGUSR2_FUNCTION} USR2
call the DebugShell

${__SIGNAL_SIGHUP_FUNCTION} HUP
toggle the verbose switch

${__SIGNAL_SIGINT_FUNCTION}
INT
process user breaks (CTRL-C)
in the default the user trap handler for CTRL-C calls the function DebugShell if enabled
${__SIGNAL_SIGQUIT_FUNCTION}
QUIT


${__SIGNAL_SIGTERM_FUNCTION}
TERM



To implement a user defined trap handler add the function for the trap handler and assign the name of the function to the approbiate variable.

E.g. to define a user defined trap handler for the signal SIGQUIT do:


# define the function for the trap signal, for example called my_sigquit_handler
#
# than set the variable for this trap handler

 __SIGNAL_SIGQUIT_FUNCTION="my_sigquit_handler"


That's it. Now your user defined trap handler is called if the signal SIGQUIT is received. Depending on the return code of the user defined trap handler the default trap handler for that trap will be called (returncode = 0) or not (returncode <> 0).

If you use one function for more then one trap handler you can check the variable ${__TRAP_SIGNAL} to get the current trap.

An example user defined trap handler is also included; you can use it as template for your own trap handler:


#### --------------------------------------
#### USER_SIGNAL_HANDLER
####
#### sample user defined trap handler
####
#### usage: __SIGNAL_<signal>_FUNCTION="USER_SIGNAL_HANDLER"
####
####        e.g. __SIGNAL_SIGUSR1_FUNCTION="USER_SIGNAL_HANDLER"
####
#### returns:  0 - execute the default action for this signal
####           else - do not execute the default action for this signal
####
####
function USER_SIGNAL_HANDLER {
  typeset THISRC=0
 
  LogMsg "***"
  LogMsg "User defined signal handler called"
  LogMsg ""
  LogMsg "Trap signal is \"${__TRAP_SIGNAL}\" "
  LogMsg "Interrupted function: \"${INTERRUPTED_FUNCTION}\", Line No: \"${__LINENO}\" "
  LogMsg "***"

  return ${THISRC}
}



Crash dumps

The script template supports some kind of "crash dump" for post mortem debugging. The "crash dump" is created in case of an error. It's also possible to manually create a "crash dump" at any time while the script is running.

The "crash dump" is really "only" a copy the current variables of the running process but in a lot of cases this is sufficient to find the bug in a script. To make this feature even more usefull you should save as much information as possible in variables. Example:

Instead of

ps -ef | grep "myprocess" >/dev/null
if [ $? -eq 0 ] ; then
 ...

use

PS_EF_OUTPUT="$( ps -ef )"
GREP_OUTPUT="$( echo "${PS_EF_OUTPUT}" | grep "myprocess" )"
if [ $? -eq 0 ] ; then

  ...


Using this code the output of the ps command and also the output of the grep command will be in the crash dump.


There are two files created for each crash dump:

<crashdumpdir>/<scriptname>.sh.envvars.$$
<crashdumpdir>/<scriptname>.exported_envvars.$$

e.g. the filenames for scriptt.sh are:

<crashdumpdir>/scriptt.sh.envvars.$$
<crashdumpdir>/scriptt.sh.exported_envvars.$$


Please note that the default signal handler for the signal USR1 will create a crash dump on request:

The default action for the signal handler USR1 is "Create an env dump in /var/tmp".  The filenames for the dumps are

/var/tmp/<scriptname>.envvars.dump_no_<no>_<PID>
/var/tmp/<scriptname>.exported_envvars.dump_no_<no>_<PID>

where <no> is a sequential number, <PID> is the PID of the process with the script,  and <scriptname> is the name of the script without the path.


The variables and functions to use the crash dump feature are

${__CREATE_DUMP} - if this variable contains the name of an existing directory a dump will be created at program end in that directory (even if the scripts ends without error). If ${__CREATE_DUMP} is not empty but does not contain the name of an existing directory the dump will be created in the directory /tmp. If this variable is empty no automatic dump will be created at normal program end.

${__DUMPDIR} - this variable defines the location for the dump in case of an error.

${__DUMP_ALREADY_CREATED} - set this variable to 0  to suppress creating the dump in case of an error.


The function  CreateDump can be used in the script to manually create a crash dump.


Example usage oft the crash dump feature:

__CREATE_DUMP=1 ./scriptt.sh


will create a dump of the environment variables in the files

   /tmp/scriptt.sh.envvars.$$
   /tmp/scriptt.sh.exported_envvars.$$


before the script ends (with or without error).


__CREATE_DUMP=/var/tmp/debug ./scriptt.sh

will create a dump of the environment variables in the files

   /var/tmp/debug/scriptt.sh.envvars.$$
   /var/tmp/debug/scriptt.sh.exported_envvars.$$


before the script ends (the directory /var/tmp/debug must already exist).


To change the directory for the crash dumps that are created in case of an script error to /var/core use:

export __DUMPDIR=/var/core  ; ./scriptt.sh


In your script use

CreateDump <uniqdirectory> [filename_add]

to manually create a crash dump.

e.g.

CreateDump /var/debug

 will create the files

   /var/debug/scriptt.sh.envvars.$$
  /var/debug/scriptt.sh.exported_envvars.$$


This call
  
CreateDump /var/debug pass2.

will create the files

   /var/debug/scriptt.sh.envvars.pass2.$$
   /var/debug/scriptt.sh.exported_envvars.pass2.$$



Another function to save only some environment variables to a file is SaveEnvironmentVariables. Example usage:

# save all environment variables beginning with "GNOME"
#
SaveEnvironmentVariables "/var/tmp/envvars.out" "^GNOME"

# save all environment variables that contain MAN or SSH in their name:
#
SaveEnvironmentVariables "/var/tmp/envvars1.out" "MAN" "SSH"



The function SaveEnvironmentVariable uses egrep to select the environment variables to save.



Include files


To make sure that all features of the script template also work for sourced-in files you should always use the function includeScript or tryIncludeScript to source-in another file. Therefore use

includeScript scriptname

or

tryIncludeScript scriptname

instead of

. scriptname

to include another script file.


Temporary files

A lot of scripts, if not all, need temporary files. Therefore the script template contains code to create temporary files that are automatically deleted at program end. In the default configuration the script template defines 2 temporary files. The variables used for the names of the temporay files are

${__TEMPFILE1}
${__TEMPFILE2}

So you can just use them without thinking of the house keeping for these files, e.g.

vxprint >"${__TEMPFILE1}"


The variables that control this functionality are:

${__TEMPDIR} - this variable contains the name of the directory for temporary files. It's initialized with the contents of the environment variable $TEMP or $TMP if set.

${__NO_OF_TEMPFILES} - this variable defines the number of temporary files that should be created automatically, the default is 2.

${__TEMPFILE_MASK} - this variable contains the umask for the temporary files




Running the script as daemon

To run as a daemon the script template also contains code to write a PID file with the PID of the current process running the script.  To use this feature add the name of the PID file to use to the variable ${__PIDFILE}:

#### __PIDFILE - save the pid of the script in a file
####
#### example usage: __PIDFILE="/tmp/${__SCRIPTNAME%.*}.pid"
__PIDFILE=""

Note: If the script you're writing is allowed to run more than once at the same time you should use a unique filename for each run; something like

__PIDFILE="/var/run/${__SCRIPTNAME%.*}.$$.pid"




Predefined parameter


Some parameter are the same for all scripts and it make sense to define them in the script template. The standard ksh shell in Solaris supports long parameters (beginning with "--") so the script template also supports them.

The predefined parameter in this script template are:

Parameter

Long format

Meaning

-v

--verbose

turn verbose mode on
Use "-v -v" to get also the verbose messages from the runtime system

-q

--quiet

turn quiet mode on

-y

--yes

assume yes to all questions

-n

--no

assume no to all questions

-a

--color

use colors

-O

--overwrite

overwrite existing files

-f

--force

force the execution

-l logfile

--logfile

use "logfile" as logfile

-h

--help

show a short usage and exit immediately

-h -v

--help --verbose

show a long usage and exit immediately

-H

--doc

print the documentation to STDERR and exit immediately

-X
--view_examples
print the usage examples to STDERR and exit immediately

-D

--debug
used for additional debug parameter  - see below

Please note that the debugger functionality  from scriptt.sh v1.x is not implemented anymore in v2.x or later; use scriptt v1.x if you need it

-S n

--summaries

print error message and warning message summaries at script end, n can be
0 - print no summaries (default)
1 - print error message summaries
2 - print warning message summaries
3 - print error and warning message summaries

-C

--writeconfigfile

write a default config file and exit immediately

-V

--version

write the version number to STDOUT and exit immediately ; use "-v -V" to also print the scriptt template version used for the script

-T
--tee
append STDOUT and STDERR to a file


The parameters "-v", "-q", "-y", "-n", "-a", "-O", and "-f" can also be used with a plus sign, e.g. "+v", "+q", to turn off the feature. This applies also to the parameter in long format, e.g. "++verbose" instead of "--verbose" to turn of verbose mode.

The parameter -o sets the variable ${__OVERWRITE_MODE}. This variable is checked by the function BackupFileIfNecessary. So before changing an existing file just call

BackupFileIfNecessary filename

and be sure there will be a backup of the file you're going to change if requested (via parameter "-o" / "+o" ).

The parameter are processed sequential in the order entered, e.g.

./scriptt.sh -v -q -v

In this example verbose is turned on because the parameter "-v" is the last parameter.


To add further parameter do the following tasks:

- add the parameter to the variable ${__SHORT_USAGE_HELP}

- add the parameter description to the variable ${__LONG_USAGE_HELP}

- add the parameter to the parameter string for getopts ${__GET_OPTS}. Note: There are two definitions for ${__GET_OPTS}: one for shells that support long parameter and one for shells that do not support long parameter:

  __GETOPTS="+:ynvqhHDfl:aOS:CVTX"
  if [ "${__OS}"x = "SunOS"x -a "${__SHELL}"x = "ksh"x ] ; then
    if [ "${__OS_VERSION}"x  = "5.10"x -o  "${__OS_VERSION}"x  = "5.11"x ] ; then
      __GETOPTS="+:y(yes)n(no)v(verbose)q(quiet)h(help)H(doc)D(debug)f(force)l:(logfile)a(color)O(overwrite)S:(summaries)C(writeconfigfile)V(version)T(tee)X(view_examples)"
    fi
  fi

- add the code to process the parameter in the "while getopts .." loop:

while getopts ${__GETOPTS} CUR_SWITCH  ; do
...
    case ${CUR_SWITCH} in
...
      "S" ) case ${OPTARG} in

                0 | 1 | 2 | 3 ) __PRINT_SUMMARIES=${OPTARG}
                                    ;;

                * )  LogError "Unknown value for -S found: \"${OPTARG}\""
                      INVALID_PARAMETER_FOUND=${__TRUE}
                      ;;
                esac
                ;;


# ??? add additional parameter here
    
        \? ) LogError "Unknown parameter found: \"${OPTARG}\" "
             INVALID_PARAMETER_FOUND=${__TRUE}
             break
          ;;
...
    esac
  done

The remaining parameter after evaluating the default parameter are saved in the variable ${NOT_PROCESSED_PARAMETER}.



Debugging


The script supports the parameter  "-D extended_option" to add other not so often used parameter to the script, e.g -D msg  or -D debugcode="x".

To get a list of the currently implemented extended options use ./scriptt.sh -D help, e.g.


[xtrnaw7@t540p /data/develop/scripts]$ ./scriptt.sh -D help
[03.12.2017 12:15:31] scriptt.sh v1.0.0 started at Sun Dec  3 12:15:31 CET 2017.
[03.12.2017 12:15:31] Reading the config file "/data/develop/scripts/scriptt.conf" ...
Known debug switches (for -D / --debug):

  help          -- show this usage and exit
  create_documentation
                -- create the script documentation
  list_rc       -- list return codes used by this script
                   Works only if you only use "die" to end the script
  msg           -- log debug messages to the file /tmp/scriptt.sh.8039.debug
                   This parameter should be the first parameter.
  trace         -- activate tracing to the file /tmp/scriptt.sh.8039.trace
  tracemain     -- trace the main function
  fn_to_stderr  -- print the function names to STDERR
  fn_to_tty     -- print the function names to /dev/tty
  fn_to_handle9 -- print the function names to the file handle 9
  fn_to_device=filename
                -- print the function names to the file "filename"
  debugcode="x" -- execute the debug code "x" at every function start
                   CAUTION: The debug code should NOT write to
                            STDOUT - use STDERR instead
  printargs     -- print the script arguments
  tracefunc=f1[,...,f#]
                -- enable tracing for the functions f1 to f#
  debug[=cmd]   -- exeucte the command "cmd" or call a very simple cmd loop
  DebugShell    -- start the DebugShell
  setvar:name=value
                -- set the variable "name" to "value"
  listfunc      -- list all functions defined and exit
  showdefaults  -- show the default variable values
  create_dump=dirname
                -- enable environment dumps; target directory is dirname
  SyntaxHelp    -- print syntax usage examples for the functions in the template
                   and exit
  dryrun        -- dry run only, do not execute commands
  dryrun=prefix -- dry run only, add the prefix "prefix" to all commands

  nocleanup     -- disable the house keeping at script end
  cleanup[=type]
                -- disable or enable the house keeping at script end; "type" can be
                     all - enable all house keeping (this is the default)
                     none - disable all house keeping
                     nodelete - disable only removing of temporary files and directories

[03.12.2017 12:15:31] The log file used was "/tmp/scriptt.sh.8039.TEMP" 
[03.12.2017 12:15:31] The debug messages are logged to "/tmp/scriptt.sh.8039.debug" 
[03.12.2017 12:15:31] scriptt.sh v1.0.0 started at Sun Dec  3 12:15:31 CET 2017 and ended at Sun Dec  3 12:15:31 CET 2017.
[03.12.2017 12:15:31] The time used for the script is 0 minutes and 0 seconds.
[03.12.2017 12:15:31] The RC is 0.
[xtrnaw7@t540p /data/develop/scripts]$


The predefined extended options are:

Option
Usage
help
print the list of known extended options
create_documentation
creates the documentation for the script
list_rc
list return codes used by the script and exit
Note: To use this feature for your code always use the function die to end the script
msg
log debug messages to a log file, to use this feature use the function LogDebugMsg in your code
trace
activate tracing (set -x ) for the script, the trace message are written to a separate log file, e.g.

[root@t540p xtrnaw7]# /data/develop/scripts/scriptt.sh -D trace
[16.08.2015 11:35:45] scriptt.sh v1.0.0 started at Sun Aug 16 11:35:45 CEST 2015.
[16.08.2015 11:35:45] No config file ("scriptt.conf") found (use -C to create a default config file)
[16.08.2015 11:35:45] Using the log file "/var/tmp/scriptt.LOG" 
[16.08.2015 11:35:45] The log file used was "/var/tmp/scriptt.LOG" 
[16.08.2015 11:35:45] The trace messages are logged to "/tmp/scriptt.sh.8001.trace" 
[16.08.2015 11:35:45] scriptt.sh v1.0.0 started at Sun Aug 16 11:35:45 CEST 2015 and ended at Sun Aug 16 11:35:45 CEST 2015.
[16.08.2015 11:35:45] The time used for the script is 0 minutes and 0 seconds.
[16.08.2015 11:35:45] The RC is 0.


tracemain activate tracing for the main function only
fn_to_stderr
print the name of every function executed to STDERR
fn_to_tty
print the name of every function executed to /dev/tty
fn_to_handle9
print the name of every function executed to the file handle 9; to use this feature call the script like this:

[root@t540p xtrnaw7]# /data/develop/scripts/scriptt.sh -D fn_to_handle9 9>/tmp/file9
[16.08.2015 11:38:10] scriptt.sh v1.0.0 started at Sun Aug 16 11:38:10 CEST 2015.
[16.08.2015 11:38:10] No config file ("scriptt.conf") found (use -C to create a default config file)
[16.08.2015 11:38:10] Using the log file "/var/tmp/scriptt.LOG" 
[16.08.2015 11:38:10] The log file used was "/var/tmp/scriptt.LOG" 
[16.08.2015 11:38:10] scriptt.sh v1.0.0 started at Sun Aug 16 11:38:10 CEST 2015 and ended at Sun Aug 16 11:38:10 CEST 2015.
[16.08.2015 11:38:10] The time used for the script is 0 minutes and 0 seconds.
[16.08.2015 11:38:10] The RC is 0.



fn_to_device=filename
print the name of every function executed to the file "filename"
debugcode="x"
execute the code "x" at every function start

Note: It's a little bit tricky to use variables in the code for "x"
printargs print the arguments of the script while processing them
tracefunc=f1[...,f#]
enable tracing (set -x) for one or more functions
debug[=cmd]
exeucte the command "cmd" or start a very simple debug shell
DebugShell call the DebugShell
setvar:name=value
set the variable "name" to the value "value", e.g. to temporary disable the cleanup at script end you can use:

./scriptt.sh -D setvar:__NO_CLEANUP=0

listfunc
list all functions defined in the script and exit
create_dump=dirname
enable environment dumps to the target directory "dirname"
SyntaxHelp
print some usage help for using the features of the script template
dryrun
dryrun=<newcode>
do a dry run; only print the commands to execute to STDOUT
To use this feature in your code prefix every call of a binary or script with the variable ${PREFIX}; the default value for ${PREFIX} is "echo ";
to change the value for ${PREFIX} use "dryrun=<newcode>"
nocleanup
disable the house keeping at script end
cleanup[=type]
disable or enable the house keeping at script end; "type" can be
all - enable all house keeping (this is the default)
none - disable all house keeping
nodelete - disable only the removing of temporary files and directories





Adding other extended options


To add another extended option do


- add the usage help for the new extended options to the help text in the function ProcessDebugSwitch


- add another statement to the case in the function ProcessDebugSwitch; for simple debug options like "-D myaction" use

    <myaction>  )
        DEBUG_PARAMETER_OKAY=${__TRUE}
        # your code for this action
         ;;


  for debug options like "-D keyword=value" use


     <keyword>=* )
        DEBUG_PARAMETER_OKAY=${__TRUE}
        <value>="${CUR_DEBUG_SWITCH#*=}"
       ;;


  Note: The code DEBUG_PARAMETER_OKAY=${__TRUE} is mandatory for every new statement in the case block.




Environment variables


The script template uses the environment variables from the table below if defined before running the script. Some of the variables can also be set with a parameter. In this case the value from the parameter overwrites the value from the environment variable.


Environment variable
script parameter
comment
${  LOGMSG_FUNCTION}

${__DEBUG_CODE}

${__RT_VERBOSE_LEVEL}

 ${__QUIET_MODE} -q

${__VERBOSE_MODE} -v

${__VERBOSE_LEVEL}

${__OVERWRITE_MODE} -O

${__USER_BREAK_ALLOWED}

${__NO_TIME_STAMPS}

${__NO_HEADERS}

${__USE_COLORS} -a

${__USE_RBAC}

${__RBAC_BINARY}

${__TEE_OUTPUT_FILE}

${_DEBUG_PREFIX}

${__INFO_PREFIX}

${__WARNING_PREFIX}

${__ERROR_PREFIX}

${__RUNTIME_INFO_PREFIX}

${__NO_CLEANUP}

${__NO_KILL_PROCS}

${__PROCS_KILL_TIMEOUT}

${__NO_EXIT_ROUTINES}

${__NO_TEMPFILES_DELETE}

${__NO_TEMPMOUNTS_UMOUNT}

${__NO_TEMPDIR_DELETE}

${__NO_FINISH_ROUTINES}

${__CLEANUP_ON_ERROR}

${__CREATE_DUMP}

${__DUMP_ALREADY_CREATED}

${_DUMPDIR}

${__USE_ONLY_KSH88_FEATURES}




${CONFIG_FILE}

${TMP / TEMP}
default for ${__TEMPDIR} if set (in this order)


Return codes

The script template uses the return codes 1, 2, and 210 to 254. You should not use these return codes in your code.

The list of predefined return codes for the script template is:

Predefined return codes:

    RC Error Message
    0 "Configfile "${NEW_CONFIG_FILE}" successfully written."
    0 ok, no error
    1 show usage and exit
    2 invalid parameter found
  228 "There is an error in an include script"
  229 Script aborted by the user
  230  "${SCRIPTFILE} does not exist or is not readable"
  231 "Can not write to the file "${CUR_VAR}""
  232 "Function SyntaxHelp NOT defined."
  233 "Can not write to file handle 9"
  234 "The return value ${THISRC} is greater than 255 in function "${__FUNCTION}""
  234 "The return value is greater than 255 in function "${__FUNCTION}""
  235 "Invalid debug switch found: "${CUR_DEBUG_SWITCH}" -- use "-d help" to list the known debug switches"
  236 "You should use the function "die" to end the program"
  237 "Can not write to the debug log file "${__DEBUG_LOGFILE}" "
  238 "This script can not run on this operating system (${__OS}); known Operating systems are "${__REQUIRED_OS}""
  239 "This script can not run in the global zone"
  239 "This script must run in one of the zones "${__REQUIRED_ZONES}"; the current zone is "${__ZONENAME}" "
  239 "This script must run in the global zone; the current zone is "${__ZONENAME}""
  240 "internal error: IncludeScript called without parameter"
  240 "internal error: TryIncludeScript signaled error ${__INCLUDE_SCRIPT_RC}"
  242 "This script can only be executed by one of the users: ${__REQUIRED_USERID}"
  243 "This script can not run on this machine architecture (${__MACHINE_ARC}); necessary machine architectures are "${__REQUIRED_MACHINE_ARC}""
  244 "This script can not run on this machine class (${__MACHINE_CLASS}); necessary machine classes are "${__REQUIRED_MACHINE_CLASS}""
  245 "This script can not run on this platform (${__MACHINE_PLATFORM}); necessary platforms are "${__REQUIRED_MACHINE_PLATFORM}""
  246 "Error writing the config file "${NEW_CONFIG_FILE}""
  247 "Include script "$1" not found"
  248 "Unsupported OS Version: ${__OS_VERSION}; necessary OS version is ${__REQUIRED_OS_VERSION}"
  249 "You must be root to execute this script"
  250 Script is already running
  251 "QUIT signal received"
  252 "Script aborted by the user via signal BREAK (CTRL-C)"
  253 "Script aborted by the external signal SIGTERM"
  254 "${SCRIPTNAME} aborted by the user"
  254 "Unknown signal caught: ${__TRAP_SIGNAL}"

 



Functions defined in the script template


The function AskUser

The function AskUser can be used to get some input from the user, the usage is

AskUser "message"

AskUser returns ${__TRUE} if the user input is yes and ${__FALSE} if the user input is no. The string entered by the user is available in the variable ${USER_INPUT}. The user input from the previous call to AskUser is  available in the variable ${LAST_USER_INPUT}.

If the parameter -y was used AskUser will always return ${__TRUE} and never prompt for user input. If the parameter -n was used AskUser will always return ${__FALSE} and never prompts for user input.

There are some variables defined for the behaviour of AskUser:

Variable
Description
Comment
${__DEBUG_SHELL_IN_ASKUSER} if this variable is true the user can call the DebugShell in AskUser
${__NOECHO} turn echo off while reading input from the user (e.g. to enter a password)
${__USE_TTY} if this variable is true all output in AskUser goes to /dev/tty and all input is read from /dev/tty

AskUser uses the standard read command so the input must be followed by <RETURN>. To only read one key from the user use the function GetKeystroke instead.


The function BackupFileIfNecessary

The function BackupFileIfNecessary can be used to create a backup of an existing file, the usage is

BackupFileIfNecessary [file1} ... {filen}

The format for the parameter is

filename[,no_of_backups]

BackupFileIfNecessary will create a backup of an existing file if the variable ${__OVERWRITE_MODE} (parameter -o/+o) is ${__FALSE}. The function keeps up to no_of_backups old versions of the file to backup, e.g.

This code

  __VERBOSE_LEVEL=1

  __VERBOSE_MODE=${__TRUE}

  for i in 1 2 3 4 5 6 7 8 ; do
    BackupFileIfNecessary "${TESTFILE}",5
    echo "This is version $i of the file">"${TESTFILE}"
  done

  __VERBOSE_MODE=${__FALSE}


produces this output :

[xtrnaw7@t540p /data/develop/scripts]$ ./scriptt.sh
[03.12.2017 14:08:24] scriptt.sh v1.0.0 started at Sun Dec  3 14:08:24 CET 2017.
[03.12.2017 14:08:24] Reading the config file "/data/develop/scripts/scriptt.conf" ...
[03.12.2017 14:08:24] Using the log file "/var/tmp/scriptt.log" 
[03.12.2017 14:08:24] This is only sample code!!!
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.1" to "./testfile.2" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.2" to "./testfile.3" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.1" to "./testfile.2" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.3" to "./testfile.4" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.2" to "./testfile.3" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.1" to "./testfile.2" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Removing the old backup file "./testfile.4" 
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.3" to "./testfile.4" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.2" to "./testfile.3" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.1" to "./testfile.2" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Removing the old backup file "./testfile.4" 
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.3" to "./testfile.4" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.2" to "./testfile.3" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.1" to "./testfile.2" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] RUNTIME INFO: Creating up to 5 backups of the file "./testfile" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Removing the old backup file "./testfile.4" 
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.3" to "./testfile.4" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.2" to "./testfile.3" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.1" to "./testfile.2" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Renaming "./testfile.0" to "./testfile.1" ...
[03.12.2017 14:08:24] RUNTIME INFO:   Copying "./testfile" to "./testfile.0" ...
[03.12.2017 14:08:24] The log file used was "/var/tmp/scriptt.log" 
[03.12.2017 14:08:24] scriptt.sh v1.0.0 started at Sun Dec  3 14:08:24 CET 2017 and ended at Sun Dec  3 14:08:24 CET 2017.
[03.12.2017 14:08:24] The time used for the script is 0 minutes and 0 seconds.
[03.12.2017 14:08:24] The RC is 0.


And the files contain:


[xtrnaw7@t540p /data/develop/scripts]$ for i in 0 1 2 3 4 ; do echo "" ; echo "File $i contains \"$( cat testfile.$i )\" " ; done

File 0 contains "This is version 7 of the file"

File 1 contains "This is version 6 of the file"

File 2 contains "This is version 5 of the file"

File 3 contains "This is version 4 of the file"

File 4 contains "This is version 3 of the file"
[xtrnaw7@t540p /data/develop/scripts]$


String handling functions


Coming from a more string oriented language like REXX one thing that is really missing in ksh are string manipulating functions. Therefore I wrote some functions for doing this. The functions use internal ksh functions (like pattern matching, typeset, etc) as much as possible to avoid the use of external binaries like sed, or awk.

All function returning a string either return the value in a variable or print it to STDOUT; this is done with the code:

  if [ "$4"x != ""x ] ; then
    eval $4=\"${resultstr}\"
  else
    echo "${resultstr}"
  fi


The string handling functions defined are:



Data convertion functions


Also missing in standard ksh scripts are functions for converting data. The functions defined for this purpose in the script template are:



UID related functions

Other often used functions are those to process UID and usernames:

Note:

These functions may or may not work as expected on other operating systems -- please test them before using.


Functions to implement a FIFO Stack

There are also functions to implement a simple LIFO stack. This is a very handy feature to temporary save the contents of variables.

The functions for the stack handling are:


Example usage of these functions:

# for debugging
    push_and_set __VERBOSE_MODE ${__TRUE}
    push_and_set __VERBOSE_LEVEL ${__RT_VERBOSE_LEVEL}
    LogInfo 0 "Setting variable $P2= \"$( eval "echo \"\$$P1\"")\" "
    pop __VERBOSE_LEVEL
    pop __VERBOSE_MODE




Misc functions


There are other functions defined in the script template that may or may not be useful (Note that some of them are used by the runtime system - so  be carefull if removing unused functions). Use

./scriptt.sh -H 2>./scriptt.txt

to create the documentation which lists also all implemented functions.

Other functions defined in this version of the script template:



Other techniques used in the script template

Define a function if it's not already defined

In ksh you can define a function if it's not already defined (either explicit or as builtin function). I use this technique to define the function whence it's not a builtin function in the used shell:

# new definition for whence:
#
whence whence 2>/dev/null 1>/dev/null || function whence {
  typeset __FUNCTION="whence"; ${__FUNCTION_INIT} ; ${__DEBUG_CODE}
  typeset THISRC=1

  if typeset +f $1 1>/dev/null ; then
    echo $1 ; THISRC=0
  elif alias $1 2>/dev/null 1>/dev/null  ; then
    echo $1 ; THISRC=0
  else
    which $1 2>/dev/null ; THISRC=$?
  fi

  ${__FUNCTION_EXIT}
  return ${THISRC}
}

Note:

Another method to check if a function is defined is

if typeset +f >/dev/null ; then

(see the source code for the function IsFunctionDefined)



Parameter handling

Some of the parameter for the script template must be evaluated before the general parameter processing is done (e.g the parameter -q or -T ) The code for this feature just checks the variable $0, e.g.

# -----------------------------------------------------------------------------
# process the parameter -q or --quiet
#
if [[ \ $*\  == *\ -q* || \ $*\  == *\ --quiet\ * ]] ; then
  __NO_HEADERS=${__TRUE}
  __QUIET_MODE=${__TRUE}
fi



Variable intializing

The code used in the script template for initializing variables that can also  be set via environment variables before starting the script use some handy features of the ksh, e.g.:

#### __NO_FINISH_ROUTINES - do not execute the finish routeins at script end if ${__TRUE}
####
: ${__NO_FINISH_ROUTINES:=${__FALSE}}

The nope statement ":" does nothing here but the variable ${NO_FINISH_ROUTES} will be set to ${__FALSE} if it's not already set.



Function templates


There are some function templates in the script template which should you use for your own functions to make sure all template features also work for your new functions:

YourRoutine - use this function as template for your function(s)

USER_SIGNAL_HANDLER - use this function as template for a user defined trap handler


The DebugShell

The function DebugShell can be used to debug a running script; just call the function DebugShell in your code at any time.

In the default configuration DebugShell is also called if CTRL-C is pressed and in the trap handler for the signal USR2.  To call the DebugShell you can also use the parameter -D DebugShell.

In principle DebugShell is only a loop that reads input from the user and executes it. DebugShell knows some aliase and interprets everything else as an OS command and executes it via eval.
DebugShell only works if the script is called in an interactive terminal session; all output is written to /dev/tty and all input is only read from /dev/tty.

DebugShell online help


[14.10.2017 17:23:04] Using the log file "/var/tmp/scriptt.log" 
Press return to continue ...
*** Debug Shell called via CTRL-C ***

 -------------------------------------------------------------------------------
scriptt.sh - debug shell (called via CTRL-C) - enter a command to execute ("exit" to leave the shell)
  defined aliasse: functions = list all defined functions, vars [help|var_list] = print global variables,
                   quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                   cont = continue script execution
>> help

Enter either an defined alias or an OS command to execute

Defined aliasse are:

functions                     - print all defined functions
func f1 [...f#]               - view the source code for the functions f1 to f#
                                (supported by this shell: yes)

add_debug_code f1 [...f#]     - add debug code to the functions f1 to f#; use all for f1 to add
                                debug code to all functions
                                (supported by this shell: yes)
                     
view_debug                    - view the current debug code
set_debug f1 [...f#]          - enable tracing for the functions f1 to f#
                                Note: The existing trace definitions will be overwritten!
clear_debug                   - disable tracing for all functions

vars help                     - print defined variable lists
vars var_list                 - print the variable from the variable list 'var_list'
vars all                      - print all variables

verbose                       - toggle the verbose switch (current value is: 1, __VERBOSE_LEVEL is 0)
break                         - toggle the break switch (current value is: 0)

exit                          - exit the shell
quit                          - exit the script
abort                         - exit the script with 'kill -9'

cont                          - continue the script execution

Everthing else is interpreted as an OS command


 -------------------------------------------------------------------------------
scriptt.sh - debug shell (called via CTRL-C) - enter a command to execute ("exit" to leave the shell)
  defined aliasse: functions = list all defined functions, vars [help|var_list] = print global variables,
                   quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                   cont = continue script execution

>> vars help

Known variable lists are:

      all               - print all variables used

      application       - print application variables
      used_env          - print environment variables used
      log               - print variables for the logfile handling
      defaults          - print variables with DEFAULT_* values
      config            - print variables for the config file processing
      house_keeping     - print variables for the housekeeping processing
      signalhandler     - print variables for the signal handler
      dump              - print variables for the dump processing
      script            - print variables with the script name, directory, etc
      debug             - print variables for the debug functions
      parameter         - print variables for the parameter
      requirements      - print variables for the script requirements
      runtime           - print various runtime variables
      os_env            - print variables for the OS environment
      internal          - print all internal variables execept these variables
                            __LONG_USAGE_HELP __SHORT_USAGE_HELP
                            __OTHER_USAGE_EXAMPLES __CONFIG_PARAMETER


 -------------------------------------------------------------------------------
scriptt.sh - debug shell (called via CTRL-C) - enter a command to execute ("exit" to leave the shell)
  defined aliasse: functions = list all defined functions, vars [help|var_list] = print global variables,
                   quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                   cont = continue script execution
>>


Known aliase in the DebugShell


functions - list all functions defined in the script

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> functions
AskUser
BackupFileIfNecessary
CheckInputDevice
CheckYNParameter
ConvertDateToEpoc
ConvertMinutesToHours
ConvertToBinary
ConvertToHex
ConvertToOctal
...
toUppercase
tryIncludeScript

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


func - view the source code of a function

Note: This functionality is implemented using "typeset -f functionname" which is not supported by all ksh versions

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> func pos
pos
function pos {
  typeset __FUNCTION="pos";    ${__FUNCTION_INIT} ; ${__DEBUG_CODE}

  typeset searchstring="$1"
  typeset sourcestring="$2"
  typeset THISRC=0

  if [[ "${sourcestring}" == *${searchstring}* ]] ; then
    typeset f="${sourcestring%%${searchstring}*}"
    THISRC=$((  ${#f}+1 ))
  fi

  [ ${THISRC} -gt 255 ] && die 234 "The return value ${THISRC} is greater than 255 in function \"${__FUNCTION}\""

  ${__FUNCTION_EXIT}
  return ${THISRC}
}


 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


add_debug_code - add code to enable tracing to one or more functions

This alias adds the string "typeset _FUNCTION=<functioname>; ${__DEBUG_CODE}" as first statements to a function

Note: This functionality is implemented using "typeset -f functionname" which is not supported by all ksh versions


view_debug - view the current value for ${__DEBUG_CODE}

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>  view_debug
The current debug code for all functions (__DEBUG_CODE) is:
eval [ 0 = 1  -o "${__FUNCTION}"x = "pos"x  ] && printf "
*** Enabling trace for the function ${__FUNCTION} ...
" >&2 && set -x

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


set_debug - add code to enable tracing for one or more functions

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> set_debug pos substr
Enabling debug code for the functions "pos substr" now
 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> view_debug
The current debug code for all functions (__DEBUG_CODE) is:
eval [ 0 = 1  -o "${__FUNCTION}"x = "pos"x  -o "${__FUNCTION}"x = "substr"x  ] && printf "
*** Enabling trace for the function ${__FUNCTION} ...
" >&2 && set -x

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> pos aa bbbXXaaYY

*** Enabling trace for the function pos ...
+ searchstring=aa
+ typeset searchstring
+ sourcestring=bbbXXaaYY
+ typeset sourcestring
+ THISRC=0
+ typeset THISRC
+ [[ bbbXXaaYY == *aa* ]]
+ f=bbbXX
+ typeset f
+ THISRC=6
+ [ 6 -gt 255 ]
+ return 6

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


clear_debug - clear the debug code

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> clear_debug
Clearing the debug code now ...

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> view_debug
The current debug code for all functions (__DEBUG_CODE) is:


 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


vars help  - list all defined variable lists for the alias vars

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> vars help

Known variable lists for the alias "vars" are:

      all               - print all variables used

      application       - print application variables
      used_env          - print environment variables used
      log               - print variables for the logfile handling
      defaults          - print variables with DEFAULT_* values
      config            - print variables for the config file processing
      house_keeping     - print variables for the housekeeping processing
      signalhandler     - print variables for the signal handler
      dump              - print variables for the dump processing
      script            - print variables with the script name, directory, etc
      debug             - print variables for the debug functions
      parameter         - print variables for the parameter
      requirements      - print variables for the script requirements
      runtime           - print various runtime variables
      os_env            - print variables for the OS environment
      internal          - print all internal variables execept these variables
                            __LONG_USAGE_HELP __SHORT_USAGE_HELP
                            __OTHER_USAGE_EXAMPLES __CONFIG_PARAMETER


 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


vars <variable_list> - view the contents of all variables in the list <variable_list>

e.g

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> vars log
*** Logging variables:
  __DEF_LOGFILE: "/var/tmp/scriptt.log"
  __LOGFILE: "/tmp/scriptt.sh.16552.TEMP"
  __DEBUG_PREFIX: "DEBUG: "
  __INFO_PREFIX: "INFO:"
  __WARNING_PREFIX: "WARNING: "
  __ERROR_PREFIX: "ERROR: "
  __RUNTIME_INFO_PREFIX: "RUNTIME INFO: "
  __NO_TIME_STAMPS: "1"
  __NO_HEADERS: "1"
  __NOECHO: "1"
  __USE_TTY: "1"
  __NO_OF_WARNINGS: "0"
  __LIST_OF_WARNINGS: ""
  __NO_OF_ERRORS: "0"
  __LIST_OF_ERRORS: ""

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


verbose - switch the verbose mode

e.g.

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> verbose
Toggling the verbose mode now ...
The verbose mode is now 0

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> verbose
Toggling the verbose mode now ...
The verbose mode is now 1

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>


break - enable or disable CTRL-C

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> break
Toggling the break mode now ...
The break mode is now 1

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>> break
Toggling the break mode now ...
The break mode is now 0

 -------------------------------------------------------------------------------
scriptt.sh - debug shell - enter a command to execute ("exit" to leave the shell)
  defined aliase: functions = list all defined functions, vars [help|var_list] = print global variables,
                  quit = exit the script, abort = abort the script with kill -9, use help for short usage help
                 
>>

exit - exit the DebugShell and continue script execution


quit - exit the script using the function die


abort - exit the script using "kill -9 $$"


cont - continue the script execution

This alias is only valid if DebugShell is called via CTRL-C.



For more indepth documentation for the function DebugShell see   http://bnsmb.de/solaris/scriptt_mini.html.



Links


The source code of the script template: http://bnsmb.de/files/public/solaris/templates/scriptt.sh


The source code of the 1.x version of scriptt.sh is here:  http://bnsmb.de/files/public/solaris/templates/scriptt.sh.1.x

A test suite for the script template: http://bnsmb.de/files/public/solaris/templates/scriptt_testsuite.sh

Output of scriptt.sh -Hhttp://bnsmb.de/files/public/solaris/templates/scriptt.txt

A mini version of scriptt.sh is here:  http://bnsmb.de/solaris/scriptt_mini.html



Scripts based on scriptt.sh

These scripts are based on scriptt.sh (be aware that most of them are based on an older version of scriptt.sh):


view_bootarchive.sh

dtrace_syscalls

create_zones.sh

execute_on_all_hosts.sh



Back to top