Shell

Overview

This module allows you to create and handle a shell with a user-defined command set. You can use it in examples where more than simple button or LED user interaction is required. This module is a Unix-like shell with these features:

  • Support for multiple instances.
  • Advanced cooperation with the Logging.
  • Support for static and dynamic commands.
  • Smart command completion with the Tab key.
  • Built-in commands: clear, shell, colors, echo, history and resize.
  • Viewing recently executed commands using keys: .
  • Text edition using keys: , , Backspace, Delete, End, Home, Insert.
  • Support for ANSI escape codes: VT100 and ESC[n~ for cursor control and color printing.
  • Support for multiline commands.
  • Built-in handler to display help for the commands.
  • Support for wildcards: * and ?.
  • Support for meta keys.
  • Kconfig configuration to optimize memory usage.

The module can be connected to any transport for command input and output. At this point, the following transport layers are implemented:

  • UART
  • Segger RTT
  • DUMMY - not a physical transport layer

Connecting to Segger RTT via TCP (on macOS, for example)

On macOS JLinkRTTClient won’t let you enter input. Instead, please use following procedure:

  • Open up a first Terminal window and enter:

    JLinkRTTLogger -Device NRF52840_XXAA -RTTChannel 1 -if SWD -Speed 4000 ~/rtt.log
    

    (change device if required)

  • Open up a second Terminal window and enter:

    nc localhost 19021
    
  • Now you should have a network connection to RTT that will let you enter input to the shell.

Commands

Shell commands are organized in a tree structure and grouped into the following types:

  • Root command (level 0): Gathered and alphabetically sorted in a dedicated memory section.
  • Static subcommand (level > 0): Number and syntax must be known during compile time. Created in the software module.
  • Dynamic subcommand (level > 0): Number and syntax does not need to be known during compile time. Created in the software module.

Creating commands

Use the following macros for adding shell commands:

Commands can be created in any file in the system that includes include/shell/shell.h. All created commands are available for all shell instances.

Static commands

Example code demonstrating how to create a root command with static subcommands.

Command tree with static commands.
/* Creating subcommands (level 1 command) array for command "demo". */
SHELL_CREATE_STATIC_SUBCMD_SET(sub_demo)
{
        SHELL_CMD(params, NULL, "Print params command.",
                                               cmd_demo_params),
        SHELL_CMD(ping,   NULL, "Ping command.", cmd_demo_ping),
        SHELL_SUBCMD_SET_END /* Array terminated. */
};
/* Creating root (level 0) command "demo" */
SHELL_CMD_REGISTER(demo, &sub_demo, "Demo commands", NULL);

Example implementation can be found under following location: samples/subsys/shell/shell_module/src/main.c.

Dynamic commands

Example code demonstrating how to create a root command with static and dynamic subcommands. At the beginning dynamic command list is empty. New commands can be added by typing:

dynamic add <new_dynamic_command>

Newly added commands can be prompted or autocompleted with the Tab key.

Command tree with static and dynamic commands.
/* Buffer for 10 dynamic commands */
static char dynamic_cmd_buffer[10][50];

/* commands counter */
static u8_t dynamic_cmd_cnt;

/* Function returning command dynamically created
 * in  dynamic_cmd_buffer.
 */
static void dynamic_cmd_get(size_t idx,
                            struct shell_static_entry *entry)
{
        if (idx < dynamic_cmd_cnt) {
                entry->syntax = dynamic_cmd_buffer[idx];
                entry->handler  = NULL;
                entry->subcmd = NULL;
                entry->help = "Show dynamic command name.";
        } else {
                /* if there are no more dynamic commands available
                 * syntax must be set to NULL.
                 */
                entry->syntax = NULL;
        }
}

SHELL_CREATE_DYNAMIC_CMD(m_sub_dynamic_set, dynamic_cmd_get);
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_dynamic)
{
        SHELL_CMD(add, NULL,"Add new command to dynamic_cmd_buffer and"
                  " sort them alphabetically.",
                  cmd_dynamic_add),
        SHELL_CMD(execute, &m_sub_dynamic_set,
                  "Execute a command.", cmd_dynamic_execute),
        SHELL_CMD(remove, &m_sub_dynamic_set,
                  "Remove a command from dynamic_cmd_buffer.",
                  cmd_dynamic_remove),
        SHELL_CMD(show, NULL,
                  "Show all commands in dynamic_cmd_buffer.",
                  cmd_dynamic_show),
        SHELL_SUBCMD_SET_END
};
SHELL_CMD_REGISTER(dynamic, &m_sub_dynamic,
           "Demonstrate dynamic command usage.", cmd_dynamic);

Example implementation can be found under following location: samples/subsys/shell/shell_module/src/dynamic_cmd.c.

Commands execution

Each command or subcommand may have a handler. The shell executes the handler that is found deepest in the command tree and further subcommands (without a handler) are passed as arguments. Characters within parentheses are treated as one argument. If shell wont find a handler it will display an error message.

Commands can be also executed from a user application using any active backend and a function shell_execute_cmd(), as shown in this example:

void main(void)
{
        /* Below code will execute "clear" command on a DUMMY backend */
        shell_execute_cmd(NULL, "clear");

        /* Below code will execute "shell colors off" command on
         * an UART backend
         */
        shell_execute_cmd(shell_backend_uart_get_ptr(),
                          "shell colors off");
}

Enable the DUMMY backend by setting the Kconfig CONFIG_SHELL_BACKEND_DUMMY option.

Command handler

Simple command handler implementation:

static int cmd_handler(const struct shell *shell, size_t argc,
                        char **argv)
{
        ARG_UNUSED(argc);
        ARG_UNUSED(argv);

        shell_fprintf(shell, SHELL_INFO, "Print info message\n");

        shell_print(shell, "Print simple text.");

        shell_warn(shell, "Print warning text.");

        shell_error(shell, "Print error text.");

        return 0;
}

Function shell_fprintf() or the shell print macros: shell_print, shell_info, shell_warn and shell_error can be used from the command handler or from threads, but not from an interrupt context. Instead, interrupt handlers should use Logging for printing.

Command help

Every user-defined command or subcommand can have its own help description. The help for commands and subcommands can be created with respective macros: SHELL_CMD_REGISTER, SHELL_CMD_ARG_REGISTER, SHELL_CMD, and SHELL_CMD_ARG.

Shell prints this help message when you call a command or subcommand with -h or --help parameter.

Parent commands

In the subcommand handler, you can access both the parameters passed to commands or the parent commands, depending on how you index argv.

  • When indexing argv with positive numbers, you can access the parameters.
  • When indexing argv with negative numbers, you can access the parent commands.
  • The subcommand to which the handler belongs has the argv index of 0.
static int cmd_handler(const struct shell *shell, size_t argc,
                       char **argv)
{
        ARG_UNUSED(argc);

        /* If it is a subcommand handler parent command syntax
         * can be found using argv[-1].
         */
        shell_print(shell, "This command has a parent command: %s",
                      argv[-1]);

        /* Print this command syntax */
        shell_print(shell, "This command syntax is: %s", argv[0]);

        /* Print first argument */
        shell_print(shell, "%s", argv[1]);

        return 0;
}

Built-in commands

  • clear - Clears the screen.

  • history - Shows the recently entered commands.

  • resize - Must be executed when terminal width is different than 80 characters or after each change of terminal width. It ensures proper multiline text display and , , End, Home keys handling. Currently this command works only with UART flow control switched on. It can be also called with a subcommand:

    • default - Shell will send terminal width = 80 to the terminal and assume successful delivery.
  • shell - Root command with useful shell-related subcommands like:

    • echo - Toggles shell echo.
    • colors - Toggles colored syntax. This might be helpful in case of Bluetooth shell to limit the amount of transferred bytes.
    • stats - Shows shell statistics.

Wildcards

The shell module can handle wildcards. Wildcards are interpreted correctly when expanded command and its subcommands do not have a handler. For example, if you want to set logging level to err for the app and app_test modules you can execute the following command:

log enable err a*
Wildcard usage example

Meta keys

The shell module supports the following meta keys:

Implemented meta keys
Meta keys Action
Ctrl + a Moves the cursor to the beginning of the line.
Ctrl + b Moves the cursor backward one character.
Ctrl + c Preserves the last command on the screen and starts a new command in a new line.
Ctrl + d Deletes the character under the cursor.
Ctrl + e Moves the cursor to the end of the line.
Ctrl + f Moves the cursor forward one character.
Ctrl + k Deletes from the cursor to the end of the line.
Ctrl + l Clears the screen and leaves the currently typed command at the top of the screen.
Ctrl + u Clears the currently typed command.
Ctrl + w Removes the word or part of the word to the left of the cursor. Words separated by period instead of space are treated as one word.
Alt + b Moves the cursor backward one word.
Alt + f Moves the cursor forward one word.

Usage

To create a new shell instance user needs to activate requested backend using menuconfig.

The following code shows a simple use case of this library:

void main(void)
{

}

static int cmd_demo_ping(const struct shell *shell, size_t argc,
                         char **argv)
{
        ARG_UNUSED(argc);
        ARG_UNUSED(argv);

        shell_print(shell, "pong");
        return 0;
}

static int cmd_demo_params(const struct shell *shell, size_t argc,
                           char **argv)
{
        int cnt;

        shell_print(shell, "argc = %d", argc);
        for (cnt = 0; cnt < argc; cnt++) {
                shell_print(shell, "  argv[%d] = %s", cnt, argv[cnt]);
        }
        return 0;
}

/* Creating subcommands (level 1 command) array for command "demo". */
SHELL_CREATE_STATIC_SUBCMD_SET(sub_demo)
{
        SHELL_CMD(params, NULL, "Print params command.",
                                               cmd_demo_params),
        SHELL_CMD(ping,   NULL, "Ping command.", cmd_demo_ping),
        SHELL_SUBCMD_SET_END /* Array terminated. */
};
/* Creating root (level 0) command "demo" without a handler */
SHELL_CMD_REGISTER(demo, &sub_demo, "Demo commands", NULL);

/* Creating root (level 0) command "version" */
SHELL_CMD_REGISTER(version, NULL, "Show kernel version", cmd_version);

Users may use the Tab key to complete a command/subcommand or to see the available subcommands for the currently entered command level. For example, when the cursor is positioned at the beginning of the command line and the Tab key is pressed, the user will see all root (level 0) commands:

clear  demo  shell  history  log  resize  version

Note

To view the subcommands that are available for a specific command, you must first type a space after this command and then hit Tab.

These commands are registered by various modules, for example:

  • clear, shell, history, and resize are built-in commands which have been registered by subsys/shell/shell.c
  • demo and version have been registered in example code above by main.c
  • log has been registered by subsys/logging/log_cmds.c

Then, if a user types a demo command and presses the Tab key, the shell will only print the subcommands registered for this command:

params  ping

Shell as the logger backend

Shell instance can act as the Logging backend. Shell ensures that log messages are correctly multiplexed with shell output. Log messages from logger thread are enqueued and processed in the shell thread. Logger thread will block for configurable amount of time if queue is full, blocking logger thread context for that time. Oldest log message is removed from the queue after timeout and new message is enqueued. Use the shell stats show command to retrieve number of log messages dropped by the shell instance. Log queue size and timeout are SHELL_DEFINE arguments.

Warning

Enqueuing timeout must be set carefully when multiple backends are used in the system. The shell instance could have a slow transport or could block, for example, by a UART with hardware flow control. If timeout is set too high, the logger thread could be blocked and impact other logger backends.

API Reference

group shell_api

Shell API.

Defines

SHELL_ARG(_mandatory, _optional)

Initializes a shell command arguments.

Parameters
  • _mandatory: Number of mandatory arguments.
  • _optional: Number of optional arguments.

SHELL_CMD_ARG_REGISTER(syntax, subcmd, help, handler, mandatory, optional)

Macro for defining and adding a root command (level 0) with required number of arguments.

Note
Each root command shall have unique syntax. If a command will be called with wrong number of arguments shell will print an error message and command handler will not be called.
Parameters
  • syntax: Command syntax (for example: history).
  • subcmd: Pointer to a subcommands array.
  • help: Pointer to a command help string.
  • handler: Pointer to a function handler.
  • mandatory: Number of mandatory arguments.
  • optional: Number of optional arguments.

SHELL_CMD_REGISTER(syntax, subcmd, help, handler)

Macro for defining and adding a root command (level 0) with arguments.

Note
All root commands must have different name.
Parameters
  • syntax: Command syntax (for example: history).
  • subcmd: Pointer to a subcommands array.
  • help: Pointer to a command help string.
  • handler: Pointer to a function handler.

SHELL_CREATE_STATIC_SUBCMD_SET(name)

Macro for creating a subcommand set. It must be used outside of any function body.

Parameters
  • name: Name of the subcommand set.

SHELL_SUBCMD_SET_END

Define ending subcommands set.

SHELL_CREATE_DYNAMIC_CMD(name, get)

Macro for creating a dynamic entry.

Parameters
  • name: Name of the dynamic entry.
  • get: Pointer to the function returning dynamic commands array

SHELL_CMD_ARG(_syntax, _subcmd, _help, _handler, _mandatory, _optional)

Initializes a shell command with arguments.

Note
If a command will be called with wrong number of arguments shell will print an error message and command handler will not be called.
Parameters
  • _syntax: Command syntax (for example: history).
  • _subcmd: Pointer to a subcommands array.
  • _help: Pointer to a command help string.
  • _handler: Pointer to a function handler.
  • _mandatory: Number of mandatory arguments.
  • _optional: Number of optional arguments.

SHELL_CMD(_syntax, _subcmd, _help, _handler)

Initializes a shell command.

Parameters
  • _syntax: Command syntax (for example: history).
  • _subcmd: Pointer to a subcommands array.
  • _help: Pointer to a command help string.
  • _handler: Pointer to a function handler.

SHELL_STATS_DEFINE(_name)
SHELL_STATS_PTR(_name)
SHELL_DEFINE(_name, _prompt, _transport_iface, _log_queue_size, _log_timeout, _shell_flag)

Macro for defining a shell instance.

Parameters
  • _name: Instance name.
  • _prompt: Shell prompt string.
  • _transport_iface: Pointer to the transport interface.
  • _log_queue_size: Logger processing queue size.
  • _log_timeout: Logger thread timeout in milliseconds on full log queue. If queue is full logger thread is blocked for given amount of time before log message is dropped.
  • _shell_flag: Shell output newline sequence.

SHELL_NORMAL

Terminal default text color for shell_fprintf function.

SHELL_INFO

Green text color for shell_fprintf function.

SHELL_OPTION

Cyan text color for shell_fprintf function.

SHELL_WARNING

Yellow text color for shell_fprintf function.

SHELL_ERROR

Red text color for shell_fprintf function.

shell_info(_sh, _ft, ...)

Print info message to the shell.

See shell_fprintf.

Parameters
  • _sh: Pointer to the shell instance.
  • _ft: Format string.
  • ...: List of parameters to print.

shell_print(_sh, _ft, ...)

Print normal message to the shell.

See shell_fprintf.

Parameters
  • _sh: Pointer to the shell instance.
  • _ft: Format string.
  • ...: List of parameters to print.

shell_warn(_sh, _ft, ...)

Print warning message to the shell.

See shell_fprintf.

Parameters
  • _sh: Pointer to the shell instance.
  • _ft: Format string.
  • ...: List of parameters to print.

shell_error(_sh, _ft, ...)

Print error message to the shell.

See shell_fprintf.

Parameters
  • _sh: Pointer to the shell instance.
  • _ft: Format string.
  • ...: List of parameters to print.

SHELL_CMD_HELP_PRINTED

Typedefs

typedef void (*shell_dynamic_get)(size_t idx, struct shell_static_entry *entry)

Shell dynamic command descriptor.

Function shall fill the received shell_static_entry structure with requested (idx) dynamic subcommand data. If there is more than one dynamic subcommand available, the function shall ensure that the returned commands: entry->syntax are sorted in alphabetical order. If idx exceeds the available dynamic subcommands, the function must write to entry->syntax NULL value. This will indicate to the shell module that there are no more dynamic commands to read.

typedef int (*shell_cmd_handler)(const struct shell *shell, size_t argc, char **argv)

Shell command handler prototype.

Parameters
  • shell: Shell instance.
  • argc: Arguments count.
  • argv: Arguments.
Return Value
  • 0: Successful command execution.
  • 1: Help printed and command not executed.
  • -EINVAL: Argument validation failed.
  • -ENOEXEC: Command not executed.

typedef void (*shell_transport_handler_t)(enum shell_transport_evt evt, void *context)

Enums

enum shell_receive_state

Values:

SHELL_RECEIVE_DEFAULT
SHELL_RECEIVE_ESC
SHELL_RECEIVE_ESC_SEQ
SHELL_RECEIVE_TILDE_EXP
enum shell_state

Values:

SHELL_STATE_UNINITIALIZED
SHELL_STATE_INITIALIZED
SHELL_STATE_ACTIVE
SHELL_STATE_PANIC_MODE_ACTIVE

Panic activated.

SHELL_STATE_PANIC_MODE_INACTIVE

Panic requested, not supported.

enum shell_transport_evt

Shell transport event.

Values:

SHELL_TRANSPORT_EVT_RX_RDY
SHELL_TRANSPORT_EVT_TX_RDY
enum shell_signal

Values:

SHELL_SIGNAL_RXRDY
SHELL_SIGNAL_LOG_MSG
SHELL_SIGNAL_KILL
SHELL_SIGNAL_TXDONE
SHELL_SIGNALS
enum shell_flag

Flags for setting shell output newline sequence.

Values:

SHELL_FLAG_CRLF_DEFAULT = (1<<0)
SHELL_FLAG_OLF_CRLF = (1<<1)

Functions

void shell_print_stream(const void *user_ctx, const char *data, size_t data_len)
int shell_init(const struct shell *shell, const void *transport_config, bool use_colors, bool log_backend, u32_t init_log_level)

Function for initializing a transport layer and internal shell state.

Return
Standard error code.
Parameters
  • shell: Pointer to shell instance.
  • transport_config: Transport configuration during initialization.
  • use_colors: Enables colored prints.
  • log_backend: If true, the console will be used as logger backend.
  • init_log_level: Default severity level for the logger.

int shell_uninit(const struct shell *shell)

Uninitializes the transport layer and the internal shell state.

Return
Standard error code.
Parameters
  • shell: Pointer to shell instance.

int shell_start(const struct shell *shell)

Function for starting shell processing.

Return
Standard error code.
Parameters
  • shell: Pointer to the shell instance.

int shell_stop(const struct shell *shell)

Function for stopping shell processing.

Return
Standard error code.
Parameters
  • shell: Pointer to shell instance.

void shell_fprintf(const struct shell *shell, enum shell_vt100_color color, const char *fmt, ...)

printf-like function which sends formatted data stream to the shell.

This function can be used from the command handler or from threads, but not from an interrupt context.

Parameters
  • shell: Pointer to the shell instance.
  • color: Printed text color.
  • fmt: Format string.
  • ...: List of parameters to print.

void shell_process(const struct shell *shell)

Process function, which should be executed when data is ready in the transport interface. To be used if shell thread is disabled.

Parameters
  • shell: Pointer to the shell instance.

int shell_prompt_change(const struct shell *shell, char *prompt)

Change displayed shell prompt.

Return
0 Success.
Return
-ENOMEM New prompt is too long.
Parameters
  • shell: Pointer to the shell instance.
  • prompt: New shell prompt.

void shell_help(const struct shell *shell)

Prints the current command help.

Function will print a help string with: the currently entered command and subcommands (if they exist).

Parameters
  • shell: Pointer to the shell instance.

int shell_execute_cmd(const struct shell *shell, const char *cmd)

Execute command.

Pass command line to shell to execute.

Note: This by no means makes any of the commands a stable interface, so this function should only be used for debugging/diagnostic.

Return
Result of the execution
Parameters
  • shell: Pointer to the shell instance. It can be NULL when the :option:CONFIG_SHELL_BACKEND_DUMMY option is enabled.
  • cmd: Command to be executed.

Variables

const struct log_backend_api log_backend_shell_api
struct shell_cmd_entry
#include <shell.h>

Shell command descriptor.

struct shell_transport_api
#include <shell.h>

Unified shell transport interface.

struct shell_stats
#include <shell.h>

Shell statistics structure.

union shell_internal
#include <shell.h>

Public Members

u32_t value
struct shell_flags flags
struct shell_ctx
#include <shell.h>

Shell instance context.

struct shell
#include <shell.h>

Shell instance internals.