-
Notifications
You must be signed in to change notification settings - Fork 0
Shell Commands
JNode's command-line environment provides a Bash-like shell experience, implemented entirely in Java, with a custom command framework for parsing arguments and handling I/O redirection.
Unlike Unix, where commands are typically separate executable binaries launched via fork() and exec(), JNode commands are Java classes executed within the same JVM (often within their own Isolates or Proclets).
The shell subsystem handles interactive input, script execution (e.g., jnode.ini), tab-completion, aliasing, and I/O redirection.
Location: shell/src/shell/org/jnode/shell/
The main interactive loop. It reads input from the user (via CommandShellReader), parses it, and executes it using an interpreter.
JNode supports different interpreters.
-
DefaultInterpreter: Simple execution of single commands. -
RedirectingInterpreter: Handles Unix-style pipelines (|) and I/O redirection (>,<). -
bjorne: A Bourne-compatible shell script interpreter capable of running more complex shell scripts.
Decides how the Java class representing the command is run.
-
DefaultCommandInvoker: Runs the command in the current context. -
ThreadCommandInvoker: Runs the command in a newVmThread. -
ProcletCommandInvoker: Runs the command in a new Proclet (a lightweight isolated context sharing the same address space but with separate standard streams and environment variables).
Commands are implemented by extending AbstractCommand (or implementing Command).
public class MyCommand extends AbstractCommand {
private final StringArgument arg = new StringArgument("name", Argument.MANDATORY, "Your name");
public MyCommand() {
super("Prints a greeting");
registerArguments(arg);
}
public void execute() throws Exception {
PrintWriter out = getOutput().getPrintWriter();
out.println("Hello, " + arg.getValue());
}
}JNode commands do not typically parse String[] args manually. Instead, they register Argument objects (e.g., FileArgument, StringArgument, FlagArgument). The shell's syntax parser automatically validates input against these arguments before execute() is called. This also automatically provides --help text and tab-completion.
For detailed information on how the syntax system works, see Syntax.
Commands must be registered to be accessible from the shell. This is done via Plugin Extension Points.
In a plugin's plugin.xml descriptor:
<extension point="org.jnode.shell.aliases">
<alias name="mycmd" class="com.example.MyCommand"/>
</extension>This tells the AliasManager to map the keyword mycmd to the given class. When the user types mycmd, the shell instantiates the class and invokes it.
Many standard Unix-like commands are implemented in shell/src/shell/org/jnode/shell/command/ and cli/src/.
-
File operations:
ls,cp,mv,rm,cat,mkdir -
System info:
free,ps,threads,gc,dmesg -
Network:
ping,ifconfig,route -
Disk:
mount,fdisk,format
Because all commands run in the same JVM, System.out and System.in cannot be safely redirected globally. Instead, commands use getInput(), getOutput(), and getError() provided by AbstractCommand. The Proclet system manages these streams per-thread-group, allowing CommandA | CommandB to work correctly without polluting the global System.out.
The install command provides an interactive shell-based installer:
install [device-name]This launches CommandLineInstaller (via InstallCommand), which:
- Accepts an optional device name argument (e.g.,
install hda0) - Auto-discovers a single JFAT partition if no argument is given
- Falls back to an interactive prompt if multiple or no candidates are found
- Registers two actions:
CopyFilesAction→GrubInstallerAction(files first, then GRUB) - Provides console-based readline I/O via
System.in/System.out - Drives the same action sequence as the boot-time installer but from within a live JNode shell
- Supports
Step.backnavigation duringcollect()for revisiting previous steps
CommandLineInstaller resolves the target device via three strategies:
- CLI argument — passed directly to the constructor
-
Auto-discovery — scans mounted filesystems for a single JFAT partition under
/devices/ - Interactive prompt — asks the user to enter a device name
The resolved device ID and mount point are cached in InputContext so downstream actions (GrubInstallerAction, CopyFilesAction) can read them without re-prompting.
CopyFilesAction runs before GrubInstallerAction so that GRUB's stage1.5 write does not corrupt the freshly written FAT filesystem. The mount point is resolved by filesystem identity (same approach as JGrub.getMountPoint) rather than by path substring matching.
Handles device selection and GRUB installation:
- Reads
DEVICE_IDfrom the action context (set byCommandLineInstaller) - Only prompts for device input if
DEVICE_IDis absent -
execute()installs GRUB stage1/stage1.5/stage2 on the selected device - Guards against no-device-selected with
IllegalStateException
JGrub handles whole-disk device names (no partition suffix):
- If
partitionSuffixis empty, defaultspartitionNumberto 0 - If
parentDeviceNameis empty, uses the device itself as the parent
-
Plugin-System — How commands are registered via
<alias>extensions. - Code-Conventions — Best practices for writing robust commands.