Java, .NET and System Commands

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

Written against MATLAB R2026b. MATLAB can use code that others wrote in other languages, and it can start any program the operating system can run. This page covers the three oldest and most direct ways: calling Java classes from the command line, calling .NET classes, and running operating system commands with system. The ways that need more preparation (Python with the py. prefix, C++ libraries with clibgen, and MEX files) have their own pages, Calling Python from MATLAB and C and C++ Libraries and MEX. Fortran MEX files are covered from the Fortran side on Fortran: MATLAB MEX, Engine and MAT-File APIs and are only pointed at here.

The release matters most for Java. The documentation for R2026b states that in R2026b and later releases MATLAB does not include a Java Runtime Environment, and that an OpenJDK is needed to enable the products and features that require Java. Releases before R2026b ship their own Java, so code and books that never mention installing Java describe an older MATLAB. NET.unloadAssembly and the Unloadable argument of NET.addAssembly are R2026a; the dotnetenv function is R2022b; setenv with a dictionary is R2023a and removing a variable with missing is R2023a; cmdsep is R2023b; jenv is R2021b, and the object it returns (JavaEnvironment) is R2023a. The release numbers come from the "Introduced in" and "Since" notes of the R2026b reference pages.

GNU Octave 11.3.0 is not MATLAB here. The examples that call system, getenv, setenv, unix and regexp were run in Octave 11.3.0 inside a Linux container, and the output shown is labelled "Octave 11.3.0 output". The Java examples were also run, but with Octave’s own Java interface (javaObject, javaMethod, javaaddpath), which is a different implementation from the MATLAB one: it agrees on the names and on the main idea, and it differs on details such as the conversion of results, so only what both define is shown as output. The MATLAB-only forms (import, the add(A,5) call syntax, jenv, matlab_jenv) and everything on .NET (NET.addAssembly, dotnetenv, System.DateTime) are labelled "not run here" and were checked line by line against the R2026b reference pages. The ! shell escape is also not run: Octave does not accept it in a script file (the same script stopped with a syntax error), so it is described from the documentation only. Container output also differs from what you see on Windows, where commands like dir and the shell cmd are used.

Which way for which job

Job Tool Needs

Use a class of the Java standard library or a .jar file

javaObject, javaMethod, dot syntax, import

A supported OpenJDK (since R2026b you add it yourself)

Use a .NET class library

NET.addAssembly, then the namespace as a prefix

A supported .NET installation (see NET.isNETSupported)

Start a program or a shell command and read its answer

system

The program on the machine

Start a program and let it run in front of you

! (shell escape), system with a trailing &

The program on the machine

Read or change the settings a started program sees

getenv, setenv, unsetenv

Nothing

Rules of thumb. Prefer a MATLAB function to a shell command whenever one exists: copyfile, movefile, mkdir, delete, zip and tar do the same work on every platform and cannot be tricked by the characters in a file name. Prefer an interface to a command-line program when you need its answer in a data structure: parsing text from system is the last resort. Reach for Java or .NET when you need a library that has no MATLAB equivalent and has a ready class interface.

Java in R2026b: MATLAB no longer installs Java

MATLAB has been built on Java for its desktop for most of its history, and for years the installation contained a private copy of the Java Runtime Environment. R2026b stops doing that. The reference page of jenv says it in the version history: as of R2026b MATLAB no longer includes Java by default, and the option "factory", which used to select the Java that came with MATLAB, is now the same as "system".

Situation What to do

You need Java classes (or a product or feature that requires Java) and MATLAB is R2026b or later

Install an OpenJDK yourself. Two ways: the MATLAB Support for OpenJDK add-on, or a specific OpenJDK that you download from Adoptium.

You use the add-on

Open the Add-Ons Explorer (Home tab, Environment section, Add-Ons), search for OpenJDK, install MATLAB Support for OpenJDK, then call jenv -clear so that MATLAB uses it.

You use a specific OpenJDK

Install a supported version. If it is the first Java on the system path, call jenv("system"). Otherwise call jenv(JRE_path) with the folder of the installation.

You want to know what MATLAB uses

Call jenv with an output (je = jenv), which returns a JavaEnvironment object (version, home, library, status, configuration), or version -java. If Java was already loaded, restart MATLAB after changing it.

You configure it from outside MATLAB, for example in a script that prepares a machine

matlab_jenv at the operating system prompt.

jenv("system") looks for Java version 8 first, then 11, then 17, and uses the platform’s usual way of finding Java: the java -version command and the registry on Windows, the JAVA_HOME variable and the system path on Linux, and /usr/libexec/java_home on macOS. The call changes the Java path of MATLAB for all future sessions, not the Java of other applications. jenv("-allusers",...) configures it for every user of the installation and needs administrator rights. With jenv("-clear") (since R2026a), MATLAB looks for Java in this order: the MATLAB Support for OpenJDK add-on, then on Windows and Linux the system Java; before R2026b the Java shipped with MATLAB came after those; on Apple silicon it uses the system Java.

The supported versions are listed on the page Versions of OpenJDK Compatible with MATLAB by Release, and it changes with every release, so check it for yours. For R2026b the page lists OpenJDK 8, 11, 17, 21 and 25; the older releases it lists support fewer (R2025b: 8, 11, 17, 21; R2023b: 8 and 11; R2022b and before: 8 only). Eclipse Adoptium and Amazon Corretto builds are the ones the page names as supported. The page also carries a footnote that on Linux only OpenJDK 21.0.2+13 and earlier are supported for R2024b. Which Java you have therefore decides which classes you can load: a library compiled for a newer class file version than the Java of MATLAB fails when you load it.

Not run here (the commands of the documentation page Configure Your System to Use Java, with a placeholder folder):

% Option 1: the add-on, after installing "MATLAB Support for OpenJDK"
jenv -clear

% Option 2: an OpenJDK downloaded from Adoptium
jenv("system")                       % when it is the first Java on the path
jenv("/opt/openjdk-21")              % or the folder of the installation (placeholder)

je = jenv                            % JavaEnvironment object: Version, Home, Status ...
version -java                        % the Java version MATLAB is using

A consequence for tools and scripts: a CI image that runs MATLAB R2026b needs the OpenJDK step in its set-up if any code calls Java (see Continuous Integration for the shape of such a pipeline). The Java classes of the standard library are on the class path already, but the runtime that runs them is not.

Calling Java classes

The documentation page Call Java from MATLAB lists what the interface gives you: access to class packages of the Java standard libraries (input and output, networking), access to third-party classes, construction of Java objects in the workspace, calls of their methods with either Java or MATLAB syntax, and the exchange of data between MATLAB variables and Java objects. The functions are these.

Function Purpose

javaObject(ClassName,x1,...,xN)

Calls a Java constructor and returns the object. The class name is a string or a character vector.

javaMethod(MethodName,JavaObj,x1,...,xN)
javaMethod(StaticMethodName,ClassName,x1,...,xN)

Calls an instance method, or a static method of a class. It is the only way to call a method whose name is longer than namelengthmax.

javaArray(ClassName,dims)

Constructs a Java array object.

import

Lets you refer to a class by its short name in the current function (or in the base import list at the command prompt). clear import empties the list.

isjava, usejava, javachk

Test whether a value is a Java object and whether the Java feature is available.

javaaddpath, javarmpath, javaclasspath

Manage the dynamic Java class path (see below).

javaObjectEDT, javaMethodEDT

The same as the first two, but run on the Event Dispatch Thread of Java.

There are two call syntaxes for the same method, and the documentation recommends MATLAB syntax in general: method(object,arg1,...,argn) or the Java form object.method(arg1,...,argn); for a static method, class.method(arg1,...,argn).

The following script was run in Octave 11.3.0 with its own Java interface. It builds an ArrayList, adds numbers with the Java call syntax, and shows that Java objects are references: after B = A, a change through B is visible through A. This is the same behaviour as a handle class (see Value and Handle Classes), and the MATLAB documentation page Call Java Method demonstrates it with the same class.

A = javaObject('java.util.ArrayList');
A.add(5);
A.add(10);
disp(class(A))
disp(A.size())
B = A;
B.add(20);
disp(A.size())
x = javaMethod('isNaN', 'java.lang.Double', 2.2);
disp(x)
s = javaObject('java.lang.String', 'hello');
disp(char(s))
disp(s.toUpperCase())
disp(isjava(A)); disp(isjava(5))

Octave 11.3.0 output (MATLAB prints the class, the sizes and the logical values in its own layout):

java.util.ArrayList
2
3
0
hello
HELLO
1
0

Read it against the documentation. A has two elements, B = A copies the reference, and B.add(20) is seen through A (size 3). The static call isNaN on 2.2 returns false. In MATLAB the sequence is the one of the Call Java Method page: import java.util.ArrayList, then A = ArrayList;, then add(A,5); with MATLAB syntax or A.add(10); with Java syntax (not run here; Octave rejected the first of those forms, which is one of the differences of its interface).

import. A fully qualified name such as java.util.ArrayList is long. import java.util.ArrayList adds the class to the import list and afterwards ArrayList alone is enough. Called inside a function, it affects only that function; at the command prompt it affects the base list. The documentation example uses import java.lang.String and import java.util.Enumeration and then calls String('hello'). A wildcard form (import java.lang.*) also exists. Not run here.

The class path: static and dynamic

MATLAB finds a Java class by searching the Java class path, a series of folders and .jar files, in order, and it stops at the first file that holds the class. The built-in Java classes of the standard library are on the path already. The path has two parts, and MATLAB searches the static one first.

Static path Dynamic path

Loaded

At the start of every session, from MATLAB’s built-in path and the file javaclasspath.txt

Any time during a session

Changed with

Editing javaclasspath.txt (in the settings folder, prefdir, or in the startup folder) and restarting MATLAB

javaaddpath, javarmpath, javaclasspath(dpath)

Use it for

Classes that are finished: faster class loading, and the behaviour is the reference one

Classes you are developing: you can change a class without restarting

Caution

A class on the static path must not depend on classes that are on the dynamic path.

Classes load more slowly, and might not behave identically to the same classes on the static path.

A line of javaclasspath.txt names a folder or a .jar. For unpackaged .class files give the folder that holds them. For a package give the parent of the folder that is the first component of the package name, for example c:\work for the package com.mw.tbx.ini whose classes are in c:\work\com\mw\tbx\ini. For a .jar give the full path including the file name, which is different from the other two cases. The macros $matlabroot, $arch and $jre_home simplify specifications for different platforms. Starting MATLAB with -nouserjavapath ignores the entries of the file. The settings folder is shown by prefdir, and a javaclasspath.txt in the startup folder is read before the one in the settings folder. Not run here.

For the dynamic path:

javaclasspath("-dynamic")            % show the dynamic path
javaaddpath(pwd)                     % add the current folder at the beginning
javaaddpath("C:\work\tools.jar","-end")   % add a .jar at the end (placeholder path)
p = javaclasspath("-all");           % static entries first, then the dynamic ones
javarmpath(pwd)                      % remove an entry

Not run here (syntax lines of the R2026b reference pages javaaddpath and javaclasspath). Two properties of these functions cause real problems. First, javaaddpath, javarmpath and javaclasspath(dpath) clear all variables and global variables of the workspace (the Java Class Path page says this explicitly for the functions that change the dynamic path), so run them at the start of a script and not in the middle of a computation. Second, they clear the definitions of all Java classes that were loaded from files on the dynamic path; if the message "Not clearing Java" appears, the dynamic path was updated but you may have to type clear java for MATLAB to notice the new classes. Octave 11.3.0 also has javaaddpath and javarmpath: the sequence add, list and remove behaved the same way there, with the entry present after the add and absent after the remove; Octave does not state that it clears variables, so the clearing is from the MATLAB documentation.

For your own classes the documentation recipe is: write the .java file, compile it to .class with a Java Development Kit that is outside MATLAB (the page points to the OpenJDK versions page for the versions that are compatible), put the folder on the dynamic path with javaaddpath and call package.myclass.mymethod(params). When the class is finished, move its folder to javaclasspath.txt so that it is always available.

The documentation also states a limitation: MATLAB does not support .jar files that were generated by MATLAB Compiler SDK on the dynamic path through javaaddpath.

Data conversions

MATLAB converts data at the boundary in both directions, and the rules are the source of most Java surprises.

MATLAB to Java. The table of the page Pass Data to Java Methods gives, for each MATLAB type, the Java parameter types from the closest match to the least close. The main rows:

MATLAB argument Java parameter types, closest first

logical

boolean, byte, short, int, long, float, double

double

double, float, long, int, short, byte, boolean

single

float, double

int8, uint8

byte, short, int, long, float, double

string scalar, character vector, char scalar

String

string array, cell array of character vectors

String[]

cell array of objects

Object[]

a MATLAB object (classdef)

unsupported

When the parameter is declared java.lang.Object, MATLAB chooses the closest java.lang class: logical becomes Boolean, double becomes Double, single becomes Float, a scalar char becomes Character, a string scalar or nonempty character vector becomes String, and int32 becomes Integer. This automatic boxing happens only for Object: MATLAB does not support autoboxing for other classes, so a double is not converted to java.lang.Double for a parameter declared Double; you must construct the object (java.lang.Double(2.2)). Three more rules from the page are worth knowing: an empty matrix becomes Java null (except that an empty character vector becomes an empty String when the parameter is a String); a missing value in a string becomes null; and an integer that does not fit the parameter is not saturated as in MATLAB integer arithmetic but handled as Java does, by discarding the high-order bits (see Numeric Types and Floating Point for MATLAB’s saturating behaviour). A floating-point value passed to an integer parameter is first converted to a 64-bit integer with the fractional part truncated.

Java to MATLAB. The page Handle Data Returned from Java Methods distinguishes three cases. A Java primitive return becomes a MATLAB type: boolean becomes logical; byte, short, int and long become double when scalar (and int8, int16, int32, int64 when an array); float becomes double when scalar and single when an array; double stays double; char becomes char. A method declared to return java.lang.Object is converted according to the actual class returned (Integer to double, String to char). Every other object stays a Java object, and you convert it yourself:

To get Use

A number from an object derived from java.lang.Number

double(obj), or write a toDouble method in your own class

Text from a java.lang.String

char(obj) or string(obj), or a toString or toChar method in your own class

A structure from an object that defines fields

struct(obj)

A cell array from a Java array

cell(arr), then cell2mat when the elements have a common type

For example, the documentation turns the ArrayList of the example above into a MATLAB matrix by calling toArray, then cell and cell2mat, and obtains [5 10 15] (not run here). The related MATLAB types are in Cell Arrays, Strings and Character Arrays and Type Identification and Conversion.

Pitfalls. Treat the following as the short list of what goes wrong: (1) the Java is missing on R2026b (the symptom is that no Java class can be created, and usejava("jvm") is the test); (2) the class file needs a newer Java than the one MATLAB uses; (3) a changed class on the static path needs a restart; (4) javaaddpath cleared your variables; (5) a method result is a Java object and not the MATLAB value you expected, so double, char, struct or cell is missing; (6) objects are references, so two variables can change the same list. The error wording and identifiers differ between releases and are not shown here.

Java classes are also a way to reach what MATLAB does not provide in a convenient form, and the standard library is the first place to look. The Java language itself is in the Java reference of this site: Java: Getting Started and Java: Build and Tooling explain how to produce the .class and .jar files that the class path expects.

.NET

MATLAB loads .NET assemblies the same way: a namespace becomes a prefix in the MATLAB workspace. MATLAB supports .NET, including .NET Core on Windows, macOS and Linux, and the .NET Framework on Windows only. By default MATLAB uses the latest supported .NET version it finds on the machine. tf = NET.isNETSupported tells you whether a supported version exists, and dotnetenv (R2022b) reads or changes the runtime: by default MATLAB loads assemblies with the .NET Framework on Windows and with .NET Core on macOS and Linux, and dotnetenv("core",Version="8") selects a numbered .NET Core runtime. The runtime is initialised when you first call a function in the NET or System packages, so change it before that or restart MATLAB. The setting persists across sessions. Not run here.

if ~NET.isNETSupported
    error("No supported .NET found on this machine")
end

ne = dotnetenv                       % the current runtime: Runtime, Status, Version
dotnetenv("core",Version="8");       % use .NET 8 (before .NET is loaded)

Using a class of the base class library. The classes of the System namespace that MATLAB loads at start-up (the documentation says it loads mscorlib dynamically) need no NET.addAssembly. The documentation example uses System.DateTime: netDate = System.DateTime.Now creates an object, netDate.DayOfWeek and netDate.Hour read properties, ToShortTimeString(netDate) and AddDays(netDate,7) call methods in MATLAB syntax, and System.DateTime.DaysInMonth(netDate.Year,netDate.Month) calls a static method. A constructor is a call of the class name, myDate = System.DateTime(2000,1,31). If you do not know the signatures, methods System.DateTime, properties System.DateTime and methodsview("System.DateTime") show them. Not run here.

Loading an assembly with NET.addAssembly. The two forms are asmInfo = NET.addAssembly(globalName), for a Base Class Library assembly or a Global Assembly Cache one (the cache exists on the .NET Framework only), and asmInfo = NET.addAssembly(privateName), for a private assembly where you give the full path of the file. The result is a NET.Assembly object whose properties Classes, Structures, Enums, GenericTypes, Interfaces and Delegates list what the assembly holds. After the call, import the namespace (or use the full name) and call the classes. The heatflow project could keep a small, assumed .NET library of unit conversions (placeholder file name and class name, so this is an illustration and not a library that exists):

% Not run here: placeholder assembly, shown to illustrate the call sequence
dotnetenv("core");
asm = NET.addAssembly("C:\libs\HeatflowUnits.dll");   % full path of the private assembly
disp(asm.Classes)                                     % list the classes it contains
k = Heatflow.Units.CelsiusToKelvin(21.4);             % a static method of a placeholder class
disp(double(k))

Since R2026a an assembly can be made unloadable: NET.addAssembly(path,Unloadable=true) followed later by NET.unloadAssembly("MyAssembly"). This is supported only for private .NET Core assemblies; a .NET Framework assembly cannot be unloaded, so after rebuilding one you must restart MATLAB.

Data and exceptions. MATLAB converts most primitive arguments automatically. A System.String result is converted with char; an array of strings is passed as a cell array; other arrays are made with NET.createArray; and an array returned from .NET is converted with the corresponding numeric function for rectangular arrays or with cell for jagged ones. You cannot pass a structure array, a sparse array or a complex number to a .NET method, and NET.createDictionary (R2023a) converts a MATLAB dictionary into a .NET one. An exception thrown by .NET reaches MATLAB as a NET.NetException, which derives from MException, so an ordinary try/catch handles it (see Errors, Warnings and Exceptions).

Limits. The page Limitations to .NET Support lists what does not work, including subclassing .NET classes from MATLAB, access to nonpublic members, casting, and concatenating .NET objects into an array. Setting a static property with ClassName.propertyname is not supported either; the page tells you to use NET.setStaticProperty. Before R2025a saving .NET objects to MAT files was not supported (see Workspace Variables and MAT-Files).

The C# language, and how .NET code calls native code in the other direction, are in C#: Native Interop; the language is introduced in C# and .NET. To call MATLAB from Java or .NET (the opposite direction), see Call MATLAB from Java and Call MATLAB from .NET in the documentation; they are not covered here.

Operating system commands

system, unix and dos

system runs a command in the operating system’s shell and waits for it. The syntaxes:

status = system(command)
[status,cmdout] = system(command)
[status,cmdout] = system(command,'-echo')
[status,cmdout] = system(___,EnvName1,EnvVal1,...,EnvNameN,EnvValN)

status is the exit status of the command: zero means success, anything else means failure. cmdout is the output of the command, as a character vector. The option '-echo' also shows the output in the Command Window, which is useful for commands that need user input. Passing the environment variable names and values after the command sets them for this call only (since R2023a a value of missing removes a variable). unix and dos are the platform-specific versions with the same two outputs; the reference pages of both say "for platform-independent code, use the system command", so use system in new code. A command that ends with & is started in the background; status then reports whether it started, and cmdout is empty. MATLAB chooses the shell this way: on Windows it checks the variable MATLAB_SHELL and uses cmd when it is empty (the supported shells are cmd, powershell and the mingw family); on UNIX it checks MATLAB_SHELL, then SHELL, and falls back to /bin/sh. Callbacks do not run while system is waiting. The reference page also notes that on UNIX system redirects standard input to the command, so a command that waits for input can swallow what you type; adding < /dev/null to the command disables that.

The next script was run in Octave 11.3.0 in a Linux container. It shows the status and output of a successful command, the exit status of a command that fails with a chosen code, and a command that does not exist.

[status, out] = system('echo hello from the shell');
fprintf('status=%d\n', status); fprintf('out=[%s]\n', out);
[status, out] = system('exit 3');
fprintf('status=%d out=[%s]\n', status, out);
[status, out] = system('badcmd_does_not_exist');
fprintf('bad: status=%d nonzero=%d\n', status, status ~= 0);
[status, out] = unix('echo via unix');
fprintf('unix: status=%d out=%s', status, out);

Octave 11.3.0 output (MATLAB layout may differ, and the text of the shell’s error line for the missing command belongs to the shell):

status=0
out=[hello from the shell
]
status=3 out=[]
sh: 1: badcmd_does_not_exist: not found
bad: status=127 nonzero=1
unix: status=0 out=via unix

The output keeps its trailing newline (use strtrim), a failing command gives a nonzero status (here 3 for exit 3 and 127 for "command not found" in this shell), and a program that writes only to standard error shows that text on the console instead of in cmdout in Octave; the line sh: 1: badcmd... above is that case. To capture the messages as well, redirect inside the command: (echo out; echo err 1>&2) 2>&1 returned both lines in cmdout in the same session. In MATLAB the unix page says it returns the standard output of the command, so use the same redirection. Check status and decide what a failure means (see Errors, Warnings and Exceptions); system does not throw an error for a failing command.

A platform-independent way to write the separator when you must chain commands is cmdsep (R2023b), which returns the separator of the current platform: & on Windows and ; on UNIX, as the Run External Commands page says. Even so, the ispc, isunix and ismac tests are the usual way to branch on the platform, and a MATLAB function avoids the branch altogether.

The shell escape !

At the command prompt, ! followed by a command line passes the rest of the line to the operating system: !vi yearlystats.m opens the vi editor on UNIX, and !dir & shows a directory listing in a console window on Windows. After the program ends, control returns to MATLAB. A trailing & runs the program in the background or in a separate window, and !excel.exe & returns to the prompt at once. The documentation notes that to use the exclamation point in a factorial you call the function factorial. The shell escape returns no result and no status, so use system whenever a script needs the answer. Not run here: the same script file stopped with a syntax error in Octave 11.3.0, because Octave does not read ! as a shell escape in a file. In MATLAB, ! belongs to interactive work; in a file prefer system.

Environment variables: getenv, setenv

A started program inherits the environment of MATLAB, and you control that environment with getenv, setenv and unsetenv. The syntaxes: val = getenv(varname) returns the value as a character vector (empty if the variable does not exist); val = getenv() returns a dictionary of strings with all names and values; setenv(varname,varvalue) sets or creates a variable and setenv(varname) gives it a null value (on Windows this removes it); setenv(d) sets every variable of a dictionary (R2023a). setenv passes the value to the operating system unchanged, so characters such as ;, /, :, $ and % are not expanded. Processes started with system, unix, dos or ! see the new values, and the changes do not persist after MATLAB ends. A variable name that contains = raises an error. This is the same mechanism that keeps keys out of the source (see Secrets and Protecting Code) and that the web functions use in JSON, XML and Web Access.

For the heatflow project the folder of the simulation data can come from the environment, and a post-processing program started with system reads the same variable. This script was run in Octave 11.3.0.

setenv('HEATFLOW_DATA', '/tmp/plate-data');
disp(getenv('HEATFLOW_DATA'))
disp(isempty(getenv('HEATFLOW_NOT_SET')))
[status, out] = system('echo "$HEATFLOW_DATA"');
fprintf('status=%d out=%s', status, out);
unsetenv('HEATFLOW_DATA');
[status, out] = system('echo "[$HEATFLOW_DATA]"');
fprintf('status=%d out=%s', status, out);

Octave 11.3.0 output:

/tmp/plate-data
1
status=0 out=/tmp/plate-data
status=0 out=[]

The variable set with setenv is visible to the child process, a missing variable gives an empty result, and after unsetenv the child sees nothing. The value is placed inside double quotes in the shell command so that a value with a space stays one word. On Windows the reference page of system shows the override for the shell form (set PATH=... && command) and for UNIX export PATH=... ; command; the EnvName, EnvVal arguments of system are the portable way, not run here. To extend the program search path of the session, the Run External Commands page appends a folder to getenv('PATH') and calls setenv('PATH',...) (not run here).

Security: do not build commands from untrusted text

system hands one string to a shell, and a shell treats some characters as syntax: ;, &, |, backticks, $(...), quotes, redirections and newlines. If a part of the string comes from outside the program (a file name from a user, a field of a JSON file or a web response, a plate name from a configuration file), then whoever controls that part can add commands of their own. This is shell injection. The reference page of system says that command can contain several commands, and cmdsep exists for exactly that, so the shell will run what the string contains. Nothing in MATLAB stops it.

The next script was run in Octave 11.3.0. The name is a harmless placeholder that stands for hostile input: it contains a command separator followed by a second command that only prints a word.

name = 'plate one; echo INJECTED';
[~, out] = system(['echo ' name]);
disp(out)
[~, out] = system(['echo ' "'" name "'"]);
disp(out)

Octave 11.3.0 output:

plate one
INJECTED

plate one; echo INJECTED

The first call ran two commands: the text after the semicolon was executed. The second call wrapped the value in single quotes and it was printed as data. Quoting is not a complete defence, though: a value that itself contains a single quote ends the quoted string, and the rules differ between cmd, powershell and the UNIX shells. The defences, from the strongest to the weakest:

  • Do not use a shell at all. A MATLAB function does the same work without one: copyfile, movefile, mkdir, delete, zip, unzip, tar, webread, readtable. A file name given to them is a name and nothing else.

  • Do not pass a command line; pass arguments. Java’s ProcessBuilder takes the program and its arguments as a list and does not start a shell, so the same hostile text is a single argument. This script was run in Octave 11.3.0 with its Java interface; the MATLAB Java interface is the same idea, and in MATLAB it needs an OpenJDK in R2026b, as above (the equivalent in MATLAB is not run here).

  • Validate against an allow list before building any command: accept only the characters you expect, and reject everything else, instead of trying to remove bad ones.

  • Keep values out of the command string. Put them in environment variables with setenv (or the EnvName, EnvVal arguments) and refer to them inside double quotes in a fixed command, as in the heatflow script above. A POSIX shell does not interpret the contents of a variable again after expanding it inside double quotes (the rules of cmd differ).

  • Do not run MATLAB with more rights than the job needs: the documentation notes that system uses the same credentials as the process that started MATLAB.

pb = javaObject('java.lang.ProcessBuilder', {'echo', 'plate one; echo INJECTED'});
pb.redirectErrorStream(true);
pr = pb.start();
rd = javaObject('java.io.BufferedReader', javaObject('java.io.InputStreamReader', pr.getInputStream()));
disp(char(rd.readLine()))
disp(pr.waitFor())

Octave 11.3.0 output:

plate one; echo INJECTED
0

The whole text is the single argument of echo; nothing after the semicolon ran. The allow-list check is short and was run in the same session:

name = 'plate one; echo INJECTED';
if isempty(regexp(name, '^[A-Za-z0-9_.-]+$', 'once'))
    disp('rejected: name has characters outside the allowed set')
else
    system(['echo ' name]);
end
name = 'plate-01.csv';
if ~isempty(regexp(name, '^[A-Za-z0-9_.-]+$', 'once'))
    [st, out] = system(['echo ' name]);
    fprintf('%d %s', st, out);
end

Octave 11.3.0 output:

rejected: name has characters outside the allowed set
0 plate-01.csv

The first value was rejected and nothing ran; the second passed the pattern and ran as a plain file name (see Regular Expressions and Patterns for the pattern syntax). The same care applies to anything that evaluates text as code; see eval and Its Alternatives. Never write a real password or key into a command line either: it can be shown in process lists. Use a placeholder in examples, and read the real value from the environment or the vault.

Where the books are outdated

Books on MATLAB written before R2026b describe a MATLAB that ships its own Java, and some describe the Java desktop windows of the old environment; a current installation needs an OpenJDK for Java classes and for the products that depend on Java, and the new desktop is described in The Desktop and the Command Window. Older text also uses unix and dos where the documentation now points to system, shows ! in scripts where system returns a status, writes javaaddpath calls without noting that they clear the workspace, and predates the .NET Core runtime (dotnetenv, R2022b), the unloadable assemblies of R2026a and the dictionary forms of getenv and setenv (R2023a). The shell injection hazard is rarely mentioned in them.