5.8. Command Substitutions

Currently, the ability to parameterize strings to have ConsoleUser substitute values at runtime is only used by the CommandShell Markov Brain.

ConsoleUser supports command substitution in that strings it uses can include sequences that cause a value to be retrieved from elsewhere and used in that spot. Without this ability every string is treated as a literal set of instructions to type. For example, when doing tasks in the _CommandShell module ConsoleUser will execute each string as it is passed to it. So the CommandShellBrain would need to load a different command to do host www.zulu.isp and host web.susilva.isp.

Using command substitution we can have ConsoleUser “know” the host command and substitute names from other parameters at runtime. To do this we use square brackets to set off tokens that indicate where the expanded value should come from. If, for example, ConsoleUser has a config value with the name lookupSites that contains a list with both of the sites used above, we could specify a command to use them as host [lookupSites]. When that command is selected the lookupSites name triggers ConsoleUser to lookup a value to put in that spot.

The way the values are looked up is to first see if the brain instance itself has an instance value matching that name. If it does not, check if the _User instance has one, or if its _UserConfig has it. If any of those lookups find a matching value ConsoleUser will derive the string to type from it. To do this, ConsoleUser will see if the value is a basic string, like the user’s loginName field generally is for example. If it is, that value is used. If, instead, the value is a list, or similar sequence, a random value will be selected from it (by calling random.choice on it). If it is neither a string nor a list, ConsoleUser will treat it as one of its Chooser instances which typically consume a CDF Line. In that case the next choice will be retrieved from the Chooser and that will be used.

If the name is not found or the value cannot be resolved the original bracketed string will be left in place.

For example, if a user has the following as part of their CommandShell.conf:

CommandShellTasks = 0.5 echo Random Task: [_commands], 0.75 echo [loginName] 1.0 netstat

and the line loginName = consoleUser1 in the user.conf file then when the user begins it will load the CommandShellTasks line into the module brain’s self._commands variable that is a CDFChooser instance, and the User instance will have its config variable set loginName to consoleUser1. When the chooser selects the first option it will then run the command that begins echo Random Task and then it will call the next_choice on the module brain’s _commands chooser instance. Similarly, if it picks the second command it will end up typing the command echo consoleUser1.

To know, or add to, the variables that are available to reference in the substitution block might require inspecting the code. For example, CommandShellBrain includes the following lines:

self._commands = CDFChooser()
self._commands.set_logger(self.logger)
self._commands.parse_line(self.config.CommandShellTasks)

which is how we know there is a _commands variable that we could reference in the config line above. All keys defined the conf files loaded by a _User will end up available as raw strings in the user.config or self.config (same object) variables, but if the value should be a list or other selecting instance like the CDFChooser above, code to parse the value might have to be added.

For example, if we want to define a list of values that could be selected for inclusion in a command, say a list of domains that we may use for commands like host or dig or ping we might want to add the following lines to CommandShellBrain’s __init__ method:

self._domains = []
if self.config.commandDomains:
    self._domains = [x.strip() for x in self.commandDomains.split(',')]

then we could use the following in the config file:

commandDomains = www.zulu.isp, web.susilva.isp, another.host.com
commandShellTasks = 0.5 ping [_domains], 1.0 dig [_domains]

It’s also worth remembering that the keys in the config file, e.g., commandDomains are case insensitive so CommandDomains would also match.