NESH User Manual
User manual

NESH — the New EFI Shell

A modern command shell for UEFI firmware: files, disks, boot entries, UEFI variables, drivers, hardware and network, with a real scripting language built in.

Version 0.2.0 Platform UEFI x86-64 File nesh.efi (one file) Audience users, administrators, script writers

Contents

The alphabetical index at the end lists every command, function and topic. Press / to search.

Introduction

NESH is a shell that runs inside the UEFI firmware, before any operating system starts. It does what the classic UEFI Shell does, and it is designed to be pleasant to use and easy to script.

What NESH is

Every modern PC starts with UEFI firmware. Before Windows or Linux is loaded, the firmware can run UEFI applications: boot loaders, setup tools, firmware updaters, diagnostics. A shell is a UEFI application that gives you a command prompt in that environment, so you can look at disks and files, change boot entries, read hardware information and run other tools.

NESH is such a shell. It is a single file, nesh.efi, that you start from the firmware boot menu or from a USB stick. It offers:

>_

A friendly prompt

Line editing, history with search, Tab completion, aliases, colors and clear error messages.

10

Real scripting

NESH BASIC: variables, loops, functions, strings, arrays, menus, files — instead of batch-file tricks.

Boot management

bootmgr lists, adds, orders and checks boot entries, with dry run, confirmation and automatic backups.

UEFI Shell commands

The UEFI Shell command set, rewritten from scratch; their options are accepted too.

{ }

Script-friendly output

With -data, commands print key=value records that scripts read with two functions.

Hardware and network

PCI, SMBIOS, ACPI, memory map, CPU; IPv4 and IPv6, ping, TFTP and HTTP downloads.

NESH and the UEFI Shell

The UEFI Shell (from the EDK2 project) is the standard shell of UEFI. NESH keeps what people already know — the same command names, the same options, the same fs0: volume names, the same environment variables — and changes what is hard to use.

TopicUEFI ShellNESH
Scripts.nsh batch files: if, for, goto, %1 .nsb scripts in NESH BASIC: variables, expressions, SUB/FUNCTION, arrays, strings
Using command output in scripts-sfo (comma separated, hard to read) -data (key=value) + RECORDS / FIELD$; RUN$() captures any output
Boot entriesbcfg (positions, no safety net) bcfg and bootmgr: IDs, dry run, confirmation, automatic backup, check for missing files
Promptbasic editinghistory search, word movement, completion, aliases, multi-line BASIC
Networkifconfig, ping, tftp, http (separate IPv6 tools) same commands; ping, tftp, http accept IPv4 and IPv6 addresses
Distributionshell + separate command librariesone file, nesh.efi — easy to copy and to sign for Secure Boot
Secure Bootno special handlinglow-level hardware writes are disabled while Secure Boot is active

UEFI applications written for the UEFI Shell (the ones using the EDK2 ShellLib) run unchanged under NESH, because NESH provides the same programming interface. See Running EFI applications and Differences from the UEFI Shell.

Conventions used in this manual

You seeIt means
ls -lSomething you type, a command, a file name or a value.
cp SRC DSTWords in capitals are placeholders: replace them with real values.
[-r]Square brackets in a syntax line mark something optional.
a | bA vertical bar in a syntax line means “one of these”.
Ctrl+RKeys to press.

Examples labeled Console show a session at the prompt: the text up to > is the prompt, the rest of that line is what you type, and the lines below are the output. Examples labeled NESH BASIC are script files.

fs0:\> echo Hello
Hello
Tip

Every command explains itself: help lists the commands and help NAME shows the details of one. help NAME also works for a statement or a function of the scripting language (help PRINT, help LEN), and help basic and help functions are the two quick references. The command texts form the command reference at the end of this manual.

Getting started

This chapter shows how to get nesh.efi, how to start it and what to do first.

Requirements

Building nesh.efi

$ make
$ ls -l build/nesh.efi

make produces build/nesh.efi, the complete shell in one file. make test runs the automatic tests; make qemu starts NESH in a virtual machine (QEMU with OVMF firmware), which is the easiest way to try it without touching a real computer.

Putting NESH on a USB stick

There are two common layouts. Both need a FAT32 (or FAT16) partition.

  1. As the default loader of a removable disk. Copy nesh.efi to \EFI\BOOT\BOOTX64.EFI on the stick. The firmware boot menu then shows the stick as a boot device, and choosing it starts NESH directly.
  2. As a tool next to other loaders. Copy nesh.efi to any folder, for example \EFI\tools\nesh.efi, and start it from another shell or add a boot entry for it (see below).
Note

NESH treats the volume it was started from as its boot volume. It looks there for the start-up script startup.nsb, keeps the command history in \nesh_history.txt and puts boot-entry backups in \nesh\backup. If the volume is read-only, NESH simply works without these files.

Starting NESH

Choose the USB stick (or the NESH boot entry) in the firmware boot menu — usually opened with F12, F11, F8 or Esc during power-on, depending on the manufacturer. NESH starts with a short banner and a prompt showing the current directory:

NESH 0.2.0 - New EFI Shell (UEFI x86_64)
Type 'help' for the commands, 'help basic' for the scripting language.

fs0:\> 

If Secure Boot is active, the banner has one more line: Secure Boot is active: low-level hardware writes are disabled. (see Secure Boot).

NESH can also be started from another UEFI shell, with optional arguments:

Command lineEffect
nesh.efiInteractive shell; runs startup.nsb first if it exists.
nesh.efi -nInteractive shell, without running startup.nsb.
nesh.efi SCRIPT [ARGS...]Runs a script and exits with its exit code.
nesh.efi -c "CODE"Runs one line of BASIC and exits, e.g. nesh.efi -c "PRINT 6*7".

The start-up script

If the boot volume contains \startup.nsb, NESH runs it when it starts. Before that it counts down three seconds:

Running fs0:\startup.nsb in 3 s (ESC to skip, any other key to start now)

Press Esc to skip the script, or any other key to run it at once. startup.nsb is an ordinary NESH BASIC script; a typical use is the interactive boot menu.

First steps: a short tour

Try these commands in order. They are safe: none of them changes anything.

fs0:\> map
fs0:\> ls
fs0:\> ver
fs0:\> sysinfo
fs0:\> bootmgr
fs0:\> help

To leave NESH type exit: the firmware continues with its boot order, or shows its boot menu.

Using the command line

How to type commands, how NESH reads them, and the tools that make the prompt fast to use.

The prompt and command syntax

The prompt shows the current directory, for example fs0:\efi\boot>. A command line is a command name followed by words separated by spaces:

fs0:\> cp -r fs0:\efi fs1:\backup

Volumes, paths and wildcards

Each file system the firmware can read gets a volume name: fs0:, fs1: and so on. A full path starts with a volume name, and folders are separated by a backslash. The forward slash works too.

PathMeaning
fs1:\efi\boot\bootx64.efiFull path on volume fs1:.
\efi\bootFrom the root of the current volume.
boot\grub.cfgRelative to the current directory.
..   .The parent directory, the current directory.
fs1:Typed alone as a command: switch to volume fs1:.

Wildcards select several files: * matches any text and ? one character. cp *.efi fs1:\tools copies every .efi file of the current folder. Commands expand wildcards themselves; the help of each command says whether it accepts them.

Volumes can get extra names with map: after map usb fs1:, the path usb:\data means fs1:\data. See Volumes and disks.

Line editing, history and completion

KeysAction
  Ctrl+B Ctrl+FMove one character.
Ctrl+ Ctrl+   Alt+B Alt+FMove one word.
Home End   Ctrl+A Ctrl+EStart / end of the line.
Backspace DelDelete before / under the cursor.
Ctrl+K   Ctrl+UDelete to the end / to the start of the line.
Ctrl+WDelete the word before the cursor.
EscClear the line.
Previous / next command of the history.
Ctrl+RSearch the history: type part of a command; Ctrl+R again finds older matches; Enter or an arrow key accepts, Esc cancels.
TabComplete a command name, an alias or a path. If several completions are possible, the common part is completed and the choices are listed.
Ctrl+LClear the screen.
Ctrl+CCancel the line; while a command or script runs, stop it.

The history keeps the last 500 commands and is saved in \nesh_history.txt on the boot volume, so it survives a restart. history shows it, history 20 the last 20 lines, history -c clears it.

Saving output to a file

> FILE at the end of a command writes its output to a file instead of the screen; >> FILE appends to the file.

fs0:\> map -v > fs1:\map.txt
fs0:\> echo checked on %date% >> fs1:\log.txt

Error messages still appear on the screen. There are no pipes (|): to process the output of a command, use a script with RUN$() (see Running commands from a script) or save it to a file and use grep, head or tail.

Note

NESH does not replace %name% in command lines (that is a UEFI Shell script feature): the second example above writes the text %date% literally. In NESH, use a BASIC expression instead: RUN "echo checked on " + DATE$ + " >> fs1:\log.txt".

Exit codes

Every command ends with an exit code: 0 means success, any other value an error. Scripts read it in ERR; at the prompt it is kept in the read-only environment variable lasterror.

CodeMeaning
0Success.
1The command failed (file not found, device error…). Some commands use 1 for “no”: grep when nothing matched, comp when files differ.
2Wrong usage: unknown option, missing argument.
127Command not found.
130Stopped with Ctrl+C.
otherEFI applications and scripts may return any code (a script with END 5 returns 5).

Running scripts and EFI programs

Scripts (.nsb) and UEFI applications (.efi) run like commands: type their name, with or without the extension, followed by their arguments.

fs0:\> backup fs1:
fs0:\> fs0:\efi\tools\memtest.efi

A name without a folder is searched in the directories listed in the environment variable path (separated by ;, where . is the current directory). By default path is .;fsN:\efi\tools;fsN:\efi\boot;fsN:\, where fsN: is the boot volume. For each directory NESH tries NAME.nsb, then NAME.efi, then NAME itself. which NAME tells what a name refers to.

A file without one of those two extensions is run only when it really is a UEFI application for this machine: NESH reads its headers and asks for the MZ and PE signatures, x86-64 code and an application (not a driver). This is what makes vmlinuz — a Linux kernel with the EFI stub, which nobody names vmlinuz.efi — start by typing its name, while a data file such as live.img answers command not found. A script must end in .nsb: there is no signature to recognise it by.

Environment variables

Environment variables are named text values shared by the shell and the programs it starts. They are the same as in the UEFI Shell.

fs0:\> set -v target fs1:\backup
fs0:\> set target
fs1:\backup
fs0:\> set -d target

Aliases

An alias is a short name for a command with its options:

fs0:\> alias ll ls -l
fs0:\> ll fs1:\efi

The second line runs ls -l fs1:\efi: the extra words are added at the end. Like variables, aliases are permanent unless created with alias -v, and alias -d NAME deletes one. NESH defines a few temporary aliases for names used by other shells: copy (cp), ren and move (mv), rd (rmdir).

BASIC at the prompt

Lines that are BASIC statements run directly at the prompt, which makes NESH a handy calculator and a place to try script fragments. A line is treated as BASIC when it starts with a statement keyword (PRINT, FOR, IF, DIM…), assigns a variable (x = 5) or calls a SUB you defined. Everything else is a command.

fs0:\> PRINT HEX$(&HFF * 16)
FF0
fs0:\> n = 0
fs0:\> FOR i = 1 TO 5
...   n = n + i
... NEXT
fs0:\> PRINT n
15

When a block (FOR, IF, SUB…) is not finished, the prompt becomes ... until it is; Ctrl+C cancels the block. Variables and SUBs defined at the prompt stay available until NESH exits.

Long output: paging

A UEFI console cannot be scrolled back, so output that does not fit would be lost. When you type a command at the prompt and its output goes to the screen, NESH stops at every screenful:

fs0:\> help
  acpiview       List the ACPI tables, or show or save one
  ...
-- More -- (Enter: one line, Space: one page, q: stop)
KeyDoes
SpaceShow the next screenful.
EnterShow one more line.
q (or Esc, Ctrl+C)Stop: the command ends and the prompt comes back.

Paging never gets in the way of automation: it is off inside scripts and whenever the output is redirected to a file or captured with RUN$. Two ways to control it:

CommandEffect
set pager offNo automatic paging at the prompt (set -d pager or set pager on restores it).
COMMAND -bPage this command even inside a script; the option of the UEFI Shell.
more FILE...Print files one screen at a time (like cat, always paged).

To keep a long output instead, save it to a file: dh > fs1:\handles.txt.

Stopping a command

Ctrl+C stops the running command, script or listing (exit code 130).

Working with files and folders

Listing, copying, moving and deleting files; attributes; comparing files. The commands work on any volume the firmware can read, and write where the volume is writable.

Listing files

ls shows names; ls -l (or dir) adds date, time, attributes and size. Directory names end with \.

fs0:\> ls
EFI\           bootmenu.nsb   inventory.nsb  startup.nsb
findefi.nsb    nesh.efi
fs0:\> ls -l
2026-09-19 22:16  d----         <DIR>  EFI\
2026-09-19 22:16  ----a           684  bootmenu.nsb
2026-09-19 22:16  ----a           506  findefi.nsb
2026-09-19 22:16  ----a           610  inventory.nsb
2026-09-19 22:16  ----a        369152  nesh.efi
2026-09-19 22:16  ----a           562  startup.nsb
5 files, 1 dir, 371514 bytes

The attribute column shows d directory, r read-only, h hidden, s system, a archive. Useful variations:

CommandLists
ls -r fs0:\efiThe folder and all its subfolders.
ls *.efiOnly matching names.
ls -aAlso hidden and system files (normally left out).
ls -adOnly directories (-a followed by attribute letters selects entries that have them).
stat nesh.efiEvery detail of one file.

Copying, moving, deleting

fs0:\> mkdir -p fs1:\backup\efi
fs0:\> cp -r fs0:\efi fs1:\backup
fs0:\> cp *.nsb fs1:\backup
fs0:\> mv fs1:\backup\old.txt fs1:\backup\notes.txt
fs0:\> rm fs1:\backup\*.tmp
fs0:\> rm -r fs1:\backup\efi
CommandWhat to know
cp [-r] SRC... DSTCopies files; -r copies folders with their contents. Existing files are overwritten without asking. If a copy fails or is stopped, the partial file is removed.
mv SRC... DSTMoves or renames. An existing target file is replaced only after the move has succeeded. Between volumes it copies, then deletes the source only after a complete copy.
rm [-r] [-f] PATH...Deletes files; -r also folders. Nothing is asked. -f: no error when nothing matches. Read-only files must be unlocked first with attrib -r.
mkdir [-p] DIR...Creates folders; -p creates the whole path.
rmdir DIR...Removes empty folders.
touch FILE...Creates empty files or updates their time.
setsize SIZE FILE...Truncates or extends files to an exact size.
Be careful

Like the UEFI Shell, NESH does not ask for confirmation before overwriting or deleting, and there is no recycle bin. Double-check wildcards: ls PATTERN first shows exactly what rm PATTERN would delete.

Attributes

FAT files have four attributes: read-only, hidden, system and archive. attrib shows and changes them:

fs0:\> attrib +r fs1:\tools\*.efi
fs0:\> attrib -h -s fs0:\*

Comparing files

comp lists the differences between two files (offsets in hex, bytes of both files); cmp stops at the first one. Both end with ERR = 0 when the files are identical, which makes them useful in scripts:

RUN "cmp", "fs0:\efi\boot\bootx64.efi", "fs1:\backup\bootx64.efi"
IF ERR = 0 THEN PRINT "The backup is up to date" ELSE PRINT "The loader has changed"

Volumes and disks

How NESH names disks and file systems, and how to inspect them at a low level.

Volumes and block devices: map

The firmware exposes two kinds of storage objects. Volumes (fs0:, fs1:…) are file systems NESH can read and write files on. Block devices (blk0, blk1…) are raw disks, partitions and drives, including those with no file system the firmware understands (for example an NTFS or ext4 partition).

fs0:\> map
Volume Label                 Size      Free  Flags
fs0    QEMU VVFAT          503.7M    502.9M  ro boot
fs1    NESHWORK             31.9M     31.9M

Device Type            Size  Volume
blk0   removable          -  (no media)
blk1   disk            504M
blk2   partition       503M  fs0
blk3   disk             32M  fs1

The flags say ro (read-only), removable and boot (the volume NESH started from). map -v adds the device path of each entry, the firmware's full description of where the device is.

flowchart LR
  D["blk1<br/>whole disk"] --> P1["blk2<br/>partition 1"]
  D --> P2["blk4<br/>partition 2"]
  P1 --> F0["fs0:<br/>FAT file system"]
  P2 --> X["no volume<br/>(NTFS, ext4…)"]
A disk, its partitions and the volumes on them

Volume numbers and extra names

Volume numbers depend on the order in which the firmware found the devices, so fs1: today may be fs2: after plugging in another stick. Two ways to be independent of the numbering:

After connecting a device, map -r rescans the devices.

Labels and free space: vol

fs0:\> vol
Volume fs0: QEMU VVFAT (ro)
     528171008 bytes total disk space
     527351808 bytes available on disk
fs0:\> vol fs1: -n BACKUP

Raw blocks: dblk and hexedit

dblk DEVICE LBA COUNT shows raw disk blocks in hex and recognizes MBR partition tables, GPT headers and FAT boot sectors. hexedit -d DEVICE LBA COUNT opens blocks in the hex editor. Numbers are hexadecimal.

fs0:\> dblk blk1 0 1
Be careful

Writing raw blocks with hexedit -d bypasses the file system: a wrong byte in a partition table or boot sector can make a disk unbootable. Save a copy of the blocks you change first.

Text, time and data tools

Reading and searching text files, dumps, date and time. Text commands read UTF-8 and UCS-2 (“Unicode”) files and ignore Windows line ends.

CommandUseExample
cat / typePrint files.cat fs0:\efi\boot\grub.cfg
head / tailFirst / last lines.tail -n 20 log.txt
grepLines containing a text; -i any case, -n line numbers, -c count, -v non-matching.grep -in timeout *.cfg
wcCount lines, words and bytes.wc notes.txt
hexdumpHex dump of a file.hexdump -n 64 nesh.efi
date / timeShow or set the clock.date 2026-09-19
timezoneTime zone of the real-time clock.timezone -s +01:00
sleep / stallWait: sleep in milliseconds (Ctrl+C stops it), stall in microseconds.sleep 500
eficompress / efidecompressUEFI compression format, as used in option ROMs and firmware volumes.eficompress drv.efi drv.z
fs0:\> grep -n BOOT bootmenu.nsb
4:n = BOOTCOUNT
12:  id = BOOTID(i)
14:  items$ = items$ + BOOTDESC$(id)
25:    id = BOOTID(choice - 1)
26:    PRINT "Next boot: "; BOOTDESC$(id)
fs0:\> date
2026-09-19

The editors

Two full-screen editors are built in: edit for text files and hexedit for binary files, disk blocks and memory.

edit — text editor

edit FILE opens a file (or a new one, created when you save). Arrow keys, Home/End, PgUp/PgDn move around; typing inserts text. The function keys have Ctrl equivalents for terminals where function keys do not work (serial consoles):

KeyOrActionKeyOrAction
F1Ctrl+GGo to line F6Ctrl+KCut the current line
F2Ctrl+SSave F7Ctrl+UPaste the cut lines
F3Ctrl+QExit (also Esc) F8Ctrl+OOpen another file
F4Ctrl+FFind F9Ctrl+TSwitch UTF-8 / UCS-2
F5Ctrl+RFind and replace F10Ctrl+NFind next

Several cuts in a row form one block, and paste inserts the last block: to move lines, cut them one after the other, move the cursor and paste. Ctrl+E shows the key list inside the editor. The file keeps its encoding, its line ends (LF or CRLF), a UTF-8 byte-order mark if it had one, and its tab characters.

hexedit — binary editor

CommandEdits
hexedit FILEA file.
hexedit -d DEVICE LBA COUNTDisk blocks (DEVICE: blkN, fsN: or a handle).
hexedit -m ADDRESS SIZEMemory (writing is disabled while Secure Boot is active).

Arrow keys and PgUp/PgDn move, Tab switches between the hex and the text column, F1 goes to an offset, F2 saves, F3 exits.

Scripting with NESH BASIC

NESH scripts are written in NESH BASIC, a small and readable language in the tradition of BASIC: variables, loops, functions, strings and arrays, plus direct access to shell commands and firmware data. This chapter is a complete guide, from the first script to the full function reference.

Your first script

A script is a text file with the extension .nsb. Create it with edit hello.nsb (or on another computer) and run it by typing its name:

' hello.nsb - my first script
PRINT "Hello from NESH "; VERSION$
name$ = "world"
PRINT "Hello, " + name$ + "!"
fs0:\> hello
Hello from NESH 0.2.0
Hello, world!

Keywords and names are not case-sensitive: print, PRINT and Print are the same. This manual writes keywords in capitals to make them stand out.

Lines, statements and comments

RuleExample
One statement per line, or several separated by :a = 1 : b = 2
A long line continues on the next one when it ends with a space and _IF a = 1 AND _
Comments start with ' or REM and run to the end of the linex = 5 ' five
Strings are in double quotes; "" inside a string is one quote"say ""hi"""
Files are UTF-8 (a byte-order mark is allowed) or UCS-2; #! on the first line is ignored

Variables, numbers and strings

There are two types, chosen by the name:

Variables need no declaration: an unused number variable is 0 and an unused string is "". Assigning a string to a number variable (or the reverse) is an error; convert with STR$ and VAL.

count = 42                 ' a number (64-bit integer)
size = &H1000              ' hexadecimal: 4096
mask = 0b1010              ' binary: 10
label$ = "EFI System"      ' a string: the name ends with $
PRINT count, size, mask
PRINT label$; " has "; LEN(label$); " characters"
PRINT 7 / 2, 7 MOD 2, 2 ^ 10, -7 \ 2
PRINT "a" < "b", 3 > 5
fs0:\> vars
42            4096          10
EFI System has 10 characters
3             1             1024          -3
-1            0
Note

Unlike classic BASIC, PRINT adds no spaces around numbers: PRINT "has"; 10 prints has10. Put the spaces in the strings. A comma moves to the next 14-column zone, which is handy for simple tables.

Operators

PriorityOperatorsMeaning
1 (first)^Power (right to left: 2^3^2 = 29)
2-x   +xSign
3*   /   \   MODMultiply; divide (both / and \ are integer divisions, rounding toward zero); remainder
4+   -Add, subtract; + also joins strings
5SHL   SHRShift bits left / right (1 SHL 4 = 16)
6= <> < > <= >=Comparisons (numbers or strings)
7NOTBitwise not
8ANDBitwise and
9 (last)OR   XORBitwise or, exclusive or

A comparison gives -1 when true and 0 when false, as in classic BASIC; any non-zero number (or non-empty string) counts as true in IF and loops. Because true is -1 (all bits set), AND, OR and NOT work both on bits and on conditions. Both sides of AND/OR are always evaluated. Strings compare byte by byte, so the comparison is case-sensitive: use UCASE$ on both sides to ignore case.

Output and input

StatementEffect
PRINT a; b$, cPrints values; ; joins, , moves to the next 14-column zone. A final ; or , keeps the cursor on the line.
INPUT "Name: "; n$Asks for a line of text (or a number, for a number variable: it asks again until one is typed). Ctrl+C stops the script.
k$ = KEY$(5000)Waits up to 5 s for one key (see KEY$).
PAUSE ["message"]Waits for a key.
CLSClears the screen.
COLOR fg [, bg]Text colors: foreground 0–15, background 0–7 (0 black, 1 blue, 2 green, 3 cyan, 4 red, 5 magenta, 6 brown, 7 light gray, 8–15 bright versions). COLOR alone restores the default.
LOCATE row, colMoves the cursor (counted from 1).
SLEEP msWaits the given milliseconds (Ctrl+C interrupts).

Working with strings

Positions count characters (not bytes) and start at 1.

path$ = "fs0:\EFI\BOOT\BOOTX64.EFI"
PRINT LEFT$(path$, 4)                  ' fs0:
PRINT MID$(path$, 6, 3)                ' EFI
PRINT RIGHT$(path$, 11)                ' BOOTX64.EFI
PRINT INSTR(path$, "BOOT")             ' position of the first BOOT
PRINT LCASE$(path$)
PRINT REPLACE$(path$, "\", "/")
PRINT "[" + TRIM$("   padded   ") + "]"
PRINT LPAD$("7", 3, "0"); " "; RPAD$("ab", 5, "."); "|"
PRINT HEX$(255), HEX$(255, 4), BIN$(5, 8)
PRINT VAL("42") + 1, STR$(99) + "!", VAL("0x1F")
PRINT CHR$(65); ASC("B"); " "; STRING$(5, "-")
fs0:\> strings
fs0:
EFI
BOOTX64.EFI
10
fs0:\efi\boot\bootx64.efi
fs0:/EFI/BOOT/BOOTX64.EFI
[padded]
007 ab...|
FF            00FF          00000101
43            99!           31
A66 -----

Decisions and loops

IF has a one-line form and a block form:

IF free < 1000000 THEN PRINT "almost full" ELSE PRINT "ok"

IF ERR = 0 THEN
  PRINT "done"
ELSEIF ERR = 127 THEN
  PRINT "command not found"
ELSE
  PRINT "failed with code "; ERR
END IF

Loops: FOR counts, WHILE repeats while a condition holds, DO … LOOP tests at the start or at the end (WHILE or UNTIL):

FOR i = 1 TO 3
  IF i = 2 THEN
    PRINT i; " is two"
  ELSE
    PRINT i
  END IF
NEXT
n = 10
WHILE n > 1
  IF n MOD 2 = 0 THEN n = n / 2 ELSE n = 3 * n + 1
  PRINT n;
  IF n > 1 THEN PRINT " ";
WEND
PRINT
DO
  tries = tries + 1
LOOP UNTIL tries = 3
PRINT "tries:"; tries
FOR i = 10 TO 0 STEP -5: PRINT i;: NEXT: PRINT
fs0:\> control
1
2 is two
3
5 16 8 4 2 1
tries:3
1050

SELECT CASE compares one value against lists, ranges (TO) and conditions (IS):

FOR code = 0 TO 4
  SELECT CASE code
    CASE 0
      msg$ = "success"
    CASE 1, 2
      msg$ = "failed"
    CASE 3 TO 9
      msg$ = "unusual"
  END SELECT
  PRINT code; ": "; msg$
NEXT
SELECT CASE "fs1:"
  CASE IS < "fs1:"
    PRINT "before"
  CASE ELSE
    PRINT "fs1: or later"
END SELECT
fs0:\> select
0: success
1: failed
2: failed
3: unusual
4: unusual
fs1: or later

GOTO jumps to a label, a name followed by : alone on its line. It can jump within the same block or out of loops, not into them. Structured statements are usually clearer.

i = 0
again:
i = i + 1
IF i < 3 THEN GOTO again
PRINT "i ="; i
fs0:\> gotolabels
i =3

Arrays

DIM name(N) creates an array with the elements 0 to N (so N+1 elements); string arrays end with $. Arrays have one dimension. REDIM changes the size and keeps the values; UBOUND gives the last index. Functions that take a whole array use the name with empty parentheses, like disk$().

DIM disk$(2)                  ' three elements: 0, 1 and 2
disk$(0) = "fs0:"
disk$(1) = "fs1:"
disk$(2) = "fs2:"
PRINT UBOUND(disk$()); " is the last index"
PRINT JOIN$(disk$(), ", ")
n = SPLIT("ata;nvme;usb", ";", bus$())
FOR i = 0 TO n - 1
  PRINT i; "="; bus$(i)
NEXT
REDIM disk$(3)                ' grow, keeping the values
disk$(3) = "fs3:"
PRINT JOIN$(disk$(), " ")
fs0:\> arrays
2 is the last index
fs0:, fs1:, fs2:
0=ata
1=nvme
2=usb
fs0: fs1: fs2: fs3:

SPLIT and RECORDS create or resize the array they fill, so it needs no DIM.

SUB and FUNCTION

A SUB is a named block of statements; a FUNCTION also returns a value with RETURN (a function whose name ends with $ returns a string). They can be defined anywhere in the script at the top level, also after the lines that use them.

FUNCTION human$(bytes)
  ' 1536 -> "1 KiB"
  LOCAL i, u$(4)
  i = SPLIT("B KiB MiB GiB TiB", " ", u$())
  i = 0
  WHILE bytes >= 1024 AND i < 4
    bytes = bytes \ 1024
    i = i + 1
  WEND
  RETURN STR$(bytes) + " " + u$(i)
END FUNCTION

SUB banner(title$)
  PRINT STRING$(LEN(title$) + 4, "=")
  PRINT "= "; title$; " ="
  PRINT STRING$(LEN(title$) + 4, "=")
END SUB

banner "Disk sizes"
PRINT human$(512)
PRINT human$(1536)
PRINT human$(250059350016)
fs0:\> procs
==============
= Disk sizes =
==============
512 B
1 KiB
232 GiB

Running commands from a script

Scripts run shell commands with RUN, capture their output with RUN$() and check the result in ERR:

RUN "mkdir -p backup"
PRINT "mkdir: ERR="; ERR
RUN "cp", "hello.nsb", "backup\hello copy.nsb"
out$ = RUN$("ls backup")
PRINT "backup contains: "; out$
RUN "ls", "nothing-here"
IF ERR <> 0 THEN PRINT "ls failed with code "; ERR
RUN "echo saved by a script" TO "log.txt"
RUN "echo second line" APPEND "log.txt"
PRINT READFILE$("log.txt");
fs0:\> runcmd
mkdir: ERR=0
backup contains: hello copy.nsb
ls: nothing-here: not found
ls failed with code 1
saved by a script
second line
FormUse
RUN "cmd arg1 arg2"One string: split like a command line (quotes group words, > file works).
RUN "cmd", a$, b$Separate arguments, passed exactly as they are — the safe way for file names with spaces or quotes.
RUN … TO "file" / RUN … APPEND "file"Output to a file (replace / add).
x$ = RUN$(…)Same arguments as RUN; returns the output as a string, without the final newlines. Error messages still go to the screen.
ERRExit code of the last command (0 = success).

Scripts and .efi programs are run the same way: RUN "backup fs1:". Each script has its own variables; values are passed with arguments, files or environment variables.

Arguments and exit code

ARGC is the number of arguments and ARG$(n) the n-th one (ARG$(0) is the script's own path). END code stops the script and sets its exit code, which the caller sees in ERR.

' args.nsb: prints its arguments
PRINT "script: "; ARG$(0)
PRINT "arguments:"; ARGC
FOR i = 1 TO ARGC
  PRINT i; ": "; ARG$(i)
NEXT
IF ARGC = 0 THEN
  PRINT "usage: args NAME..."
  END 2
END IF
RUN "args one ""two words"""
RUN "args"
PRINT "exit code:"; ERR
fs0:\> callargs
script: fs0:\args.nsb
arguments:2
1: one
2: two words
script: fs0:\args.nsb
arguments:0
usage: args NAME...
exit code:2
To stop…Use
this script, with an exit codeEND 2 (also EXIT 2, or RUN "exit /b 2")
all scripts and leave NESHRUN "exit 2"

Reading and writing files

WRITEFILE "notes.txt", "first line" + CHR$(10)
APPENDFILE "notes.txt", "second line" + CHR$(10)
PRINT "size:"; FILESIZE("notes.txt")
text$ = READFILE$("notes.txt")
n = SPLIT(TRIM$(text$), CHR$(10), line$())
PRINT n; " lines, the second is: "; line$(1)
IF FILEEXISTS("notes.txt") THEN PRINT "notes.txt exists"
x$ = READFILE$("missing.txt")
PRINT "reading a missing file: ERR="; ERR
f$ = DIR$("*.txt")
WHILE f$ <> ""
  PRINT "found "; f$
  f$ = DIR$
WEND
fs0:\> files
size:23
2 lines, the second is: second line
notes.txt exists
reading a missing file: ERR=1
found log.txt
found notes.txt

File functions never stop the script: they set ERR (0 success, 1 failure), so the script can react. READFILE$ reads UTF-8 and UCS-2 text and removes carriage returns; WRITEFILE and APPENDFILE write UTF-8. DIR$(pattern$) returns the first matching name and each following DIR$ the next one, then "".

Environment variables in scripts

PRINT "search path: "; ENV$("path")
SETENV "backupdir", "fs1:\backup"
PRINT "backupdir = "; ENV$("backupdir")
x$ = ENV$("nosuchvar")
PRINT "missing variable: ERR="; ERR
DELENV "backupdir"
fs0:\> env
search path: .;fs0:\efi\tools;fs0:\efi\boot;fs0:\
backupdir = fs1:\backup
missing variable: ERR=1

Errors

A mistake in the script (a syntax error, a division by zero, an index out of range, a type mismatch) stops it with a message that names the file and the line; the exit code is 1. A syntax error is reported before anything runs.

PRINT "before"
x = 10
y = 0
PRINT x / y
PRINT "never printed"
fs0:\> errors
before
fs0:\errors.nsb:4: error: division by zero

Command failures are not script errors: RUN sets ERR and the script continues. Check ERR after the commands that matter. nesh.efi -k SCRIPT checks the syntax of a script without running it.

MENU(title$, items$ [, timeout [, default]]) shows a menu and returns the number of the chosen item (counted from 1), or 0 if Esc was pressed. Items are separated by |. The user moves with the arrow keys and chooses with Enter, or presses the item number. With a timeout (in seconds), the default item is chosen when time runs out; any key stops the countdown.

choice = MENU("Backup", "Copy the loaders|Copy everything|Cancel", 15, 1)
IF choice = 1 THEN RUN "cp -r fs0:\efi fs1:\backup"
IF choice = 2 THEN RUN "cp -r fs0:\ fs1:\backup"
Backup
  1. Copy the loaders
  2. Copy everything
  3. Cancel
Up/Down + Enter to choose, ESC to cancel. Default in 15 s

A menu usually sits in a loop, so that the program comes back to it after each action; examples\menu.nsb is a complete one:

DO
  choice = MENU("What do you want to do?", _
                "Show the system|List the boot entries|Leave the menu")
  SELECT CASE choice
    CASE 1
      RUN "sysinfo"
    CASE 2
      RUN "bootmgr list"
    CASE 0, 3          ' 0 means Esc
      END 0
  END SELECT
  PAUSE "Press a key to go back to the menu..."
LOOP

KEY$ reads single keys: KEY$ returns a key if one is waiting (or ""), KEY$(ms) waits up to ms milliseconds (−1: forever). Special keys have names: "UP", "DOWN", "LEFT", "RIGHT", "HOME", "END", "PGUP", "PGDN", "INSERT", "DELETE", "ENTER", "ESC", "TAB", "BACKSPACE", "F1""F10".

Worked example: an interactive boot menu

This script shows a menu of the boot entries each time the computer starts, with a 10-second countdown, and starts the chosen system. It is the examples\bootmenu.nsb file shipped with NESH. It uses three things explained in this manual: -data output read with RECORDS and FIELD$, the MENU function, and the boot commands.

flowchart TD
  A["bootmgr -data<br/>read the boot entries"] --> B{"entry in the boot order,<br/>active, not hidden,<br/>not this shell?"}
  B -- yes --> C["add it to the menu"]
  B -- no --> D["skip it"]
  C --> E["MENU with a 10 s countdown"]
  D --> E
  E -- "a system" --> F["bootmgr next -B ID<br/>(BootNext: next boot only)"]
  F --> G["reset: the firmware starts it"]
  E -- "Firmware setup" --> H["reset setup"]
  E -- "Power off" --> I["reset -s"]
  E -- "Stay / Esc" --> J["NESH prompt"]
How the boot menu works
' bootmenu.nsb - interactive boot menu built from the firmware boot entries.
' Copy it to the root of the boot volume as startup.nsb to show it at every start.

' The entries, from "bootmgr -data": one record per entry, in boot order.
n = RECORDS(RUN$("bootmgr -data"), rec$())
DIM id$(n), desc$(n)
count = 0
items$ = ""
FOR i = 0 TO n - 1
  r$ = rec$(i)
  ' keep the entries that are in the boot order, active, not hidden and
  ' not the one that started this shell
  IF FIELD$(r$, "kind") = "entry" AND FIELD$(r$, "order") <> "-" AND _
     FIELD$(r$, "active") = "yes" AND FIELD$(r$, "hidden") = "no" AND _
     FIELD$(r$, "current") = "no" THEN
    id$(count) = FIELD$(r$, "id")
    desc$(count) = FIELD$(r$, "description")
    items$ = items$ + desc$(count) + "|"
    count = count + 1
  END IF
NEXT
items$ = items$ + "Firmware setup|Power off|Stay in the shell"

' 10 seconds to choose; then the first entry starts
choice = MENU("Start which system?", items$, 10, 1)

SELECT CASE choice
  CASE 1 TO count
    PRINT "Starting "; desc$(choice - 1); "..."
    ' BootNext: used once at the next boot; -B: no backup for this change
    RUN "bootmgr next -B " + id$(choice - 1)
    IF ERR = 0 THEN RUN "reset"
    PRINT "Cannot set the next boot entry (error "; ERR; ")"
  CASE count + 1
    RUN "reset setup"
  CASE count + 2
    RUN "reset -s"
  CASE ELSE
    PRINT "Staying in the shell."
END SELECT

How it works:

  1. Read the entries. bootmgr -data prints one record for the boot settings and one per boot entry, in boot order, with fields such as id, description, active and hidden. RECORDS splits them into the array rec$().
  2. Choose what to show. Entries outside the boot order, disabled, hidden (such as the firmware's own setup application) and the entry that started NESH are skipped. The IDs and names are kept in two arrays, and the names are joined with | for MENU.
  3. Ask. MENU returns the chosen number, or the first entry after 10 seconds.
  4. Start the system. bootmgr next ID sets BootNext, a firmware variable that says “start this entry once, at the next boot”, and reset restarts the machine; the firmware then starts the chosen entry and afterwards goes back to the normal boot order. The option -B skips the automatic backup that bootmgr otherwise saves before every change, which would pile up at every start (and fail on a read-only volume).

To install it, copy the file to the root of the volume NESH starts from with the name startup.nsb, and make NESH the first boot entry (bootmgr top ID). The menu then appears at every start; Esc during the three-second countdown before it (or the “Stay in the shell” item) gives you the prompt.

fs0:\> cp fs0:\examples\bootmenu.nsb fs0:\startup.nsb
Ideas to extend it
  • Show only some systems: test FIELD$(r$, "description") with INSTR.
  • Remember the last choice: write the ID to a file with WRITEFILE and pass its position as the default item of MENU.
  • Add your own items, for example a tool on the disk: run it with RUN "fs0:\efi\tools\memtest.efi" when chosen.

Starting a Linux kernel with different options

A menu does not have to choose between different systems: it can choose between different ways of starting the same one. Almost every x86-64 distribution ships a kernel built as an EFI application (the “EFI stub”), so NESH can start vmlinuz itself and give it a command line — exactly what a boot loader does. The kernel is usually called vmlinuz, without an extension, and NESH runs it as it is. Anything the script writes after the file name reaches the kernel:

fs0:\> vmlinuz initrd=initrd.img root=/dev/sda2 ro quiet splash

In a script the line is built as a string, so one entry per set of options is all it takes:

kernel$ = "fs0:\vmlinuz"
root$   = "root=/dev/sda2 ro"

choice = MENU("Start Linux", "Normal|Safe graphics|Rescue", 10, 1)
SELECT CASE choice
  CASE 1 : opt$ = root$
  CASE 2 : opt$ = root$ + " nomodeset"
  CASE 3 : opt$ = root$ + " single"
END SELECT
RUN kernel$ + " initrd=initrd.img " + opt$

examples\linuxboot.nsb is the finished version, with a ten-second countdown and an entry that asks for the options from the keyboard. Two details are worth knowing:

Kernel or boot entry?

Starting the kernel directly, as above, keeps everything in one script and lets you change the options without touching the firmware. The other way is to create one firmware boot entry per set of options — bootmgr add fs0:\vmlinuz "Linux (rescue)" "initrd=initrd.img root=/dev/sda2 ro single" — and let the menu pick one with bootmgr next ID followed by reset. Use that when other boot menus, or the firmware itself, must see the entries too.

More examples

The examples folder contains complete scripts:

ScriptWhat it shows
bootmenu.nsbThe interactive boot menu above.
findefi.nsbLooks for boot loaders in \EFI\*\ on every volume: map -data, ls -ad -data, DIR$, a SUB with a LOCAL variable.
linuxboot.nsbOne Linux kernel, several sets of options: a countdown menu that builds the kernel command line, with an entry that asks for the options.
inventory.nsbWrites a text report of the machine (system, volumes, boot entries, memory) with RUN$ and APPENDFILE.
menu.nsbA maintenance menu that comes back after every action, with a second menu built at run time from the volumes found by map -data.
' findefi.nsb - lists the boot loaders in \EFI\*\ of every volume.

SUB scan(folder$)
  LOCAL f$
  f$ = DIR$(folder$ + "\*.efi")
  WHILE f$ <> ""
    PRINT "  "; folder$; "\"; f$
    f$ = DIR$
  WEND
END SUB

nv = RECORDS(RUN$("map -t fs -data"), vol$())
FOR v = 0 TO nv - 1
  name$ = FIELD$(vol$(v), "volume") + ":"
  IF DIREXISTS(name$ + "\EFI") THEN
    PRINT name$; "  "; FIELD$(vol$(v), "label")
    ' the folders inside \EFI (-ad: directories only)
    nd = RECORDS(RUN$("ls -ad -data " + name$ + "\EFI"), sub$())
    FOR i = 0 TO nd - 1
      scan name$ + "\EFI\" + FIELD$(sub$(i), "name")
    NEXT
  END IF
NEXT

Statement reference

StatementMeaning
[LET] var = expr, arr(i) = exprAssignment (LET is optional).
PRINT [expr {; | ,} …]Output; see Output and input.
INPUT ["prompt";] varRead a line from the keyboard.
IF … THEN … [ELSEIF …] [ELSE …] END IFDecision (block or one-line form).
FOR v = a TO b [STEP s] … NEXT [v]Counting loop.
WHILE cond … WEND (or END WHILE)Loop while true.
DO [WHILE|UNTIL cond] … LOOP [WHILE|UNTIL cond]General loop.
SELECT CASE expr … CASE list … CASE ELSE … END SELECTMultiple choice; a list item is a value, a TO b or IS op value.
EXIT FOR|WHILE|DO|SUB|FUNCTIONLeave a loop or a procedure.
CONTINUE FOR|WHILE|DONext round of a loop.
GOTO labelJump to label: (alone on its line).
SUB name[(params)] … END SUBDefine a procedure.
FUNCTION name[(params)] … END FUNCTIONDefine a function.
CALL name(args), name argsCall a SUB.
RETURN [expr]Leave a SUB/FUNCTION, with the function's value.
LOCAL v, a$(n), …Local variables and arrays of a SUB/FUNCTION.
DIM a(n), b$(m), …Create arrays (indexes 0 to n).
REDIM a(n)Resize an array, keeping its values.
RUN args [TO|APPEND file]Run a shell command; see Running commands.
END [code], EXIT [code]End the script with an exit code.
CLS, COLOR [fg[, bg]], LOCATE row, colScreen control.
SLEEP ms, PAUSE ["msg"]Wait.
' text, REM textComment.

Functions that do something rather than compute a value — SETENV, DELENV, WRITEFILE, APPENDFILE, SETVAR, SETVARSTR, DELVAR — can also be written as statements, without parentheses: WRITEFILE "a.txt", text$.

Function reference

Arguments ending in $ are strings, the others numbers. Functions without arguments are written without parentheses (ERR, TICKS). Positions in strings start at 1.

Strings

LEN(s$)
Number of characters.
LEFT$(s$, n)
The first n characters.
RIGHT$(s$, n)
The last n characters.
MID$(s$, start [, n])
n characters from position start (all the rest without n).
INSTR([start,] s$, find$)
Position of find$ in s$ (from start), 0 if absent. Case-sensitive.
UCASE$(s$) · LCASE$(s$)
Upper / lower case (letters A–Z only).
TRIM$(s$) · LTRIM$(s$) · RTRIM$(s$)
Remove spaces (and tabs, newlines) at both ends / the start / the end.
REPLACE$(s$, find$, new$)
Replace every find$ with new$.
STRING$(n, c$ | code)
n copies of a character: STRING$(3, "*") = "***".
SPACE$(n)
n spaces.
LPAD$(s$, width [, c$]) · RPAD$(s$, width [, c$])
Pad on the left / right to width characters with spaces (or c$); longer strings are not cut.
CHR$(code)
The character with a Unicode code: CHR$(10) is a newline.
ASC(s$)
Unicode code of the first character.
SPLIT(s$, sep$, arr$())
Split s$ at every sep$ into arr$(); returns the number of parts. With sep$ = "" it splits at runs of spaces.
JOIN$(arr(), sep$)
Join the elements of an array with sep$ between them.

Numbers and conversions

VAL(s$)
Number in a string: decimal, 0x/&H hex, 0b/&B binary, with an optional sign; leading spaces are skipped and reading stops at the first invalid character. 0 if none.
STR$(n)
Number as a decimal string.
HEX$(n [, digits]) · BIN$(n [, digits]) · OCT$(n [, digits])
Number in hexadecimal (upper case), binary or octal, padded with zeros to digits. Negative numbers show their 64-bit two's complement.
ABS(n) · SGN(n)
Absolute value; sign (−1, 0, 1).
MIN(a, b, …) · MAX(a, b, …)
Smallest / largest of up to 16 numbers.
RND · RND(n)
A random number; with n, a random number from 0 to n−1.
UBOUND(arr())
Last index of an array (−1 for an empty one).

Program, time and keyboard

ERR
Result of the last command or of the last function that reports one (0 = success).
ARGC · ARG$(n)
Number of arguments of the script; the n-th argument (ARG$(0): the script path).
RUN$(cmd$ [, arg$, …])
Run a command and return its output (final newlines removed); sets ERR.
TICKS
Milliseconds counter, for measuring time.
DATE$ · TIME$
Current date YYYY-MM-DD and time HH:MM:SS.
KEY$ · KEY$(ms)
A key press: immediately ("" if none), or waiting up to ms milliseconds (−1 = no limit). Special keys return names such as "UP", "ENTER", "ESC", "F1".
MENU(title$, items$ [, timeout [, default]])
Interactive menu; items separated by |. Returns the item number (from 1) or 0 for Esc. See Menus and keys.
PLATFORM$ · VERSION$
The platform ("UEFI x86_64") and the NESH version.

Files and folders

CWD$
Current directory, e.g. fs0:\efi.
FILEEXISTS(p$) · DIREXISTS(p$)
True (−1) if the file / directory exists.
FILESIZE(p$)
Size in bytes; −1 and ERR = 1 if the file does not exist.
READFILE$(p$)
Contents of a text file (UTF-8 or UCS-2); "" and ERR = 1 on error.
WRITEFILE p$, s$ · APPENDFILE p$, s$
Write / add text (UTF-8) to a file; ERR = 0 on success.
DIR$(pattern$) · DIR$
First file name matching a pattern (or in a directory), then the next ones; "" at the end.

Environment and command output

ENV$(name$)
Value of an environment variable; "" and ERR = 1 if it does not exist.
SETENV name$, value$ [, permanent]
Set a variable, temporary unless permanent is true.
DELENV name$
Delete a variable (ERR = 1 if it did not exist).
RECORDS(text$, arr$())
Split -data output into records; returns their number. See Script-friendly output.
FIELD$(record$, key$)
Value of key=value in a record; "" and ERR = 1 if missing.

Firmware: UEFI variables and Secure Boot

guid$ is a GUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) or one of the names global (the default), security, shim, shell, systemd. After a firmware error ERR holds the low byte of the UEFI status code (for example 14 = not found).

VAREXISTS(name$ [, guid$])
True if the variable exists.
VAR$(name$ [, guid$])
Value as text: UCS-2 strings are converted; other data is returned as it is, up to the first zero byte (use VARHEX$ for binary data).
VARHEX$(name$ [, guid$])
Value as hex bytes, e.g. "0100".
VARNUM(name$ [, guid$])
Value as a little-endian number (e.g. Timeout).
SETVAR name$, hex$ [, guid$ [, attr]]
Write bytes given in hex. Without attr an existing variable keeps its attributes and a new one is non-volatile with boot and runtime access (7).
SETVARSTR name$, text$ [, guid$ [, attr]]
Write a UCS-2 string (with terminator).
DELVAR name$ [, guid$]
Delete a variable.
FWVENDOR$
Firmware vendor, e.g. "American Megatrends".
SECUREBOOT
True if Secure Boot is active.

Firmware: boot entries

Entries are identified by their number (the #### of Boot####, e.g. &H0003).

BOOTCOUNT
Number of entries in the boot order.
BOOTID(i)
Number of the i-th entry of the boot order (i from 0).
BOOTDESC$(id)
Description; ERR = 127 if the entry does not exist.
BOOTPATH$(id)
Device path, as text.
BOOTARGS$(id)
Optional data (arguments), as text.
BOOTENABLED(id)
True if the entry is active.
BOOTCURRENT
Number of the entry used for this boot, −1 if unknown.
' list the boot order with the descriptions
FOR i = 0 TO BOOTCOUNT - 1
  id = BOOTID(i)
  PRINT HEX$(id, 4); "  "; BOOTDESC$(id)
NEXT
PRINT "Timeout: "; VARNUM("Timeout"); " s, Secure Boot: "; SECUREBOOT

Script-friendly output (-data)

Command output is designed for people: aligned columns, headings, units. Scripts need something simpler. With the option -data, many commands print their information as records of key=value lines that a script reads with two functions.

The format

fs0:\> map -data
kind=volume
volume=fs0
label=QEMU VVFAT
size=528171008
free=527351808
readonly=yes
removable=no
boot=yes
aliases=
devpath=PciRoot(0x0)/Pci(0x2,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)

kind=volume
volume=fs1
label=NESHWORK
…

Reading records: RECORDS and FIELD$

RECORDS(text$, arr$()) splits the output into an array with one record per element and returns the number of records. FIELD$(record$, key$) returns the value of one field, or "" with ERR = 1 when the record has no such key.

RUN "mkdir d"
WRITEFILE "d\a.txt", "hello"
WRITEFILE "d\big.bin", STRING$(3000, "x")
n = RECORDS(RUN$("ls -data d"), f$())
FOR i = 0 TO n - 1
  PRINT RPAD$(FIELD$(f$(i), "name"), 10); LPAD$(FIELD$(f$(i), "size"), 6); " bytes"
NEXT
fs0:\> data
a.txt          5 bytes
big.bin     3000 bytes
rec$ = "name=a.txt" + CHR$(10) + "size=5" + CHR$(10)
PRINT FIELD$(rec$, "size")
x$ = FIELD$(rec$, "owner")
PRINT "[" + x$ + "] ERR="; ERR
fs0:\> datafield
5
[] ERR=1

A typical use: find a volume by its label instead of its number, which can change.

' returns the volume with the given label, e.g. "fs1:", or "" if none
FUNCTION volume$(label$)
  LOCAL n, i, v$(0)
  n = RECORDS(RUN$("map -data"), v$())
  FOR i = 0 TO n - 1
    IF FIELD$(v$(i), "kind") = "volume" AND UCASE$(FIELD$(v$(i), "label")) = UCASE$(label$) THEN
      RETURN FIELD$(v$(i), "volume") + ":"
    END IF
  NEXT
  RETURN ""
END FUNCTION

target$ = volume$("BACKUP")
IF target$ = "" THEN
  PRINT "Please insert the BACKUP disk"
  END 1
END IF
RUN "cp", "-r", "fs0:\efi", target$ + "\efi"

Commands with -data

CommandOne record perFields
ls, dir, statfile or directoryname, path, type, size, modified, readonly, hidden, system, archive
mapvolume / block devicekind=volume: volume, label, size, free, readonly, removable, boot, aliases, devpath; kind=block: device, type, media, size, blocksize, readonly, volume, devpath
volvolumevolume, label, size, free, readonly
ver(one record)shell, version, platform, firmware, firmware_revision, uefi, secure_boot
sysinfo(one record)firmware, cpu, memory, console and graphics sizes, config_tables, acpi, smbios, boot_volume…
memmapmemory descriptor (with -s: type)type, start, end, pages, attributes (-s: type, pages, bytes)
date, time(one record)date, year, month, day / time, hour, minute, second
set, aliasvariable / aliasname, value, kind
var listUEFI variablename, guid, guid_name, attributes, size
bootmgrsettings + boot entrykind=settings: bootcurrent, bootnext, timeout; kind=entry: id, variable, order, active, hidden, current, next, description, devpath, args
driversdriverhandle, version, type, config, diag, controllers, children, name, image
devicesdevicehandle, type, config, diag, parents, drivers, children, name
dhhandlehandle, name, protocols (and devicepath with -d)
pciPCI function (one device: its decoded header)segment, bus, device, function, vendor, deviceid, class, classname…
smbiosviewSMBIOS structuretype, typename, handle, length + decoded fields (vendor, version, serial, size…)
acpiviewACPI tablesignature, address, length, revision, oemid, oemtable, checksum
ifconfig -l, ifconfig6 -lnetwork interfacename, media, policy, mac, ip, mask (IPv4), gateway, dns (dad for IPv6)

The exact fields of each command are also listed at the end of its help. Commands without -data reject the option with an error. The UEFI Shell's -sfo format is not supported: -data replaces it.

Managing boot entries

The firmware decides what to start from a list of boot entries kept in UEFI variables. NESH shows and changes them safely with bootmgr, and also offers the UEFI Shell's bcfg.

How UEFI boots

flowchart LR
  P["Power on"] --> N{"BootNext set?"}
  N -- yes --> X["start Boot#### = BootNext<br/>(BootNext is cleared)"]
  N -- no --> O["try the entries of BootOrder<br/>in order"]
  O --> E["Boot####: description,<br/>device path, arguments"]
  E --> L["boot loader, OS,<br/>or NESH"]
  X --> L
What the firmware does at power-on
VariableContent
Boot####One boot entry (#### is its hexadecimal ID): description, device path of the file or device to start, optional arguments, and flags active (used) and hidden (not shown in the firmware menu).
BootOrderThe list of IDs, in the order the firmware tries them.
BootNextAn ID to start once, at the next boot only.
BootCurrentThe ID used for the current boot (set by the firmware).
TimeoutSeconds the firmware boot menu waits.

Listing entries

fs0:\> bootmgr
BootCurrent: 0001   BootNext: -   Timeout: 0 s
Ord  Id    Flg  Description
  1  0000  AH   UiApp
                 Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(462CAA21-7614-4503-836E-8AB6F4662331)
  2  0001  A *  UEFI Misc Device
                 PciRoot(0x0)/Pci(0x2,0x0)
  3  0002  A    UEFI Non-Block Boot Device
                 PciRoot(0x0)/Pci(0x3,0x0)
  4  0003  A    EFI Internal Shell
                 Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(7C04A583-9E3E-4F1C-AD65-E05268D0B4D1)
Flags: A active, H hidden, * current boot, N next boot. '-' in Ord: not in the boot order.

bootmgr list -v also shows the arguments of each entry.

Adding an entry

fs0:\> bootmgr add fs0:\efi\tools\nesh.efi "NESH shell" -t

This creates a Boot#### entry that starts nesh.efi from that volume, and -t puts it first in the boot order. Words after the description become the arguments passed to the program. Words starting with - are read as options of bootmgr itself; to give the program such an argument, set the arguments afterwards with bootmgr edit ID -a "-n".

Safety: dry run, confirmation, backups

Every bootmgr command that changes something follows the same steps:

  1. Plan — it prints the changes it is going to make. With -n (dry run) it stops here.
  2. Confirm — it asks Apply these changes? [y/N], unless -y is given. (bootmgr next never asks: it only affects one boot.)
  3. Back up — it saves all boot variables to \nesh\backup on the boot volume, unless -B is given. If the backup cannot be written the change is not made.
  4. Apply the changes.

bootmgr restore FILE puts back a saved state; bootmgr backup FILE saves one on demand.

Common tasks

TaskCommand
Start an entry once, at the next bootbootmgr next 0003, then reset
Make an entry the defaultbootmgr top 0003
Set the whole orderbootmgr order 0003,0001,0002
Disable / enable an entry without deleting itbootmgr disable 0002 / bootmgr enable 0002
Rename an entry, change its file or argumentsbootmgr edit 0003 -D "Ubuntu"
Delete an entrybootmgr del 0004
Find entries whose file no longer existsbootmgr check
Change the firmware menu timeoutbootmgr timeout 5
Work on driver entries (Driver####)add -driver to the command
Reboot into the firmware setupreset setup

The same information is available to scripts through bootmgr -data and the boot functions; the boot menu example puts them together.

bcfg (UEFI Shell syntax)

bcfg works like in the UEFI Shell, using positions in the boot order rather than IDs: bcfg boot dump, bcfg boot add 0 fs0:\efi\tools\nesh.efi "NESH", bcfg boot mv 3 0, bcfg boot rm 2. It asks nothing (as in the UEFI Shell) but still saves a backup when it can. Existing UEFI Shell scripts and instructions that use bcfg work as they are.

UEFI variables

UEFI variables are small named values stored by the firmware — most of them in non-volatile memory (NVRAM). The boot entries, the Secure Boot keys, the language and the shell's own settings live there.

Names, GUIDs and attributes

A variable is identified by a name and a GUID (the “namespace” of its owner). Standard UEFI variables use the global GUID. NESH knows a few GUIDs by name: global, security (Secure Boot databases), shim, shell (UEFI Shell and NESH settings), systemd.

AttributeMeaning
NVNon-volatile: kept across reboots.
BSBoot services: visible before the OS starts.
RTRuntime: also visible to the operating system.
AAuthenticated: can only be changed with a signed update (Secure Boot keys).

The var command

fs0:\> var list Boot*
global                                 BSRT       4  BootOptionSupport
global                                 BSRT       2  BootCurrent
global                               NVBSRT      62  Boot0000
global                               NVBSRT      78  Boot0001
global                               NVBSRT       8  BootOrder
fs0:\> var get BootOrder
0000,0001,0002,0003
fs0:\> var set MyTool.Mode fast -g shell -t ascii
fs0:\> var save fs1:\vars.txt
fs0:\> var del MyTool.Mode -g shell

var get decodes the value when it can (numbers, strings, lists such as BootOrder); -x shows a hex dump. var save writes variables to a readable text file and var load restores them.

The UEFI Shell commands dmpstore (dump, save, load and delete variables, with the same binary file format as the UEFI Shell) and setvar are available too. Scripts use VAR$, VARNUM, SETVAR and the other firmware functions.

Careful

Deleting or corrupting firmware variables can make a machine unbootable or reset its setup. Save them first (var save or dmpstore -all -s FILE) and change only variables you understand.

Drivers and devices

UEFI firmware is built from drivers that find devices and offer services to programs. These commands show how the pieces fit together, and let you load, connect and remove drivers.

The UEFI driver model in brief

flowchart TD
  PCI["PCI bus driver"] -- "creates" --> C1["controller handle<br/>(a disk controller)"]
  D1["disk driver"] -- "manages" --> C1
  D1 -- "installs BlockIo on" --> C1
  PART["partition driver"] -- "creates" --> C2["partition handles"]
  FAT["FAT driver"] -- "installs SimpleFileSystem" --> C2
  C2 --> V["NESH volume fs0:"]
Drivers, handles and protocols: from a PCI device to a volume

Inspecting

CommandShows
driversEvery driver: version, type, how many devices it manages and creates, name, file.
devicesEvery device: type, parents, drivers, children, name.
devtreeThe devices as a tree, from the root bridges down.
dhHandles and their protocols; dh 8F details of one, dh -p BlockIo only those with a protocol.
openinfo HANDLEWhich drivers and programs have opened the protocols of a handle.
fs0:\> devtree
Ctrl[33] PciRoot(0x0)
  Ctrl[8C] PciRoot(0x0)/Pci(0x0,0x0)
  Ctrl[8D] QEMU Video PCI Adapter
  Ctrl[8E] PciRoot(0x0)/Pci(0x2,0x0)
    Ctrl[9C] FAT File System
  Ctrl[91] ATA Controller
    Ctrl[9D] QEMU QEMU DVD-ROM
fs0:\> dh -p BlockIo
 8E: DiskIo BlockIo fa920010-6785-4941-b6ec-498c579f160a PciIo DevicePath
 9C: SimpleFileSystem DiskIo PartitionInfo BlockIo DevicePath

Loading and connecting

CommandEffect
load FILE.efiLoads and starts a driver, then connects all devices so it can find its hardware (-nc: do not connect).
connect [-r]Connects drivers to all devices (or to one handle; -r also to the new children).
disconnect HANDLE / reconnect HANDLEStops / restarts the drivers of a device.
unload HANDLERemoves a driver from memory (if the driver allows it).
loadpcirom FILE.romLoads the UEFI drivers contained in a PCI option ROM image.
drvdiag / drvcfgRuns driver diagnostics / driver configuration, for drivers that offer them.

A new USB stick or disk that does not appear in map usually needs connect -r followed by map -r.

Hardware and firmware information

Reading what the firmware knows about the machine: memory, PCI devices, SMBIOS and ACPI tables, the processor, the screen.

Overview

fs0:\> sysinfo
Firmware: Debian distribution of EDK II (revision 0x00010000)
UEFI:     2.7
Secure Boot: inactive
CPU:      QEMU Virtual CPU version 2.5+ / GenuineIntel
Memory:   511 MiB
Console:  100 x 31
Graphics: 1280 x 800 (mode 0 of 30), frame buffer at 0x80000000
Tables:   10 configuration tables, ACPI, SMBIOS
Started from: fs0: PciRoot(0x0)/Pci(0x2,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
CommandShows
memmapThe UEFI memory map; -s totals per memory type.
pciPCI devices; pci BUS DEV FUNC -i decodes one device's configuration header.
smbiosviewSMBIOS tables: BIOS, system, board, processor, memory modules (-t 17).
acpiviewACPI tables; -s APIC shows one, -s DSDT -d saves it to a file.
cpuidProcessor vendor, family, model and features; raw registers for a leaf.
mode / gopText size and screen resolution; see Screen resolution and text size.
sermodeSerial port settings.
dmem / mmMemory dump / read and write memory, I/O ports and PCI registers.
getmtcThe firmware's monotonic counter.
fs0:\> memmap -s
Type                      Pages        MiB
Reserved                  65672        256
LoaderCode                  100          0
LoaderData                  266          1
BootServicesCode            698          2
BootServicesData           9999         39
RuntimeCode                 256          1
RuntimeData                 581          2
Conventional             118412        462
ACPIReclaim                  18          0
ACPINVS                     514          2
Total memory: 511 MiB, available to the OS: 505 MiB
fs0:\> smbiosview -t 1
SMBIOS 2.8 (32-bit entry point at 0x1f988000), 9 structures
Type 1 (System Information)  Handle 0x0100  Length 27
    Manufacturer: QEMU
    Product: Standard PC (Q35 + ICH9, 2009)
    Version: pc-q35-10.0
    Serial:
    UUID: 00000000-0000-0000-0000-000000000000
    SKU:
    Family:
fs0:\> acpiview
RSDP at 0x1fb7e014, revision 2, OEM BOCHS
Sig   Address           Length  Rev     OEM     Table
XSDT  0x1fb7d0e8      84  rev 1   BOCHS   BXPC      checksum ok
FACP  0x1fb79000     244  rev 3   BOCHS   BXPC      checksum ok
DSDT  0x1fb7a000    8474  rev 1   BOCHS   BXPC      checksum ok
FACS  0x1fbdd000      64  FACS
APIC  0x1fb78000     120  rev 3   BOCHS   BXPC      checksum ok

Screen resolution and text size

Two different things decide what you see:

This is why the shell often uses only part of a large screen: a 100 x 31 text console covers 800 x 589 pixels, so on a 1920 x 1080 screen the text sits in a block in the middle. Three ways out, from the simplest:

fs0:\> mode -max
Text mode 2: 100 x 31 characters
fs0:\> mode -fill
Restarting the firmware console driver...
Text mode 4: 160 x 42 characters
fs0:\> gop -fit
Graphics mode 3: 800 x 600 (text 100 x 31)
  1. mode -max takes the largest text size the firmware already offers. Try this first: on many machines the firmware started its console at the screen's own resolution and a large mode is there.
  2. mode -fill is for when mode only lists small sizes, which means the console driver started at a low resolution. It restarts that driver (like reconnect -r), so the firmware builds a text mode for the whole screen, and selects it. Two effects to know: the screen resolution goes back to the firmware's own default, and the consoles are disconnected for a moment — on a serial console you may lose some output.
  3. gop -fit keeps the text size and lowers the resolution until the text fills the screen, which makes the characters larger. Useful when you want big, readable text.
A high resolution and a full screen of text

Those two together depend on the firmware: the text modes are built when the console driver starts, so the resolution has to be set before that. Many firmware setups have a “preferred resolution” or “video mode” setting that is applied at the next boot; with that set, mode -max inside NESH is enough. Setting the resolution afterwards with gop gives a large screen with the old, smaller text block.

To set a resolution for a program you are going to start (a graphical tool, a boot loader), use gop:

CommandEffect
gopList the modes: number, resolution, pixel format, and the frame buffer.
gop 1024 768Switch to that resolution.
gop 7Switch to the mode with that number.
gop -maxThe largest resolution available.
mode 100 31A text size, from those mode lists.

If the new resolution is too small for the current text size, NESH switches the console to one that fits first, so the text never ends up outside the screen.

Low-level access

mm and hexedit -m write directly to memory, I/O ports and PCI registers. A wrong value can hang the machine or damage data. dmem only reads, but reading an unmapped address can also hang some machines. These writes are disabled while Secure Boot is active.

Network

NESH uses the network stack of the firmware (the same one used for network boot) to configure interfaces and download files over IPv4 and IPv6.

Making the network available

Most firmware loads its network drivers only when network boot (PXE) is enabled in the setup. If ifconfig -l says no network interface, either enable network boot in the firmware setup, or load the drivers from files with load (they are part of the firmware project EDK2; a copy extracted from the firmware of the same machine works best):

fs0:\> load MnpDxe.efi ArpDxe.efi Ip4Dxe.efi Udp4Dxe.efi Dhcp4Dxe.efi Mtftp4Dxe.efi TcpDxe.efi DnsDxe.efi HttpUtilitiesDxe.efi HttpDxe.efi
fs0:\> load Ip6Dxe.efi Udp6Dxe.efi Dhcp6Dxe.efi Mtftp6Dxe.efi

The network drivers need a random number generator, which modern machines provide.

IPv4

fs0:\> ifconfig -s eth0 dhcp
eth0: waiting for DHCP... done
name         : eth0
media state  : unknown
policy       : dhcp
mac addr     : 52-54-00-12-34-56
ipv4 address : 10.0.2.15
subnet mask  : 255.255.255.0
gateway      : 10.0.2.2
dns server   : 10.0.2.3
route table  :
  subnet 10.0.2.0 netmask 255.255.255.0 gateway 0.0.0.0
  subnet 0.0.0.0 netmask 0.0.0.0 gateway 10.0.2.2

A fixed address: ifconfig -s eth0 static 192.168.1.10 255.255.255.0 192.168.1.1, DNS servers with ifconfig -s eth0 dns 192.168.1.1, back to DHCP with ifconfig -r. The settings are kept by the firmware.

IPv6

Every interface has a link-local address (fe80::…). ifconfig6 -s eth0 auto enables automatic configuration from the routers of the network; ifconfig6 -s eth0 man host 2001:db8::10/64 gw 2001:db8::1 sets an address by hand.

fs0:\> ifconfig6 -s eth0 auto
fs0:\> ifconfig6 -l eth0 -data
name=eth0
media=unknown
policy=automatic
mac=52-54-00-12-34-56
ip=fe80::5054:ff:fe12:3456/64 fec0::5054:ff:fe12:3456/64
gateway=fe80::2
dns=
dad=1

ping, TFTP and HTTP

fs0:\> ping -n 3 10.0.2.2
Ping 10.0.2.2 16 data bytes.
16 bytes from 10.0.2.2 : icmp_seq=1 ttl=255 time=0ms
16 bytes from 10.0.2.2 : icmp_seq=2 ttl=255 time=0ms
16 bytes from 10.0.2.2 : icmp_seq=3 ttl=255 time=0ms

3 packets transmitted, 3 received, 0% packet loss
Rtt(round trip time) min=0ms max=0ms avg=0ms
fs0:\> tftp 10.0.2.2 hello.txt fs1:\tftp.txt
Downloading hello.txt from 10.0.2.2 ...
20 bytes saved to fs1:\tftp.txt
fs0:\> http http://[fec0::2]:18080/tool.efi fs1:\tool.efi

ping, tftp and http accept both IPv4 and IPv6 addresses; in URLs an IPv6 address goes in square brackets. ping6 is also available for UEFI Shell compatibility. http supports http:// only (https needs a TLS driver in the firmware).

' download a diagnostic tool from a server and run it
RUN "ifconfig -s eth0 dhcp"
IF ERR <> 0 THEN PRINT "no network": END 1
RUN "http", "http://192.168.1.5/tools/diag.efi", "fs1:\diag.efi"
IF ERR = 0 THEN RUN "fs1:\diag.efi"

Running EFI applications

NESH starts UEFI programs — boot loaders, firmware updaters, diagnostics, other shells — and gives them the environment they expect.

Programs designed only for the old EFI 1.10 shell interface (from before 2009) are not supported.

Secure Boot

With Secure Boot active, the firmware starts only programs signed with a trusted key. NESH is one file, so one signature covers the whole shell.

Signing nesh.efi

To run NESH on a machine with Secure Boot, sign nesh.efi with a key the machine trusts. With your own keys (on Linux, with the sbsigntools package):

$ sbsign --key db.key --cert db.crt --output nesh-signed.efi build/nesh.efi
$ sbverify --cert db.crt nesh-signed.efi

The certificate must be in the firmware's db database (enrolled from the firmware setup or with your platform's key-management tools), or — on machines that boot through shim — in the MOK list (mokutil --import db.der).

What changes when Secure Boot is active

NESH shows Secure Boot is active: low-level hardware writes are disabled when it starts, and refuses writes that could be used to get around the protection:

RefusedStill allowed
mm ADDRESS VALUE and interactive changes in mm (memory, MMIO, I/O ports, PCI registers) Reading with mm, dmem, pci
Saving memory in hexedit -mViewing memory in hexedit -m

In addition, the firmware itself refuses to load unsigned drivers and programs (load, loadpcirom, starting .efi files); NESH reports this as not allowed by Secure Boot (the image is not signed by a trusted key). Authenticated variables, such as the Secure Boot keys, cannot be changed either. Ordinary UEFI variables, files, the clock and the serial port settings keep working. Scripts can check the state with SECUREBOOT.

Differences from the UEFI Shell

For people who know the UEFI Shell: what is the same, what is different, and how to translate.

UEFI ShellNESH
.nsh scripts with if, for, goto, shift, %1 Not supported: write .nsb scripts in NESH BASIC. %1ARG$(1); if existIF FILEEXISTS(…); %lasterror%ERR.
%var% in command linesNot expanded; use ENV$("var") in a script.
-sfo output-data output (see Script-friendly output).
-b (pause after each page)Accepted and ignored.
startup.nshstartup.nsb on the boot volume.
msrNot included (reading a missing model-specific register can hang the machine).
exit /bSame meaning: ends only the current script.
Separate IPv6 toolsifconfig6 and ping6 exist; ping, tftp, http also accept IPv6.

Everything else — command names, their options, volume names, environment variables and aliases (stored in the same UEFI variables, so both shells share them), dmpstore files, bcfg — works as in the UEFI Shell. NESH adds commands the UEFI Shell does not have: bootmgr, var, grep, head, tail, wc, hexdump, stat, which, history, sysinfo, cpuid, gop, devtree and more.

Troubleshooting

ProblemWhat to try
The firmware does not show the USB stickThe stick must be FAT-formatted and contain \EFI\BOOT\BOOTX64.EFI. Some firmware needs “USB boot” enabled or “fast boot” disabled in the setup.
“Security violation” or nothing happens when starting NESHSecure Boot is active and nesh.efi is not signed with a trusted key: see Signing nesh.efi, or disable Secure Boot in the setup.
A disk or stick is missing from mapconnect -r, then map -r. Partitions with a file system the firmware does not know (NTFS, ext4) appear only as blkN.
“command not found”Check the spelling with help; for a script or program, give the path or check set path and which NAME.
A long output scrolls awayIt should stop at every screenful; if it does not, check set pager (see Long output: paging), or save it to a file with > file.
The shell uses only part of the screenmode -max, then mode -fill if the text sizes offered are all small; gop -fit makes the characters larger instead (see Screen resolution and text size).
Function keys do not work (serial console)Use the Ctrl equivalents listed for each editor.
History or backups are not savedThe boot volume is read-only; NESH continues without them.
A script stops with “error: …”The message gives file and line. nesh.efi -k SCRIPT checks the syntax.
“no network interface”See Making the network available.
A command runs too longCtrl+C.

Command reference

Every command, grouped by topic. These texts are the same ones that help NAME shows inside NESH (this chapter is generated from them). Commands marked -data support script-friendly output.

Shell and session

CommandWhat it does
helpShow help
ver -dataShow shell, firmware and UEFI versions
clsClear the screen
exitLeave the shell and return to the firmware
historyShow the last N commands (-c clears the history)
echoPrint text (-n: no newline)
pauseWait for a key
sleepWait for the given time
whichShow what a command name refers to
set -dataShow or change environment variables
alias -dataShow or define command aliases
resetRestart or shut down the machine, or reboot into the firmware setup
sysinfo -dataFirmware, CPU, memory and display information

help

Show help

help [COMMAND | STATEMENT | FUNCTION | basic | functions]
  help                list all the commands with a one-line summary
  help NAME           usage and details of one command
  help PRINT          a statement or function of the language: the lines
                      about it from the references below
  help basic          quick reference of the BASIC scripting language
  help functions      list of the built-in BASIC functions
Names are not case-sensitive. 'help language' and 'help func' also work.
An unknown NAME is an error.

ver -data

Show shell, firmware and UEFI versions

ver
  ver                 shell version and platform; on UEFI also the firmware
                      vendor and revision, UEFI version and Secure Boot state
With -data: shell, version, platform, firmware, firmware_revision, uefi,
secure_boot (yes/no).

cls

Clear the screen

cls [BACKGROUND]
  cls                 clear the screen
  cls 1               set a blue background, then clear
BACKGROUND: 0 black, 1 blue, 2 green, 3 cyan, 4 red, 5 magenta, 6 brown,
7 light gray. The screen is not cleared when the output is redirected.

exit

Leave the shell and return to the firmware

exit [/b] [CODE]
  exit                leave the shell with exit code 0
  exit 3              leave the shell with exit code 3
  exit /b 2           end only the current script, with exit code 2
In a script (RUN "exit"), exit stops all running scripts at once, then
leaves the shell. exit /b is like END CODE; at the prompt it only sets the
exit code. CODE may be decimal or hex (0x10). -b is the same as /b.

history

Show the last N commands (-c clears the history)

history [-c] [N]
Keys at the prompt: Up/Down browse the history, Ctrl-R searches it,
Tab completes commands and paths, Ctrl-A/E start/end of line,
Ctrl-K/U delete to end/start, Ctrl-W delete word, Ctrl-L clear screen.

echo

Print text (-n: no newline)

echo [-n] [TEXT...]
  echo TEXT...        print the words separated by one space, then a newline
  echo -n TEXT...     the same without the final newline
  echo "a   b"        quotes keep the spaces
Only a first -n is an option; any other word is printed as it is,
including -b (echo never pages).
echo -on and echo -off (UEFI Shell script switches) do nothing.

pause

Wait for a key

pause [-q] [MESSAGE]
  pause               print "Press any key to continue..." and wait
  pause Insert disk   print your own message and wait for a key
  pause -q            wait without a message
Exit code 0 for most keys, 130 for Esc or Ctrl-C. In a script, Esc can be
tested in ERR; Ctrl-C also stops the script.

sleep

Wait for the given time

sleep MILLISECONDS
  sleep 500           wait half a second
Ctrl-C stops the wait (exit code 130). For microseconds see stall.

which

Show what a command name refers to

which NAME...
  which NAME...       say if NAME is an alias, a built-in command, a file
                      or a BASIC function
  which ls            ls: built-in command
  which ll            ll: alias for ls -l
Files are searched like commands: NAME.nsb and NAME.efi in the directories
of the path variable, or at the path given. Exit code 127 if a NAME is not
found.

set -data

Show or change environment variables

set [-v] [NAME [VALUE...]] | set -d NAME
  set                 list all the variables
  set NAME            show one variable
  set NAME VALUE      set a variable, kept in the firmware across reboots
  set -v NAME VALUE   set a temporary variable (lost when the shell exits)
  set -d NAME         delete a variable
Environment variables are shared with the EFI applications started by
the shell and are separate from BASIC variables. In scripts: ENV$(name$),
SETENV, DELENV. path lists the directories searched for commands (';'
separated, '.' = current directory). pager=off stops the automatic
paging of long output at the prompt. Read-only: cwd, lasterror,
uefishellsupport, uefishellversion, uefiversion, neshversion.
With -data (set): name, value, kind (permanent, temporary, readonly).

alias -data

Show or define command aliases

alias [-v] [NAME [COMMAND...]] | alias -d NAME
  alias               list the aliases (V: temporary)
  alias ll ls -l      define ll, kept in the firmware across reboots
  alias -v ll ls -l   temporary alias
  alias -d ll         delete an alias
Extra arguments are appended: 'll fs0:' runs 'ls -l fs0:'.

reset

Restart or shut down the machine, or reboot into the firmware setup

reset [-w [STRING] | -s [STRING] | -c [STRING] | -fwui | setup]
  (none), -c         cold reset
  -w                 warm reset
  -s                 shut down (power off)
  setup, -fwui, -f   restart into the firmware setup screen (sets the
                     OsIndications variable; the firmware must support it)
  STRING             reset reason passed to the firmware as ResetData
Only one mode may be given. If the firmware does not reset, reset fails
with an error.
Example: reset -s "maintenance"

sysinfo -data

Firmware, CPU, memory and display information

sysinfo
Shows firmware vendor and revision, UEFI version, Secure Boot state, CPU,
memory size, console size, graphics mode, configuration tables and the
volume NESH was started from. Arguments are ignored.
With -data: one record with firmware, firmware_revision, uefi,
secure_boot, cpu, cpu_vendor, memory, console_columns, console_rows,
graphics_width, graphics_height, framebuffer, config_tables, acpi,
smbios, boot_volume, boot_devpath.

Files and folders

CommandWhat it does
ls -dataList files and directories
dir -dataList files with details (same as ls -l)
cdChange the current directory
pwdPrint the current directory
catPrint files (UTF-8 or UCS-2 text, detected automatically)
typePrint files (same as cat)
morePrint files one screen at a time
cpCopy files (-r directories too)
mvMove or rename files and directories
rmDelete files (-r or -q: directories too, -f ignore missing)
delDelete files (same as rm)
mkdirCreate directories (-p also the parents)
mdCreate directories (same as mkdir)
rmdirRemove empty directories
touchSet the modification time to now (creates missing files; -r recursive)
stat -dataShow file details
attribShow or change file attributes (archive, system, hidden, read-only)
compCompare two files and show where they differ
cmpCompare two files and stop at the first difference
setsizeSet the size of files (truncate or extend with zeros)

ls -data

List files and directories

ls [-l] [-r] [-a[ashrd]] [PATH | PATTERN...]
  ls                    names in the current directory
  ls -l fs1:\efi        details: date, time, attributes, size, name
  ls -r                 also every subdirectory
  ls *.efi              only matching names
  -a                    also hidden and system files
  -aXY                  only entries with all the attributes X, Y...:
                        d directory, r read-only, h hidden, s system,
                        a archive (e.g. ls -ad lists directories)
Directory names end with \ (and are blue; programs .efi/.nsb are green).
Hidden and system files are left out unless -a is given or a pattern
names them. -b pages the output (see help more). dir is the same as
ls -l.
With -data: name, path, type, size, modified, readonly, hidden, system,
archive (one record per entry).

dir -data

List files with details (same as ls -l)

dir [-r] [-a[ashrd]] [PATH | PATTERN...]
  dir                   current directory
  dir -r fs0:\efi       also all subdirectories
  dir *.efi             only matching names (hidden files included)
Each line: date, time, attributes (d dir, r read-only, h hidden, s system,
a archive), size and name; a total line ends each directory. Hidden and
system files are shown only with -a. -l is accepted and has no effect;
-b pages the output. Other options as for ls.
With -data: name, path, type, size, modified, readonly, hidden, system,
archive.

cd

Change the current directory

cd [DIR | fsN:]
  cd                  print the current directory
  cd DIR              go to DIR (relative, or absolute from the root)
  cd fs1:             go to the root of volume fs1:
  cd ..               go to the parent directory
Both \ and / work as separators. DIR must be an existing directory.

pwd

Print the current directory

pwd
  Prints the full path, e.g. fs0:\efi\boot (same as cd with no arguments).

cat

Print files (UTF-8 or UCS-2 text, detected automatically)

cat [-a|-u] FILE...
  cat readme.txt            print a file
  cat fs0:\logs\*.txt       print all matching files, one after another
Files may be UTF-8 (with or without BOM) or UCS-2 little-endian with BOM;
carriage returns are removed. -a and -u (UEFI Shell: force ASCII or
UCS-2) are accepted before the files and ignored. Ctrl-C stops.

type

Print files (same as cat)

type [-a|-u] FILE...
  Same as cat (UEFI Shell name).

more

Print files one screen at a time

more FILE...
  more log.txt        stop at every screenful: Enter one line,
                      Space one page, q stops
Same as cat, with paging always on (cat pages too when you type it at
the prompt). Any command pages with -b; 'set pager off' turns the
automatic paging of the prompt off.

cp

Copy files (-r directories too)

cp [-r] [-q] SRC... DST
  -r        copy directories and all their contents
  -q        accepted for UEFI Shell compatibility (cp never asks)
  cp a.txt b.txt               copy a file
  cp *.efi fs1:\tools          copy several files into a directory
  cp -r fs0:\efi fs1:\backup   copy a whole folder
If DST is an existing directory, each SRC is copied into it; with several
sources DST must be a directory. Existing files are overwritten without
asking; with -r, existing directories are merged. A directory cannot be
copied into itself. After an error or Ctrl-C the partial file is deleted.

mv

Move or rename files and directories

mv SRC... DST
  mv old.txt new.txt           rename a file
  mv *.log fs0:\logs           move files into a directory
  mv fs0:\tools fs1:\          move a directory to another volume
If DST is an existing directory, each SRC is moved into it; with several
sources DST must be a directory. An existing file at the target is
replaced without asking, but only once the move has succeeded (if not,
it is put back); an existing directory is an error. Between volumes, mv
copies and deletes the source only after a complete copy (a partial copy
is removed). There are no options.

rm

Delete files (-r or -q: directories too, -f ignore missing)

rm [-r] [-f] [-q] PATH...
  -r, -q    also delete directories with all their contents
  -f        no error for paths or patterns that match nothing
  rm *.tmp                 delete matching files
  rm -r fs1:\old           delete a directory tree
Nothing is asked before deleting. Read-only files cannot be deleted:
clear the flag with attrib -r first. The root of a volume is never
removed. -q is the UEFI Shell form (there rm deletes directories too).

del

Delete files (same as rm)

del [-r] [-f] [-q] PATH...
  Same as rm (UEFI Shell name).

mkdir

Create directories (-p also the parents)

mkdir [-p] DIR...
  -p        also create missing parents; no error if a directory exists
  mkdir -p fs0:\a\b\c       create a whole path in one step
Without -p the parent must exist and DIR must not exist yet. A file with
the same name as DIR or a parent is always an error.

md

Create directories (same as mkdir)

md [-p] DIR...
  Same as mkdir (UEFI Shell name).

rmdir

Remove empty directories

rmdir DIR...
  rmdir fs0:\old           remove an empty directory
A directory that is not empty is an error (use rm -r to delete a whole
tree). Wildcards are not expanded.

touch

Set the modification time to now (creates missing files; -r recursive)

touch [-r] FILE...
  -r        also every file and directory inside the given directories
  touch log.txt            create an empty file, or update its time
  touch -r fs0:\data       update a whole tree
Wildcards are allowed; a pattern that matches nothing is skipped silently.

stat -data

Show file details

stat PATH...
  stat boot.efi            path, type, size, modified time, attributes
Size is not shown for directories. Wildcards are not expanded.
With -data: name, path, type, size, modified, readonly, hidden, system,
archive.

attrib

Show or change file attributes (archive, system, hidden, read-only)

attrib [+a|-a] [+s|-s] [+h|-h] [+r|-r] [FILE | DIR | PATTERN...]
  attrib                   attributes of everything in the current dir
  attrib DIR               attributes of the entries in DIR
  attrib +r boot.efi       make a file read-only
  attrib -h -s fs0:\*      clear hidden and system on all root entries
Letters: a archive, s system, h hidden, r read-only (upper case works).
Each line shows D (directory), A, S, H, R and the path. When changing, a
directory itself is changed, not its contents. Wildcards are allowed.

comp

Compare two files and show where they differ

comp [-n COUNT|all] [-s BYTES] FILE1 FILE2
  -n COUNT  stop after COUNT differences (default 10; all: no limit)
  -s BYTES  bytes shown for each difference (default 16)
  comp -n all old.rom new.rom   list every difference
A run of consecutive different bytes is one difference: its hex offset is
printed, then the bytes of both files in hex and as text. Different sizes
count as one more difference. ERR is nonzero if the files differ. Both
files are read into memory.

cmp

Compare two files and stop at the first difference

cmp [-s BYTES] FILE1 FILE2
  Same as comp -n 1. -s BYTES sets the bytes shown (default 16).

setsize

Set the size of files (truncate or extend with zeros)

setsize SIZE FILE...
  setsize 1048576 disk.img    make a file of 1 MiB
  setsize 0 log.txt           empty a file
SIZE is in bytes (decimal, or hex with 0x). Missing files are created
first, as in the UEFI Shell.

Text and data

CommandWhat it does
grepPrint the lines of files that contain a text
headFirst N lines of a file (default 10)
tailLast N lines of a file (default 10)
wcCount lines, words and bytes
hexdumpHexadecimal dump of a file
date -dataShow or set the date
time -dataShow or set the time
timezoneShow or set the time zone of the clock (-l list, -f details)
parseExtract a column from standard-format (comma separated) output
stallWait for the given number of microseconds
eficompressCompress a file in the UEFI compression format
efidecompressDecompress a file in the UEFI compression format

grep

Print the lines of files that contain a text

grep [-i] [-v] [-n] [-c] TEXT FILE...
  -i                  ignore upper/lower case
  -v                  print the lines that do NOT contain TEXT
  -n                  put the line number before each line
  -c                  print only the number of matching lines
  grep -i error log.txt      lines containing "error", any case
  grep -c TODO *.nsb         matching lines in each script
TEXT is plain text, not a pattern. FILE may contain wildcards; with several
files each line starts with the file name. UTF-8 and UCS-2 files are read;
directories are skipped. Use -- before a TEXT that starts with '-'.
Exit code: 0 if a line matched, 1 if none, other values on errors.

head

First N lines of a file (default 10)

head [-n N] FILE
  -n N                print the first N lines (default 10)
  head -n 20 log.txt  the first 20 lines
The file is read as text (UTF-8 or UCS-2; carriage returns are removed).
One FILE only; -n goes before it.

tail

Last N lines of a file (default 10)

tail [-n N] FILE
  -n N                print the last N lines (default 10)
  tail -n 5 log.txt   the last 5 lines
The file is read as text (UTF-8 or UCS-2; carriage returns are removed).
One FILE only; -n goes before it.

wc

Count lines, words and bytes

wc FILE...
  wc a.txt b.txt      one line per file: lines, words, bytes, name
Lines are counted as newline characters; words are separated by spaces,
tabs or newlines; bytes is the file size. Wildcards are not expanded.

hexdump

Hexadecimal dump of a file

hexdump [-s OFFSET] [-n LENGTH] FILE
  -s OFFSET           start at byte OFFSET (default 0)
  -n LENGTH           dump at most LENGTH bytes (default: to the end)
  hexdump -n 0x200 disk.img      the first 512 bytes
Each line shows the offset, 16 bytes in hex and the printable characters.
Numbers may be decimal or hex (0x). Options go before FILE. Ctrl-C stops.

date -data

Show or set the date

date [YYYY-MM-DD | MM/DD/YYYY | MM/DD/YY]
  date                show the date as YYYY-MM-DD
  date 2026-09-19     set the date
  date 09/19/2026     set the date, UEFI Shell order (MM/DD/YYYY or MM/DD/YY)
A two-digit year means 20YY. The time of day is not changed.
With -data: date, year, month, day.

time -data

Show or set the time

time [HH:MM[:SS]]
  time                show the time as HH:MM:SS (24 hours)
  time 14:30          set the time (seconds become 0)
  time 14:30:15       set the time with seconds
The date is not changed.
With -data: time, hour, minute, second.

timezone

Show or set the time zone of the clock (-l list, -f details)

timezone [-s [+|-]hh:mm] [-l] [-f]
  (none)          show the time zone of the real-time clock
  -s [+|-]hh:mm   set the offset from UTC (up to 14:00; :mm optional)
  -l              list common offsets with example places
  -f              also show the raw TimeZone and Daylight fields
  -b              page the output (UEFI Shell option, see help more)
-s changes only the time zone, not the date and time. -l ignores the
other options.
Example: timezone -s +01:00

parse

Extract a column from standard-format (comma separated) output

parse FILE TABLE COLUMN [-i INSTANCE] [-s INSTANCE]
  Lines look like: TableName,"value1","value2",...  COLUMN 1 is value1.
  -i N: only the Nth line of TABLE.
  -s N: only in the Nth ShellCommand section.

stall

Wait for the given number of microseconds

stall MICROSECONDS
  stall 1000          wait 1000 microseconds (1 ms)
Uses the firmware Stall service: the wait cannot be stopped with Ctrl-C.
For longer waits use sleep (milliseconds, can be stopped).

eficompress

Compress a file in the UEFI compression format

eficompress INFILE OUTFILE
  eficompress big.bin big.cmp    compress big.bin into big.cmp
Writes the UEFI (EFI 1.1) compressed format, which firmware and efidecompress
can read. OUTFILE is overwritten. Prints the sizes before and after.

efidecompress

Decompress a file in the UEFI compression format

efidecompress [-n] INFILE OUTFILE
  -n                  use the NESH decoder even if the firmware has one
  efidecompress big.cmp big.bin  decompress big.cmp into big.bin
By default the firmware decompressor is used when available. OUTFILE is
overwritten. Prints the sizes before and after.

Volumes and disks

CommandWhat it does
map -dataList volumes and block devices; give volumes extra names
vol -dataShow a volume, set (-n) or delete (-d) its label
dblkShow raw blocks of a disk, decoding MBR, GPT and FAT boot sectors
getmtcShow the next monotonic count of the firmware

map -data

List volumes and block devices; give volumes extra names

map [-r] [-v] [-t fs|blk] | map NAME TARGET | map -d NAME
  map                 volumes (fsN:) and block devices (blkN:)
  map -r              rescan the devices (also -u)
  map usb fs1:        fs1: can also be called usb:
                      (TARGET: fsN:, blkN: or a handle)
  map -d usb          remove an extra name

vol -data

Show a volume, set (-n) or delete (-d) its label

vol [fsN:] [-n LABEL | -d]
  vol                      current volume: label, total and free space
  vol fs1:                 another volume (also fs1, a map name or a path)
  vol fs1: -n BOOTDISK     set the label (at most 11 characters)
  vol -d                   delete the label of the current volume
A label cannot contain % ^ * + = [ ] | : ; " < > ? / or a dot.
With -data: volume, label, size, free, readonly.

dblk

Show raw blocks of a disk, decoding MBR, GPT and FAT boot sectors

dblk DEVICE [LBA [COUNT]]
  DEVICE  blkN (see map), fsN: or a handle number (hex)
  LBA     first block (hex, default 0)
  COUNT   number of blocks (hex, 1 to 10, default 1)
  -b      page the output (UEFI Shell option, see help more)
Each block is shown in hex and text. An MBR (LBA 0), a GPT header (LBA 1)
and FAT boot sectors are also decoded. COUNT stops at the end of the
device. The disk is only read.
Example: dblk blk0 1

getmtc

Show the next monotonic count of the firmware

getmtc
Prints the 64-bit monotonic counter of the firmware in hex. Each call
increases it, so two runs never show the same value.

Boot options and UEFI variables

CommandWhat it does
bootmgr -dataManage the UEFI boot entries
bcfgManage boot/driver options (UEFI Shell syntax)
var -dataRead and write UEFI variables
dmpstoreDump, delete, save or load UEFI variables (UEFI Shell syntax)
setvarShow, set or delete a UEFI variable (UEFI Shell syntax)

bootmgr -data

Manage the UEFI boot entries

bootmgr [SUBCOMMAND] [OPTIONS]
  list [-v]                  entries in boot order (default)
  add FILE "DESC" [ARGS] [-t] new entry for FILE (-t: first in the order)
  del ID                     delete an entry
  enable ID | disable ID     activate / deactivate an entry
  edit ID [-D DESC] [-a ARGS] [-F FILE]   change an entry
  order [ID,ID,...]          show or set the boot order
  top ID                     move an entry to the top of the order
  next ID | next -d          entry for the next boot only / clear it
  timeout [SECONDS]          firmware boot menu timeout
  backup FILE | restore FILE save / restore all the boot variables
  check                      find entries pointing to missing files
Common options: -n dry run (show, change nothing), -y do not ask,
-B no automatic backup, -driver work on Driver#### entries.
IDs are the hexadecimal numbers of Boot#### (e.g. 0003).
Before every change a backup is saved in \nesh\backup on the boot volume.
To start an EFI file now, type its name (or RUN it in a script).
With -data (list): one record per entry, plus one with the boot settings.

bcfg

Manage boot/driver options (UEFI Shell syntax)

bcfg boot|driver [dump [-v] | add|addp|addh # FILE|HANDLE "DESC" | rm # | mv # # | mod # "DESC" | modf|modp|modh # FILE|HANDLE | -opt # [FILE|"DATA"]]
  # is the position in BootOrder/DriverOrder (hex, as shown by dump).
  dump [-v]             list the options in order
  add # FILE "DESC"     new option for FILE at position # (full device path)
  addp # FILE "DESC"    same with a short-form path (partition + file)
  addh # HANDLE "DESC"  new option for the device path of a handle
  rm #                  delete the option at position #
  mv # #                move an option to another position
  mod # "DESC"          change the description
  modf|modp|modh # ...  change the file (full / short path) or use a handle
  -opt # [FILE|"TEXT"]  optional data: a file's content or a text (UCS-2);
                        without it the optional data is cleared
No confirmation is asked; a backup is saved in \nesh\backup when possible.
See also: bootmgr (NESH syntax, with dry run and confirmation).

var -data

Read and write UEFI variables

var list|get|set|del|save|load ...
  var list [PATTERN] [-g GUID]     list variables (e.g. var list Boot*)
  var get NAME [-g GUID] [-x|-s|-n] [-v]
                                   show a value (-x hex dump, -s string,
                                   -n number)
  var set NAME VALUE [-g GUID] [-t TYPE] [-a ATTR]
        TYPE: str (UCS-2, default), ascii, u8, u16, u32, u64, hex, file
        ATTR: nv,bs,rt (default for new variables; existing ones keep
        theirs)
  var del NAME [-g GUID]           delete a variable
  var save FILE [PATTERN] [-g GUID] save variables to a text file
  var load FILE                    restore variables saved with var save
GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx or a name: global (default),
security, shim, shell, systemd.
With -data (var list): name, guid, guid_name, attributes, size.

dmpstore

Dump, delete, save or load UEFI variables (UEFI Shell syntax)

dmpstore [-b] [-d] [-all | [NAME] [-guid GUID]] [-s FILE | -l FILE]
  dmpstore                  all variables with the global GUID
  dmpstore -all             variables of every GUID
  dmpstore Boot*            variables matching a pattern
  dmpstore NAME -d          delete (asks when a pattern matches several)
  dmpstore -all -s FILE     save to a file (binary format of the UEFI Shell)
  dmpstore -l FILE          load variables from such a file
See also: var (NESH syntax).

setvar

Show, set or delete a UEFI variable (UEFI Shell syntax)

setvar NAME [-guid GUID] [-bs] [-rt] [-nv] [=DATA...]
  setvar NAME                 show the value
  setvar NAME -nv -rt =0100   set hexadecimal bytes
  setvar NAME ="text"         ASCII text (no terminator)
  setvar NAME =L"text"        UCS-2 text (with terminator)
  setvar NAME =--PciRoot(0)   binary device path from its text form
  setvar NAME =               delete the variable
Without -bs/-rt/-nv an existing variable keeps its attributes; a new one
is temporary (boot services only).

Drivers and devices

CommandWhat it does
drivers -dataList the UEFI drivers
devices -dataList the devices (controllers) and their drivers
devtreeShow the device tree (-d: device paths)
dh -dataShow handles and their protocols
openinfoShow who opened the protocols of a handle
connectConnect drivers to devices (no handle: all; -r recursive; -c consoles)
disconnectDisconnect drivers from a device (-r: all devices, consoles reconnected)
reconnectDisconnect and connect again (-r: all devices)
loadLoad UEFI drivers (-nc: do not connect them to devices)
unloadUnload an image (-n: no question, -v: show details)
drvdiagDriver diagnostics (-s standard, -e extended, -m manufacturing; none: list)
drvcfgDriver configuration (-f defaults, -v validate, -s set options; none: list)

drivers -data

List the UEFI drivers

drivers [-l LANG]
  drivers           one line per driver
  drivers -l fr     driver names in another language, if the driver has it
Columns: DRV driver handle, VERSION driver version (hex), TYPE, CFG and DIAG
(X: supports drvcfg / drvdiag), #D devices it manages, #C child devices it
created, driver name (default language en) and the file it was loaded from.
TYPE B is a bus driver: it created child devices (e.g. one per USB device);
D is a device driver (a bus driver with no children yet also shows D).
Handle numbers are hexadecimal; -b pages the output.
With -data: handle, version, type, config, diag, controllers, children,
name, image.

devices -data

List the devices (controllers) and their drivers

devices [-l LANG]
  devices           one line per device (controller)
Columns: CTRL device handle, TYPE (R root: no parent, B bus: has children,
D device), CFG and DIAG (X: one of its drivers supports drvcfg / drvdiag),
#P parents, #D drivers managing it, #C children, device name.
A device is a handle with a device path that is not an image or a driver.
Names come from the drivers (in English) or else from the device path;
-l is accepted and ignored; -b pages the output.
With -data: handle, type, config, diag, parents, drivers, children, name.

devtree

Show the device tree (-d: device paths)

devtree [-d] [-l LANG] [HANDLE]
  devtree           tree of all devices, from the root devices down
  devtree HANDLE    only the device HANDLE and what is below it
  devtree -d        show device paths instead of names
Each line is "Ctrl[HANDLE] name"; children are indented under their parent.
A device without a device path shows up to 4 of its protocols in <...>.
-l is accepted and ignored; -b pages the output.
Example:  devtree -d 3F    device paths of device 3F and its children

dh -data

Show handles and their protocols

dh [-d|-v] [-p PROTOCOL] [HANDLE]
  dh              one line per handle: number and protocol names
  dh HANDLE       details of one handle (device path, image, driver, disk...)
  dh -p BlockIo   only handles with a protocol (name or GUID)
  dh -d           details of all handles
Handle numbers are hexadecimal and stay the same during the session.
With -data: handle, name, protocols (and devicepath with -d or HANDLE).

openinfo

Show who opened the protocols of a handle

openinfo HANDLE
  openinfo HANDLE    each protocol of HANDLE and who has it open
For each protocol, one line per user: Drv[agent handle] Ctrl[controller
handle] Cnt(open count), how it is open, and the agent's image name.
How: Driver (a driver manages the device), Exclusive, Driver+Exclusive,
Child (used by a child device), HandProt / GetProt / TestProt (simple use).
Handles are hexadecimal (see dh); -b pages the output.

connect

Connect drivers to devices (no handle: all; -r recursive; -c consoles)

connect [-r] [-c] [[DRIVER] CONTROLLER]
  connect                    connect all drivers to all devices (recursive)
  connect CONTROLLER         connect the best drivers to one device
  connect DRIVER CONTROLLER  connect only DRIVER to that device
  -r                         also connect the new child devices, recursively
  -c                         connect the consoles (ConIn, ConOut, ErrOut)
Connecting asks drivers to start managing a device. Bus drivers then create
child devices (e.g. the partitions of a disk, which then get a file system).
With -c and handles, the consoles are connected first, then the device.
Exit code is nonzero if the firmware reports an error for the device.
Example:  connect -r 3F    connect device 3F and everything below it

disconnect

Disconnect drivers from a device (-r: all devices, consoles reconnected)

disconnect CONTROLLER [DRIVER [CHILD]] | disconnect -r
  disconnect CONTROLLER               stop all drivers managing a device
  disconnect CONTROLLER DRIVER        stop only DRIVER on that device
  disconnect CONTROLLER DRIVER CHILD  make DRIVER release one child device
  disconnect -r                       disconnect all devices, then connect
                                      the consoles again (screen, keyboard)
When a bus driver stops, the child devices it created disappear too.
DRIVER is a driver handle (see drivers); -r is ignored when handles are given.
Exit code is nonzero if the firmware reports an error.

reconnect

Disconnect and connect again (-r: all devices)

reconnect CONTROLLER [DRIVER [CHILD]] | reconnect -r
  reconnect CONTROLLER               disconnect all drivers, connect again
  reconnect CONTROLLER DRIVER        the same, only for DRIVER
  reconnect CONTROLLER DRIVER CHILD  release one child, then connect DRIVER
  reconnect -r                       disconnect and connect all devices
The connect step is recursive (child devices are connected too) and runs
only if the disconnect succeeded; -r is ignored when handles are given.
Exit code is nonzero if a step fails.

load

Load UEFI drivers (-nc: do not connect them to devices)

load [-nc] FILE...
  load FILE...      load and start drivers, then connect all devices
  load -nc FILE...  load and start drivers, but do not connect them
FILE may contain wildcards. Applications are refused (run them by typing
their name), and so are drivers not allowed by Secure Boot.
The connect step runs if at least one driver was loaded and started.
Exit code is nonzero if any file fails.
Example:  load fs0:\drivers\MyDxe.efi   load a driver and connect it

unload

Unload an image (-n: no question, -v: show details)

unload [-n] [-v] HANDLE
  unload HANDLE     unload an image, after asking for confirmation
  unload -n HANDLE  do not ask
  unload -v HANDLE  show the handle details (like dh HANDLE) first
HANDLE must be a loaded image (see dh -p LoadedImage); for most drivers it
is the DRV number shown by drivers. -verbose is the same as -v.
Many drivers cannot be unloaded: the result is then "not supported".
Exit code is nonzero on error or if you do not answer y.

drvdiag

Driver diagnostics (-s standard, -e extended, -m manufacturing; none: list)

drvdiag [-c] [-l LANG] [-s|-e|-m] [DRIVER [CONTROLLER [CHILD]]]
  drvdiag           list driver/device pairs that offer diagnostics
  -s                run the standard diagnostics
  -e                run the extended diagnostics
  -m                run the manufacturing diagnostics
  -c                also include the child devices of each device
  -l LANG           language of the messages (default en)
Handles narrow the selection: DRIVER, then CONTROLLER, then CHILD.
Each line shows Drv[..] Ctrl[..] (Child[..]) and the driver name, or the
result ("passed" or the error) and the driver's message.
Only drivers with the Driver Diagnostics 2 protocol are used.
Exit code is nonzero if a test fails.
Example:  drvdiag -s 7A    standard tests of driver 7A on all its devices

drvcfg

Driver configuration (-f defaults, -v validate, -s set options; none: list)

drvcfg [-c] [-l LANG] [-f TYPE | -v | -s] [DRIVER [CONTROLLER [CHILD]]]
  drvcfg            list driver/device pairs with a configuration protocol
  -s                set options: the driver asks for its settings
  -v                check that the current settings are valid
  -f TYPE           restore defaults: 0 safe, 1 manufacturing (other
                    numbers are passed to the driver)
  -c                also include the child devices of each device
  -l LANG           language used by -s (default en)
Handles narrow the selection: DRIVER, then CONTROLLER, then CHILD.
After -s or -f the driver may ask to stop or restart the controller or the
platform: the line says so ("action required").
Only the Driver Configuration 2 protocol is used. If none is found, the
handles with HII setup forms are listed (use the firmware setup menu).
Exit code is nonzero if an operation fails.

Hardware and firmware

CommandWhat it does
memmap -dataShow the UEFI memory map (-s summary only)
dmemShow memory (hex; no address: the system table and configuration tables)
mmRead or write memory, I/O ports or PCI configuration space
pci -dataList PCI devices, or show the configuration space of one device
smbiosview -dataShow the SMBIOS tables (BIOS, system, board, CPU, memory...)
acpiview -dataList the ACPI tables, or show or save one
cpuidShow processor information (or raw CPUID registers)
modeList the text modes or select one
gop -dataShow or change the screen resolution (graphics modes)
sermodeShow or set serial port settings (parity n|e|o|m|s, stop bits 0|1|1.5|2)
loadpciromLoad the UEFI drivers contained in a PCI option ROM file

memmap -data

Show the UEFI memory map (-s summary only)

memmap [-s] [-b]
  (none)  every memory descriptor, then totals per type
  -s      totals per type only
  -b      page the output (UEFI Shell option, see help more)
Start and End are physical addresses; a page is 4 KiB. Total memory
excludes reserved and MMIO ranges; "available to the OS" counts
conventional, loader and boot services memory.
With -data: type, start, end, pages, attributes (one record per
descriptor); with -s: type, pages, bytes (one record per type).

dmem

Show memory (hex; no address: the system table and configuration tables)

dmem [ADDRESS [SIZE]] [-MMIO]
  ADDRESS  start address (hex); without it, dmem dumps the EFI system
           table and lists the configuration tables (GUID and address)
  SIZE     bytes to show (hex, default 0x200, max 0x100000)
  -MMIO    read through the PCI root bridge of segment 0
  -b       page the output (UEFI Shell option, see help more)
Read-only. Reading an unmapped address can hang the machine.
Example: dmem 0xFED00000 40 -MMIO

mm

Read or write memory, I/O ports or PCI configuration space

mm ADDRESS [VALUE] [-w 1|2|4|8] [-MEM|-MMIO|-IO|-PCI|-PCIE] [-n]
  mm ADDRESS            show the value and let you type new ones
                        (Enter: next address, q: quit)
  mm ADDRESS VALUE      write VALUE
  -n                    only show the value
  -w 1|2|4|8            access width in bytes (default 1)
  -MEM                  memory (default)   -MMIO  memory-mapped I/O
  -IO                   I/O port
  -PCI                  address 0x000000ssbbddffrr (segment, bus, device,
                        function, register)
  -PCIE                 address 0x0000ssbbddfffrrr (register up to 0xFFF)
Numbers are hexadecimal. Writes are refused while Secure Boot is active.

pci -data

List PCI devices, or show the configuration space of one device

pci [BUS DEV [FUNC]] [-s SEG] [-i] [-ec]
  pci                 list every device: segment, bus, device, function,
                      class, vendor and device ID
  pci BUS DEV [FUNC]  hex dump of the configuration space (FUNC 0 by default)
  -i                  also decode the header: IDs, class, command/status,
                      BARs, bus numbers, interrupt, capabilities
  -ec                 dump the 4 KiB PCI Express extended space
  -s SEG              PCI segment (default: all segments when listing, 0 else)
  -b                  page the output (UEFI Shell option, see help more)
Numbers are hexadecimal:  pci 0 1f 3 -i   decodes device 00:1F.3.
With -data: one record per function (segment, bus, device, function, vendor,
deviceid, class, classname); for one device, the decoded header.

smbiosview -data

Show the SMBIOS tables (BIOS, system, board, CPU, memory...)

smbiosview [-t TYPE] [-h HANDLE] [-s]
  smbiosview          every structure, with the fields NESH decodes
  -t TYPE             only one type (decimal): 0 BIOS, 1 system, 2 board,
                      3 enclosure, 4 processor, 16 memory array, 17 memory
                      device, 19 mapped address
  -h HANDLE           only the structure with this handle (hexadecimal)
  -s                  statistics: how many structures of each type
  -a                  accepted for UEFI Shell compatibility, ignored
  -b                  page the output (UEFI Shell option, see help more)
  smbiosview -t 17    memory modules: slot, size, speed, part number
With -data: one record per structure (type, typename, handle, length and the
decoded fields, e.g. vendor, version, serial, size).

acpiview -data

List the ACPI tables, or show or save one

acpiview [-l] | acpiview -s SIG [-d]
  acpiview            list the tables: signature, address, length, revision,
                      OEM, checksum (-l does the same)
  -s SIG              show one table: summary of APIC, MCFG, HPET and FACP,
                      then a hex dump (SIG is case-insensitive, e.g. apic)
  -s SIG -d           save the table to SIG.bin in the current directory
                      (SIG2.bin... when there are several)
  -r, -q, -h          accepted for UEFI Shell compatibility, ignored
With -data: one record per table (signature, address, length, revision,
oemid, oemtable, checksum); -d cannot be combined with -data.

cpuid

Show processor information (or raw CPUID registers)

cpuid [LEAF [SUBLEAF]]
  (none)          vendor, family, model, stepping and main features
  LEAF [SUBLEAF]  raw EAX, EBX, ECX, EDX of one leaf (SUBLEAF default 0)
LEAF and SUBLEAF are hexadecimal. Family, model and stepping print in hex.
Example: cpuid 80000001

mode

List the text modes or select one

mode [COLUMNS ROWS | -max | -fill]
  (none)        list the text modes of the console (current one marked)
  COLUMNS ROWS  switch to the mode with exactly this size
  -max          switch to the mode with the most characters
  -fill         make the text use the whole screen: restarts the
                firmware console driver (as reconnect -r does), which
                then offers a mode for the whole screen, and selects it
The firmware decides which text modes exist, from the screen resolution
it had when it started its console driver, so a large screen often shows
a small block of text. -fill fixes that, but the screen resolution goes
back to the firmware default and the consoles are disconnected for a
moment (on a serial console you may lose some output). To keep a chosen
resolution instead, gop -fit lowers it until the text fills the screen.
Example: mode -fill

gop -data

Show or change the screen resolution (graphics modes)

gop [MODE | WIDTH HEIGHT | -max | -fit]
  (none)         list the modes (number, resolution, pixel format), then
                 the frame buffer address and size
  WIDTH HEIGHT   switch to the mode with this resolution
  MODE           switch to this mode number, as listed
  -max           switch to the largest resolution available
  -fit           smallest resolution that still holds the text console,
                 so the text fills the screen
  gop 1024 768   a common resolution
The text console keeps its own size (see mode): the firmware draws
characters in cells of 8 x 19 pixels, so a large screen may show the
text in a corner. A text mode too large for the new resolution is
changed first. With -data: mode, width, height, format, current (one
record per mode), then framebuffer, framebuffer_size.

sermode

Show or set serial port settings (parity n|e|o|m|s, stop bits 0|1|1.5|2)

sermode [HANDLE [BAUD PARITY DATABITS STOPBITS]]
  (none)                            list all serial ports and settings
  HANDLE                            show one port (handle number, hex)
  HANDLE BAUD PARITY DATABITS STOP  change the settings of a port
  PARITY    d (default), n, e, o, m or s (only the first letter counts)
  DATABITS  5 to 8; STOP 0 (default), 1, 1.5 (or 15) or 2
The receive FIFO depth and timeout are kept. Handles are shown by dh.
Example: sermode 5C 115200 n 8 1

loadpcirom

Load the UEFI drivers contained in a PCI option ROM file

loadpcirom [-nc] ROMFILE...
  ROMFILE  PCI option ROM image file; several files may be given
  -nc      load and start the drivers but do not connect them
Every x64 UEFI image in the ROM is loaded and started (compressed images
are expanded first); legacy and other-architecture images are skipped.
Then all controllers are connected, unless -nc is given.
With Secure Boot active, the firmware checks the driver signatures.
Example: loadpcirom -nc nic.rom

Network

CommandWhat it does
ifconfig -dataShow or set the IPv4 configuration of the network interfaces
ifconfig6 -dataShow or set the IPv6 configuration of the network interfaces
pingSend echo requests to an IPv4 or IPv6 address
ping6Send echo requests to an IPv6 address (ping accepts IPv6 too)
tftpDownload a file with TFTP
httpDownload a file with HTTP

ifconfig -data

Show or set the IPv4 configuration of the network interfaces

ifconfig [-l [NAME]] | -r [NAME] | -s NAME dhcp | -s NAME static IP MASK GW | -s NAME dns IP...
  ifconfig -l [NAME]          list the interfaces (eth0...) and their settings
  ifconfig -s eth0 dhcp       get an address with DHCP (waits up to 15 s)
  ifconfig -s eth0 static 192.168.1.10 255.255.255.0 192.168.1.1
                              fixed address, mask and gateway
  ifconfig -s eth0 dns 192.168.1.1   DNS servers
  ifconfig -r [NAME]          reset to DHCP
The firmware network drivers must be loaded (network boot enabled in the
setup, or: load MnpDxe.efi ArpDxe.efi Ip4Dxe.efi Udp4Dxe.efi Dhcp4Dxe.efi
Mtftp4Dxe.efi TcpDxe.efi ...). See also ifconfig6 for IPv6.
With -data (ifconfig -l -data): name, media, policy, mac, ip, mask,
gateway, dns.

ifconfig6 -data

Show or set the IPv6 configuration of the network interfaces

ifconfig6 [-l [NAME]] | -r [NAME] | -s NAME auto | -s NAME man [host ADDR[/LEN]...] [gw ADDR...] [dns ADDR...] | -s NAME dad COUNT
  ifconfig6 -l [NAME]         list the interfaces and their IPv6 settings
  ifconfig6 -s eth0 auto      automatic configuration (router advertisements)
  ifconfig6 -s eth0 man host 2001:db8::10/64 gw 2001:db8::1 dns 2001:db8::53
                              manual configuration (prefix length 64 if
                              omitted)
  ifconfig6 -s eth0 dad 1     duplicate address detection messages per address
  ifconfig6 -r [NAME]         reset to automatic configuration
A link-local address (fe80::...) is always present. ping, tftp and http
accept IPv6 addresses; in URLs they go in brackets: http://[2001:db8::1]/f.
The firmware IPv6 drivers must be loaded (IPv6 network boot enabled in the
setup, or: load MnpDxe.efi Ip6Dxe.efi Udp6Dxe.efi Dhcp6Dxe.efi
Mtftp6Dxe.efi TcpDxe.efi).
With -data (ifconfig6 -l -data): name, media, policy, mac, ip (ADDR/LEN
list), gateway, dns, dad.

ping

Send echo requests to an IPv4 or IPv6 address

ping [-n COUNT] [-l SIZE] [-i IF] ADDRESS
  ping 192.168.1.1           IPv4
  ping -n 3 2001:db8::1      IPv6 (same as ping6)
Ends with an error (ERR <> 0) when no answer arrives.

ping6

Send echo requests to an IPv6 address (ping accepts IPv6 too)

ping6 [-n COUNT] [-l SIZE] [-s SOURCE] [-i IF] ADDRESS
  -n COUNT    number of requests (default 10)
  -l SIZE     data bytes per request (0-1400, default 16)
  -s SOURCE   source address (default: one that can reach ADDRESS)
  -i IF       interface name (default: the first one)
  ping6 -n 3 fe80::1       link-local address: the link-local source is used
Ends with an error (ERR <> 0) when no answer arrives. Without an address
other than link-local, run 'ifconfig6 -s eth0 auto' first.

tftp

Download a file with TFTP

tftp [-i IF] [-l PORT] [-r PORT] [-c TRIES] [-t TIMEOUT] [-s BLKSIZE] SERVER REMOTE [LOCAL]
  tftp 192.168.1.5 boot/grubx64.efi            saved as grubx64.efi
  tftp 2001:db8::5 boot/grubx64.efi fs1:\g.efi  IPv6 server

http

Download a file with HTTP

http [-i IF] [-t TIMEOUT_MS] [-s BUFSIZE] URL [LOCAL]
  http http://192.168.1.5/images/tool.efi
  http http://[2001:db8::5]:8080/tool.efi fs1:\tool.efi
                               IPv6 address: in brackets
Only http:// (https needs the firmware TLS driver).

Editors

CommandWhat it does
editFull-screen text editor (UTF-8 or UCS-2 files; Ctrl-E for help)
hexeditFull-screen hex editor for files, disk blocks or memory

edit

Full-screen text editor (UTF-8 or UCS-2 files; Ctrl-E for help)

edit FILE
  edit FILE           open FILE; a new FILE is created when you save
Keys (Ctrl-E shows them inside the editor):
  F1  Ctrl-G  go to line          F6  Ctrl-K  cut the current line
  F2  Ctrl-S  save                F7  Ctrl-U  paste the cut lines
  F3  Ctrl-Q  exit (also Esc)     F8  Ctrl-O  open another file
  F4  Ctrl-F  find                F9  Ctrl-T  switch UTF-8 / UCS-2
  F5  Ctrl-R  find and replace    F10 Ctrl-N  find next
Find is case-sensitive and wraps around; replace asks y/n/a(ll) per match.
Cut lines in a row form one block; paste inserts the last block cut.
The file keeps its encoding (UTF-8 with or without BOM, or UCS-2) and line
ends (LF or CRLF). Tab characters are kept and shown up to the next multiple
of 4 columns; the Tab key inserts 4 spaces. Needs an interactive console.

hexedit

Full-screen hex editor for files, disk blocks or memory

hexedit [-f] FILE | hexedit -d DEVICE LBA COUNT | hexedit -m ADDRESS SIZE
  -d DEVICE LBA COUNT   disk blocks (DEVICE: blkN, fsN: or a handle;
                        numbers in hex)
  -m ADDRESS SIZE       memory (writing is disabled while Secure Boot
                        is active)
  Keys: arrows, PgUp/PgDn, Tab switches hex/ASCII, F1 go to, F2 save, F3 exit.

Index

Commands, BASIC functions and topics in alphabetical order. The number after each entry is the section.