1
set -o errexit -o pipefail -o noclobber -o nounset
The loader is the script $FW_HOME/bin/shell/shell.sh
.
It provides an interactive shell to either execute shell commands or tasks.
Most shell commands are realized by tasks, so the actual shell implementation is rather simple.
The initial settings are shown in the source block below. Line 1 restricts bash, providing a safer execution environment.
1
set -o errexit -o pipefail -o noclobber -o nounset
The shell then tests if it is run from the right parent (i.e. the loader) and it loads the temporary configuration.
The parent test is rather simple: if FW_HOME
or FW_L1_CONFIG
are not set, it is very likely that the shell has not been started by the loader.
Otherwise, the temporary configuration can be loaded (line 5).
The configuration is set for running in the shell (line 6).
This information is used by the API functions in the console to determine what output (log) level and what error/warning counters to use.
1
2
3
4
5
6
if [[ -z ${FW_HOME:-} || -z ${FW_L1_CONFIG-} ]]; then
printf " ==> please run from framework or application\n\n"
exit 40
fi
source $FW_L1_CONFIG
CONFIG_MAP["RUNNING_IN"]="shell"
Next, the shell includes the framework’s API functions (lines 1-2) and the functions to maintain its command history (line 3). Then the error and warning counters are reset (set to 0), and a simple new line is printed (if messages are allowed).
1
2
3
4
5
6
7
source $FW_HOME/bin/api/_include
source $FW_HOME/bin/api/describe/task.sh
source $FW_HOME/bin/shell/history.sh
ConsoleResetErrors
ConsoleResetWarnings
ConsoleMessage "\n"
Now the shell realizes its core settings. These settings are:
SCMD - used to store shell input
SARG - used to store arguments (only used for history now)
STIME - used to set the time when a command was entered, and later for time calculations
RELOAD_CFG - a flag to indicate if the temporary configuration should be reloaded.
This flag is required for instance when the task set
alters settings.
HISTORY - a map with the history of commands #end::settings[]
1
2
3
4
5
6
SCMD= # a shell-command from input
SARG= # argument(s), if any, for a shell command
STIME= # time a command was entered
RELOAD_CFG=false # flag to reload configuration, e.g. after a change of settings
declare -A HISTORY # the shell's history of executed commands
HISTORY[-1]="help" # dummy first entry, size calculation doesn't seem to work otherwise
The inner loop is an interpreter for all input the shell wants to add to its history. This input might be the request to execute a task, or a command that the shell realizes using a task, or a simple command. The inner loop is defined as a function.
1
2
3
4
5
FWInterpreter() {
case "$SCMD" in
# ...
esac
}
Inside this function, the inner loop, the shell tests first for all input that it can associate to a command. If none of these tests is satisfied, it assumes that the input is actually a task to be executed with parameters.
The first command tested is to execute a scenario.
This command starts with either execute-scenario
or es
.
No argument means error.
Some argument means the name of a scenario.
In this case, execute the scenario and put the command into the history.
1
2
3
4
5
6
7
8
9
10
11
12
13
execute-scenario | es)
printf "\n execute-scenario/rs requires a scenario as argument\n\n"
;;
"execute-scenario "*)
SARG=${SCMD#*execute-scenario }
ExecuteScenario $SARG
ShellAddCmdHistory
;;
"es "*)
SARG=${SCMD#*es }
ExecuteScenario $SARG
ShellAddCmdHistory
;;
This a simple command without arguments. The clear screen functionality is realized by printing the ANSI escape sequence for clear screen.
1
2
3
4
clear-screen | "clear-screen "* | cls | "cls "*)
printf "\033c"
ShellAddCmdHistory
;;
Print the current time.
1
2
3
4
time | "time "* | T | "T "*)
printf "\n %s\n\n" "$STIME"
ShellAddCmdHistory
;;
Execute the task list-configuration
with default settings, which displays a list with the current configuration.
1
2
3
4
configuration | "configuration "* | c | "c "*)
${DMAP_TASK_EXEC["list-configuration"]}
ShellAddCmdHistory
;;
Execute the task statistics
with default settings, which displays an overview of statistic information.
1
2
3
4
statistic | "statistic "* | s | "s "*)
${DMAP_TASK_EXEC["statistics"]}
ShellAddCmdHistory
;;
Execute the task list-tasks
with default settings, which displays an list of loaded tasks.
1
2
3
4
tasks | "tasks "* | t | "t "*)
${DMAP_TASK_EXEC["list-tasks"]}
ShellAddCmdHistory
;;
Execute the task list-tasks
with setting --origin app
, which displays an list of loaded tasks with origin application.
1
2
3
4
tasks-application | "tasks-application "* | ta | "ta "*)
${DMAP_TASK_EXEC["list-tasks"]} --origin app
ShellAddCmdHistory
;;
Do nothing if the input starts with the comment character #
in any variation.
1
2
"" | "#" | "#"* | "# "*)
;;
All other input is interpreted as the request to execute a task with optional arguments.
1
2
3
4
5
6
7
8
9
*)
SARG="$SCMD"
ExecuteTask "$SARG"
ShellAddCmdHistory
case "$SCMD" in
"set "* | "setting "*) RELOAD_CFG=true;;
esac
;;
The outer loop is the shell’s main loop reading input from standard input and interpreting it.
While input is read (a line finished with an enter), the line is read into the variable SCMD
and a time stamp is written into STIME
.
Then SCMD
is evaluated.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FWShell() {
while read -a args; do
SCMD="${args[@]:-}" <&3
STIME=$(date +"%T")
case "$SCMD" in
# ...
esac
if [[ $RELOAD_CFG == true ]]; then
source $FW_L1_CONFIG
CONFIG_MAP["RUNNING_IN"]="shell"
RELOAD_CFG=false
fi
if ConsoleIsPrompt; then ConsoleMessage "${CONFIG_MAP["SHELL_PROMPT"]}"; fi
done
}
Inside this function, the outer loop, the shell tests first for shell commands. If none of these tests is satisfied, it calls the inner loop to deal with the input.
Once finished with the input, the value of RELOAD_CFG
to see if the configuration has to be reloaded.
Finally, a new prompt is displayed.
If help is requested, display the command help file for the current print mode.
1
2
3
help | h | "?")
cat ${CONFIG_MAP["FW_HOME"]}/etc/help/commands.${CONFIG_MAP["PRINT_MODE"]}
;;
History means to print the history or to run a commend stored in the history. Both functionalities are provided by the history function.
1
2
3
4
5
6
7
8
!*)
SARG=${SCMD#*!}
ShellCmdHistory
;;
history*)
SARG=${SCMD#*history}
ShellCmdHistory
;;
If exit is requested, the shell leaves the outer loop.
1
2
3
exit | quit | q | bye)
break
;;
All other input is forwarded to the inner loop for further evaluation.
1
2
3
*)
FWInterpreter
;;
The final lines run the actual shell.
First, input is redirected to #3
while the shell is running (line 1).
This allows to read lines from the standard input into the shell.
Next, the first prompt is displayed (line 2).
Then the outer loop is called (line 3).
When the outer loop is finished, the redirection is reverted (line 4).
Now the shell is finished and the process returns to the loader.
1
2
3
4
exec 3</dev/tty || exec 3<&0
if ConsoleIsPrompt; then ConsoleMessage "${CONFIG_MAP["SHELL_PROMPT"]}"; fi
FWShell
exec 3<&-