BashSpark
Loading...
Searching...
No Matches
API Usage explanation

This section provides detailed instructions on how to utilize the BashSpark library effectively. It covers essential operations such as creating a shell instance, initializing a shell session, executing commands, and developing custom commands.

By following these guidelines, developers can manage command execution securely and efficiently while avoiding common pitfalls. The examples included showcase practical implementations, helping users to grasp each concept through hands-on coding snippets. Whether you are new to the library or seeking to enhance your command structures, this section serves as a comprehensive guide for operational success.

Create a shell instance

The first step to run commands is to create a shell. This class is named bs::shell. This class manages the available commands.

This command management method allow the programmer to strictly control the allowed behaviors, preventing security holes. This enhances the safety and reliability of command execution, ensuring that only authorized commands are executed and that errors are handled gracefully.

It is possible to obtain the default shell using method std::unique_ptr<bs::shell> bs::shell::make_default_shell(). The user may also use the default constructor bs::shell() to create a custom shell.

The class shell session may have the following methods overridden to change behavior (such as internationalization):

  • void shell::msg_error_command_not_found(shell_session &, const std::string &) const: error message on command not found.
  • void shell::msg_error_syntax_error(const shell_session &, const shell_exception &) const: error message on syntax error.

Example on obtaining the default session:

std::unique_ptr<bs::shell> pShell = bs::shell::make_default_shell();
static std::unique_ptr< shell > make_default_shell()
Construct bash object.
Definition shell.cpp:50

Example on building the default session:

std::unique_ptr<shell> shell::make_default_shell() {
auto pShell = std::make_unique<shell>();
pShell->set_command<command_echo>();
pShell->set_command<command_eval>();
pShell->set_command<command_getenv>();
pShell->set_command<command_getvar>();
pShell->set_command<command_setenv>();
pShell->set_command<command_setvar>();
pShell->set_command<command_seq>();
pShell->set_command<command_test>();
pShell->set_command<command_math>();
pShell->set_command<command_fcall>();
return pShell;
}

Create a shell session

In order to execute shell commands a shell session is needed. This class is named bs::shell_session. The class bs::shell_session contains the streams (stdin, stdout, stderr), the environment variables, the function arguments, the local variables, the last exit status code and the shell depth. See the documentation on bs::shell_session for more details.

A bs::shell_session is constructed as shell_session(const shell *pShell, std::istream &oStdIn, std::ostream &oStdOut, std::ostream &oStdErr).

The class bs::shell_session may be extended in order to add custom information to the session. If a custom command uses the custom session, on method bs::command::run(const std::span<const std::string> &, shell_session &) const, it will be necessary to use auto pCustomSession = dynamic_cast<shell_session_custom*>(&oSession) in order to acquire it.

Example of shell session directly linked to terminal:

// Generate shell
std::unique_ptr<bs::shell> pShell = bs::shell::make_default_shell();
// Instantiare session
pShell.get(), // Owning shell
std::cin, // stdin
std::cout, // stdout
std::cerr // stderr
);
Represents an execution environment for a shell instance.
Definition shell_session.h:57

Run a command

Running a command requires instantiating the sell session and using the methods shell_status shell::run(std::istream &oCommand,shell_session &oSession);, shell_status run(const std::string &sCommand, shell_session &oSession); or shell_status run(const std::string_view &sCommand, shell_session &oSession);.

Example of hello world:

bs::shell_status run_helloworld() {
// Generate shell
std::unique_ptr<bs::shell> pShell = bs::shell::make_default_shell();
// Instantiare session
pShell.get(), // Owning shell
std::cin, // stdin
std::cout, // stdout
std::cerr // stderr
);
// Run command
return bs::shell::run("echo -n 'Hello world!'"sv, oSession);
}
static shell_status run(std::istream &oCommand, shell_session &oSession)
Runs a command or script.
Definition shell.cpp:150
shell_status
Shell status codes.
Definition shell_status.h:47

Create a custom command

Creating a custom command requires implementing the bs::command interface.

The custom command class has to override the method bs::command::run(const std::span<const std::string> &, shell_session &) const. On success, the run method must return bs::shell_status::SHELL_SUCCESS. On failure, the run method must return return bs::make_user_code(USER_ERROR_CODE) (see bs::make_user_code) where USER_ERROR_CODE is an unsigned int.

It is recommended to define supplementary functions to print the error messages. Such functions would have a prototype like: print_error(std::ostream& oStderr) const.

Use the function bs::shell::set_command(std::unique_ptr<command> &&) to add the custom commands. The helper template template<typename CommandT, typename... Args> void bs::shell::set_command(Args &&...) may also be useful.

If the custom command uses a custom session, on method bs::command::run(const std::span<const std::string> &, shell_session &) const, it will be necessary to use auto pCustomSession = dynamic_cast<shell_session_custom*>(&oSession) in order to acquire it.

If the custom command calls subshells, to prevent SIGSEGV by stack overflow it is recommended to check the shell depth. Custom commands should not modify this depth lightly.

Example:

// @class command_helloworld
// @brief Prints "Hellow world!" on stdout.
// Syntax: helloworld
class command_helloworld : public bs::command {
public:
// @brief Constructs the command
// Instantiates bs::command with the command name
command_helloworld()
: bs::command("helloworld") {
}
public:
// @brief Prints "Hellow world!" on stdout
// If the argument number is different from 0 then msg_error_param_number is called.
// @param vArgs Command arguments
// @param oSession Shell session
// @return Status code
shell_status run(const std::span<const std::string> &vArgs, shell_session &oSession) const override {
// Check there are 0 arguments
if (!vArgs.empty()) {
// Call error printing functon
this->msg_error_param_number(
oSession.err(), // stderr
vArgs.size() // Argument number
);
// Return error code 1 (make user code)
return bs::make_user_code(1);
}
// Print "Hello World! on stdout
oSession.out() << "Hello World!";
// Return sucess code
}
// @brief Print an error if the wrong number of arguments is provided.
// @param oStdErr Stream to print error message.
// @param nArgs Number of provided arguments.
virtual void msg_error_param_number(std::ostream &oStdErr, const std::size_t nArgs) const {
// Print error message
oStdErr << "helloworld: takes 0 parameters, but received " << nArgs << "." << std::endl;
// The user may implement more complex behaviour such as internationalization
}
};
Abstract base class for all shell commands.
Definition command.h:50
virtual shell_status run(const std::span< const std::string > &vArgs, shell_session &oSession) const =0
Execute the command.
BashSpark main namespace.
Definition command.h:39
@ SHELL_SUCCESS
Indicates successful execution.
constexpr shell_status make_user_code(const unsigned int nCode)
Generates status codes for user use from user codes.
Definition shell_status.h:249

Example of adding it to a shell instance:

pShell1->set_command<command_helloworld>();
auto pCommand = std::make_unique<command_helloworld>();
pShell2->set_command(std::move(pCommand));

Example of shell depth check:

// Increase depth
if (!oSession.increase_shell_depth()) {
this->msg_error_max_depth_reached(oSession.err());
return shell_status::SHELL_ERROR_MAX_DEPTH_REACHED;
}
// Run command here
// Decrease depth
oSession.decrease_shell_depth();