Skip to main content

Shell Features

The jline-shell module provides a rich set of features for building interactive command-line applications. This page covers the key extensibility points and built-in capabilities.

Aliases​

The alias system allows users to define short names for frequently used commands.

Setting Up​

Wire an AliasManager into the shell builder:

Shell shell = Shell.builder()
.aliasManager(new DefaultAliasManager())
.build();

For persistence across sessions, provide a file path:

Path aliasFile = Paths.get(System.getProperty("user.home"), ".myapp-aliases");
DefaultAliasManager aliasManager = new DefaultAliasManager(aliasFile);
aliasManager.load();

Built-in Commands​

When an AliasManager is configured, the shell automatically registers:

  • alias — list all aliases, define an alias (alias name=expansion), or show one (alias name)
  • unalias — remove an alias (unalias name)

Parameter Substitution​

Alias expansions support parameter markers:

  • $1, $2, ... — positional arguments
  • $@ — all arguments
alias grp=grep $1 $2
grp pattern file.txt → grep pattern file.txt

alias e=echo $@
e hello world → echo hello world

Demo​

Loading snippet: ShellAliasExample...


Built-in Commands​

The shell provides several opt-in command groups that can be enabled via the builder.

History​

Shell.builder().historyCommands(true)

Adds the history command:

UsageDescription
historyList all entries
history NList last N entries
history -cClear history
history -sSave history to file
history /patternSearch by regex

Help​

Shell.builder().helpCommands(true)

Adds the help command (also aliased as ?):

UsageDescription
helpList all commands grouped by category
help <command>Show detailed help for a specific command

Options​

Shell.builder().optionCommands(true)

Adds commands for runtime LineReader configuration:

CommandDescription
setopt OPTIONEnable a LineReader option
setoptList enabled options
unsetopt OPTIONDisable a LineReader option
setvar NAME VALUESet a LineReader variable
setvarList all variables

Syntax Highlighting​

The CommandHighlighter provides command-aware syntax highlighting:

  • Known commands are highlighted in bold
  • Unknown commands are highlighted in red
  • Pipeline operators (|, &&, ||, ;, etc.) are highlighted in cyan

Enabling​

Shell.builder().commandHighlighter(true)

Custom Highlighters​

You can provide a custom Highlighter implementation:

Shell.builder().highlighter(myHighlighter)

Or combine both — the command highlighter can delegate to a custom highlighter:

Shell.builder()
.highlighter(myLanguageHighlighter)
.commandHighlighter(true)

Job Control​

The shell supports background execution and job management.

Enabling​

Shell.builder().jobManager(new DefaultJobManager())

Usage​

SyntaxDescription
command &Run in background
jobsList active jobs
fg [id]Bring job to foreground
bg [id]Resume job in background

Jobs are tracked with status: Foreground, Background, Suspended, Done.

Demo​

Loading snippet: ShellJobExample...


Pipeline Extensibility​

The pipeline parser supports standard shell operators and can be extended with custom operators.

Built-in Operators​

OperatorNameDescription
|PipeSend output to next command
|;FlipAppend output as argument
&&ANDExecute next only if previous succeeded
||ORExecute next only if previous failed
;SequenceExecute next unconditionally
>RedirectWrite output to file
>>AppendAppend output to file
&BackgroundRun in background (at end of line)

Custom Operators​

Register custom operator symbols that map to existing operator types:

PipelineParser parser = new PipelineParser(Map.of(
"==>", Pipeline.Operator.PIPE,
"-->", Pipeline.Operator.FLIP
));
Shell.builder().pipelineParser(parser)

Subclassing​

For more control, subclass PipelineParser and override matchOperator():

PipelineParser parser = new PipelineParser() {
@Override
protected String matchOperator(String line, int pos) {
// Custom matching logic
if (line.startsWith("::", pos)) {
return "::";
}
return super.matchOperator(line, pos);
}
};

I/O Redirection​

In addition to > and >>, the pipeline parser supports:

OperatorNameDescription
<Input redirectRead stdin from file
2>Stderr redirectRedirect stderr to file
&>Combined redirectRedirect both stdout and stderr to file
echo hello > out.txt # write to file
cat < out.txt # read from file
cmd 2> errors.txt # capture stderr
cmd &> all.txt # capture both stdout and stderr

Demo​

Loading snippet: ShellPipelineExample...


Variable Expansion​

The LineExpander interface provides pluggable variable expansion that runs after alias expansion and before pipeline parsing.

Setting Up​

Shell.builder()
.lineExpander(new DefaultLineExpander())
.build();

Built-in Expansions​

The DefaultLineExpander supports:

SyntaxDescription
$VARSession variable, then environment variable
${VAR}Same, with braces for disambiguation
~Expands to user.home (at word start)
'...'Single-quoted regions are not expanded
"..."Double-quoted regions expand variables
${VAR:-default}Use default if VAR is unset or empty
${VAR:=default}Assign default if VAR is unset or empty
${VAR:+alt}Use alt if VAR is set and non-empty
${VAR:?error}Error if VAR is unset or empty
echo $HOME → /home/user
echo ${HOME}/docs → /home/user/docs
echo ~/docs → /home/user/docs
echo '$HOME' → $HOME (no expansion)
echo "$HOME" → /home/user
echo ${MISSING:-hello} → hello
echo ${NAME:+yes} → yes (if NAME is set)

Unknown variables are left as-is ($UNKNOWN → $UNKNOWN).

Setting Variables​

Variables can be set in several ways:

SyntaxDescription
NAME=VALUEBare assignment (at dispatcher level)
set NAME=VALUESet command
set NAME VALUESet command (space form)
export NAME=VALUEExport command (alias for set)
unset NAMERemove a variable
setList all session variables

Enable the set/unset/export commands:

Shell.builder().variableCommands(true)

Bare NAME=VALUE assignment is always available when a LineExpander is configured.

Custom Expansion​

Implement LineExpander for custom syntax:

LineExpander expander = (line, session) -> {
// Custom expansion logic
return line.replace("@user", System.getProperty("user.name"));
};

Or subclass DefaultLineExpander and override expandBracedExpression() or resolve():

new DefaultLineExpander() {
@Override
protected String resolve(String name, CommandSession session) {
// Custom variable resolution, Groovy evaluation, etc.
return super.resolve(name, session);
}
};

Subcommands​

Commands can define subcommands via the subcommands() method. The dispatcher routes git commit -m msg to the commit subcommand with args [-m, msg].

Defining Subcommands​

public class GitCommand extends AbstractCommand {
private final Map<String, Command> subs = Map.of(
"commit", new CommitCommand(),
"status", new StatusCommand()
);

public GitCommand() { super("git"); }

@Override
public Map<String, Command> subcommands() { return subs; }

@Override
public Object execute(CommandSession session, String[] args) {
session.out().println("usage: git <command>");
return null;
}
}

Tab completion automatically offers subcommand names after the parent command.


Script Execution​

The ScriptRunner interface enables executing script files line by line through the dispatcher.

Setting Up​

Shell.builder()
.scriptRunner(new DefaultScriptRunner())
.scriptCommands(true) // registers source/. commands
.build();

Usage​

source ~/.myapprc
. ./setup.sh

Script Format​

The DefaultScriptRunner supports:

  • Lines starting with # are comments
  • Blank lines are skipped
  • \ at end of line joins with the next line (continuation)
# This is a comment
alias ll=ls -la

echo hello \
world

Init Scripts​

Run a script at shell startup:

Shell.builder()
.scriptRunner(new DefaultScriptRunner())
.initScript(new File("~/.myapprc"))
.build();

Signal Handling​

When the dispatcher is a DefaultCommandDispatcher, the shell automatically registers signal handlers:

SignalBehavior
INT (Ctrl-C)Interrupts the running command, not the shell
TSTP (Ctrl-Z)Suspends the foreground job (when job manager is configured)

Previous signal handlers are restored when the shell exits.


Builtins Integration​

The PosixCommandGroup wraps the builtins module's POSIX commands as shell commands:

Shell.builder()
.groups(new PosixCommandGroup())
.build();

Available commands: cd, pwd, echo, cat, ls, grep, head, tail, wc, sort, date, sleep, clear.

For interactive commands (nano, less, ttop):

Shell.builder()
.groups(new InteractiveCommandGroup())
.build();

Full Example​

Loading snippet: ShellBuilderExample...