unstable-doc only.Expand description
§CLI Concepts
Note: this will be speaking towards the general case.
§Environmental context
When you run a command line application, it is inside a terminal emulator, or terminal. This handles integration with the rest of your system including user input, rendering, etc.
The terminal will run inside of itself an interactive shell.
The shell is responsible for showing the prompt, receiving input including the command you are writing,
letting that command take over until completion, and then repeating.
This is called a read-eval-print loop, or REPL.
Typically the shell will take the command you typed and split it into separate arguments,
including handling of quoting, escaping, and globbing.
The parsing and evaluation of the command is shell specific.
The shell will then determine which application to run and then pass the full command-line as
individual arguments to your program.
These arguments are exposed in Rust as std::env::args_os.
Windows is an exception in Shell behavior in that the command is passed as an individual
string, verbatim, and the application must split the arguments.
std::env::args_os will handle the splitting for you but will not handle globs.
Takeaways:
- Your application will only see quotes that have been escaped within the shell
- e.g. to receive
message="hello world", you may need to type'message="hello world"'ormessage=\"hello world\"
- e.g. to receive
- If your applications needs to parse a string into arguments,
you will need to pick a syntax and do it yourself
- POSIX’s shell syntax is a common choice and available in packages like shlex
- See also our REPL cookbook entry
- On Windows, you will need to handle globbing yourself if desired
wildcan help with that
§Argument Parsing
The first argument of std::env::args_os is the Command::bin_name
which is usually limited to affecting Command::render_usage.
Command::no_binary_name and Command::multicall exist for rare cases when this assumption is not valid.
Command-lines are a context-sensitive grammar, meaning the interpretation of an argument is based on the arguments that came before. Arguments come in one of several flavors:
- Values
- Flags
- Subcommands
When examining the next argument,
- If it starts with a
--, then that is a long Flag and all remaining text up to a=or the end is matched to aArg::long,Command::long_flag, or alias.- Everything after the
=is taken as a Value and parsing a new argument is examined. - If no
=is present, then Values will be taken according toArg::num_args - We generally call a Flag that takes a Value an Option
- Everything after the
- If it starts with a
-, then that is a sequence of short Flags where each character is matched against aArg::short,Command::short_flagor alias until=, the end, or a short Flag takes Values (seeArg::num_args) - If its a
--, that is an escape and all future arguments are considered to be a Value, even if they start with--or- - If it matches a
Command::name, then the argument is a subcommand - If there is an
Argat the nextArg::index, then the argument is considered a Positional argument
When a subcommand matches,
all further arguments are parsed by that Command.
There are many settings that tweak this behavior, including:
Arg::last: a positional that can only come after--Arg::trailing_var_arg: all further arguments are captured as additional ValuesArg::allow_hyphen_valuesandArg::allow_negative_numbers: assumes arguments starting with-are Values and not Flags.Command::subcommand_precedence_over_arg: when anArg::num_argstakes Values, stop if one matches a subCommandCommand::allow_missing_positional: in limited cases aArg::indexmay be skippedCommand::allow_external_subcommands: treat any unknown argument as a subcommand, capturing all remaining arguments.
Takeaways
- Values that start with a
-either need to be escaped by the user with--(if a positional), or you need to setArg::allow_hyphen_valuesorArg::allow_negative_numbers Arg::num_args,ArgAction::Append(on a positional),Arg::trailing_var_arg, andCommand::allow_external_subcommandsall affect the parser in similar but slightly different ways and which to use depends on your application
§Value Parsing
When reacting to a Flag (no Value),
Arg::default_missing_values will be applied.
The Value will be split by Arg::value_delimiter.
The Value will then be stored according to its ArgAction.
For most ArgActions,
the Value will be parsed according to ValueParser
and stored in the ArgMatches.