Skip to content

CLI Machines Remote Exec

github-actions[bot] edited this page Aug 17, 2026 · 3 revisions

ed machines exec

Runs one command on one machine and gets out of the way. Both output streams stay separate, the remote exit code becomes yours, and the only byte ed adds is the newline it puts back on a last line that had none.

ed machines exec [--tty] <machine> [--] <command...>
ed machines run  [--tty] <machine> [--] <command...>

Arguments

Name Type / values Default What it does
<machine> machine name, ssh config alias, id, or an unambiguous prefix of a name or alias none, required Which machine to run on. Matched case-insensitively: exact name first, then ssh alias, then UUID, then a unique prefix.
<command...> one or more words none, required The command to run. A single leading -- is dropped before anything is sent.

Options

Name Type / values Default What it does
--tty, -t flag off Allocate a terminal for the command with ssh -tt. Needed by anything interactive; wrong for scripting.
--help, -h flag off Print the help for this command on stdout and exit 0.
--version flag off Print the CLI version, 1.0.0, and exit 0. Generated by the parser and accepted here as well as at the root.

Every one of these has to come before the machine name. The command array is captured for passthrough, so the parser stops reading options at the first positional word: in ed machines exec tuf --tty top the --tty is part of the command and is sent to the machine, where it is not a program:

$ ed machines exec tuf --tty top
bash: line 1: --tty: command not found
$ echo $?
127

There is no --json. ed machines exec is one of the handful of commands that do not offer it, because the bytes on stdout belong to the remote process and wrapping them would break every pipe. --json before the machine name is an unknown option and exits 2.

Examples

ed machines exec tuf uptime
ed machines exec tuf -- ls -la /etc
ed machines exec --tty tuf top
ed machines run tuf systemctl status nginx

Behaviour notes

Nothing local is mutated except the remembered working directory, which only cd writes, and what opening the connection leaves behind: the ControlMaster socket and the host key pinned beside it. The machine is resolved from Edith's own machine list, so a host is never retyped, and the connection is opened before the command runs: if a ControlMaster socket for that machine is already alive, from the app or from an earlier ed, the command is one round trip on it, and if there is not, ed opens one that outlives the process by ControlPersist=10m.

How the words become a command line depends on how many there are. One word is sent verbatim, which is the one-shot escape hatch for anything the remote shell has to interpret. Several words are quoted individually and joined, so an argument with spaces survives and a shell metacharacter does not:

$ ed tuf echo 'a;pwd'
a;pwd

$ ed tuf 'ls /etc | head -3'
adduser.conf
alsa
alternatives

A word is left unquoted only when every character in it is a letter, a digit or one of . _ - + / = : @ % ,. Everything else is wrapped in single quotes, which covers spaces, ~, *, $, |, >, ;, & and quotes themselves. So ed tuf ls '*.log' does not glob on the machine, and ed tuf ls '~/Desktop' does not expand the tilde; quote the whole line instead when you want either.

--tty builds its command line differently: the words are joined with single spaces and nothing is quoted, because the point of a terminal session is to let the remote shell parse the line. The same input therefore behaves differently under the two paths:

$ ed machines exec --tty tuf echo 'a;pwd'
a
/home/pulkit
Shared connection to 192.168.1.12 closed.

That trailing line is ssh's own, on stderr, and appears on every --tty run.

Stdin is not forwarded on the plain path. The remote process is given /dev/null, so printf 'x\n' | ed tuf cat prints nothing and ed tuf wc -l < file counts zero. With --tty your terminal is handed straight to ssh and typing works, but a redirected file or a pipe still will not end the command: the remote side sees a terminal rather than a closed pipe, so a cat with nothing more to read waits instead of exiting. Feed data to a machine with ed machines files put or by quoting a line that reads the file on the far side.

Output is read line by line off two pipes and re-emitted, stdout to stdout and stderr to stderr, so a partial last line with no newline is still printed and the exact interleaving of the two streams is not guaranteed to match what the machine produced. There is no timeout: ed tuf tail -f /var/log/syslog streams until you interrupt it.

The exit code is the remote command's, verbatim and unclamped, which is the one documented exception to ed's 0 to 4 contract:

$ ed tuf exit 42
$ echo $?
42

Naming no command at all fails before anything is dialled, and the error names the machine you gave:

$ ed machines exec tuf
error: name a command to run, for example `ed tuf uptime`

The command runs in whatever directory ed <machine> cd last set for this terminal, on both the plain and the --tty path. That is a cd prefix on the line ssh is given, cd '<directory>' 2>/dev/null || cd;, so a directory that has since been deleted quietly falls back to the home directory rather than failing every command after it.

Where to go next

CLI reference

Getting Started
Config
App
Extensions
Permissions
Usage
System
Music
Calendar
Clipboard
Color
Download
Apps
Tools
Shelf
Cleaner
Machines
Machines Remote
Machines Docker
Machines Files
Machines Power
Machines Workspace
Companion
Herdr
Lid Awake
Machines Thermal

Guides

Clone this wiki locally