.. _command_substitutions: 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 :ref:`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``: .. code-block:: 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: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: 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. .. _CommandShell: https://skaion.com/cu/api/commandShell.html .. _User: https://skaion.com/cu/api/User.html .. _UserConfig: https://skaion.com/cu/api/UserConfig.html