`multipass exec` and shells

Errors or typos? Topics missing? Hard to read? Let us know or open an issue on GitHub.

See also: exec

How exec parses commands

When one calls multipass exec from a shell, the whole command is first parsed by the shell one is in. The result of that parsing is what the multipass client sees in its argument list (argv).

For example, when multipass exec primary -- ls ~ is entered on a Linux shell, the tilde is translated to the calling user’s local home before the command is passed to multipass. But that is not the case on a Windows PowerShell, because “~” does not have the same meaning there.


Quoting also depends on the calling shell. On most Linux and macOS shells, single quotes delimit a string that the shell passes verbatim to the program.

The Windows Command Prompt doesn’t treat single quotes this way. A program called with 'abc def' there would get two arguments: 'abc and def'. Double quotes can be used instead: "abc def", but the string they delimit is subject to shell expansion.

C:\> set USER=me
C:\> multipass exec -n rich-zorilla -- bash -c "echo %USER%"

With Multipass, this is seldom a problem, as expansions use a different syntax on Linux:

C:\> multipass exec -n rich-zorilla -- bash -c "echo $USER"

How SSH parses commands

Multipass executes the command after -- in the given instance as if there was no further shell in the middle (“as if” because the reality is a little more complicated).

This is slightly different from what one would get with SSH. Consider the following command in bash:

$ multipass exec mp-builder -- python3 -c 'import sys; print(sys.argv)' foo bar
['-c', 'foo', 'bar']

With SSH, the whole command would need to be quoted.

$ ssh -i /var/root/Library/Application\ Support/multipassd/ssh-keys/id_rsa ubuntu@ python -c 'import sys; print(sys.argv)' foo bar
bash: -c: line 1: syntax error near unexpected token `sys.argv'
bash: -c: line 1: `python -c import sys; print(sys.argv) foo bar'

Using a shell to parse commands

To overcome the above problem with multipass exec, one can still have another shell parse the command in the instance with multipass exec, it just needs to be called explicitly. For example, in bash we can say:

$ multipass exec calm-woodcock -- sh -c 'ls -a ~'
.  ..  .bash_logout  .bashrc  .cache  .profile  .ssh

Similarly, on the Windows Command Prompt:

$ multipass exec calm-woodcock -- sh -c "ls -a ~"
.  ..  .bash_logout  .bashrc  .cache  .profile  .ssh

Provided we use the appropriate quoting for the calling shell, this behaves the same regardless of the host platform. Without sh -c, it also fails on all platforms (although possibly in different ways, depending on whether or not the nested command is quoted). The inner-shell trick provides a more consistent cross-platform experience.

Input/output redirection

The multipass exec command can be used together with piping to redirect input/output between commands run on the host and on the instance. For example, this writes the contents of the current directory on the host to a file called save in the instance rich-zorilla:

$ ls -la | multipass exec -n rich-zorilla -- bash -c "cat > save"

Conversely, this saves the contents of the home directory inside rich-zorilla to a file on the host:

$ multipass exec -n rich-zorilla -- bash -c "ls -la" | cat > save

Other shell tricks

Other shell features can be combined with multipass exec for different effects. Here is an example using bash’s here-strings:

$ multipass exec -n primary -- bash << EOF
> hostname
> whoami

And another using command substitution:

$ ping $(multipass exec rich-zorilla -- hostname -I)
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=64 time=0.371 ms
64 bytes from icmp_seq=2 ttl=64 time=0.304 ms
64 bytes from icmp_seq=3 ttl=64 time=0.439 ms
--- ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2054ms
rtt min/avg/max/mdev = 0.304/0.371/0.439/0.055 ms

Last updated 13 days ago. Help improve this document in the forum.