`multipass exec` command

See also: multipass exec and shells, How to use instance command aliases

The multipass exec command executes the given commands inside the instance. The first argument is the instance to run the commands on, -- optionally separates the multipass options from the rest - the command to run itself:

$ multipass exec primary -- uname -r

You can pipe standard input and output to/from the command:

$ multipass exec primary -- lsb_release -a | grep ^Codename:
No LSB modules are available.
Codename:       bionic

The -- separator is required if you want to pass options to the command being run. Options to the exec command itself must be specified before --.

It is possible to specify on which instance directory the command must be executed. For that, there are three options. The first one is --working-directory <dir>, which tells Multipass that the command must be executed in the folder <dir>. For example,

$ multipass exec arriving-pipefish --working-directory /home -- ls -a
.  ..  ubuntu

The ls -la command showed the contents of the /home directory, because it was executed from there.

The second option to specify the working directory is to look through the mounted folders first. In case we are executing the alias on the host from a directory which is mounted on the instance, the command will be executed on the instance from there. If the working directory is not mounted on the instance, the command will be executed on the default directory on the instance. This is the default behavior and no parameter must be specified for this mapping to happen.

The third option is to directly execute the command in the default directory in the instance (usually, it is /home/ubuntu. The parameter to force this behaviour is --no-map-working-directory.

The full multipass help exec output explains the available options:

$ multipass help exec
Usage: multipass exec [options] <name> [--] <command>
Run a command on an instance

  -h, --help                      Displays help on commandline options
  -v, --verbose                   Increase logging verbosity. Repeat the 'v' in
                                  the short option for more detail. Maximum
                                  verbosity is obtained with 4 (or more) v's,
                                  i.e. -vvvv.
  -d, --working-directory <dir>   Change to <dir> before execution
  -n, --no-map-working-directory  Do not map the host execution path to a
                                  mounted path

  name                            Name of instance to execute the command on
  command                         Command to execute on the instance

I have a lot of scripts that I need to pass into a multipass instance, on launch I am passing a YAML file with a cloud config using write_files. I need to keep those files up to date so I am trying to run a command that will write those files using exec.

Something along the lines of this:

multipass exec name -- sudo echo content > /path/to/file

What would be the best approach to accomplishing this?

1 Like

I’m finding that I can’t use either tilde expansion (~) or env vars (like $HOME) in commands passed in to multipass exec. For example, doing simply multipass exec primary ls ~ or multipass exec primary -- ls ~ will get:
ls: cannot access '~': No such file or directory

And that’s whether I use the -- arg or not. So first, is that to be expected? I suppose there may be some underlying reason (like technically the exec command is not opening a terminal session/running a shell, or doesn’t load the user’s profile, etc.).

But if nothing else, I wondered if it may be worth documenting (even if it somehow won’t ever be possible to use such args.)

Before someone may say, “no one should think to do such a thing”, I’ll note that WSL supports it without any problem (wsl ls ~). (I know, I know. WSL may be seen by some as a bastard frankenstein, and none of its ideas should be used for comparison. Let’s please not go there.)

Finally, let me head off anyone who may say “just do ls, alone, as the default pwd is already the ubuntu home directory”. I get that, of course. The point here is about using the ~ or the env vars in some other command, such as perhaps to name a subdir or file: without clarifying the location, it could make the intent unclear on the command.

Or worse, someone may be trying to run some command they’ve found documenting some step, and they do want to do it bit without shelling in if it’s a one-liner. That cmd won’t work if it references ~ or env vars, and that just caught me out, so I wanted to bring this up.

Hi @carehart :wave:

So when you call multipass exec from a shell, the whole command you type is first parsed by the shell you’re in. The result of that parsing is what the multipass client gets in its argv.

For example, if you say multipass exec primary -- ls ~ on a linux shell, that tilde is translated to your local home before the command is passed to multipass. But that is not the case in a PowerShell, because “~” doesn’t have the same meaning there.

Multipass then executes the command in the given instance as if there was no further shell in the middle (I say “as if” because the reality is a little more complicated). This is slightly different from what you’d get with SSH:

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

$ 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'

With SSH, you’d have to quote the whole command.

Note that you can still have a shell in the instance parse your command with multipass exec, you just have to call it explicitly:

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

With that, you get the same behavior regardless of the platform. Without sh -c, it also fails in all plaftorms (although possibly in different ways, depending on whether or not you quote). It makes for a more consistent cross-platform experience.

BTW, -- is only needed so that multipass doesn’t read things starting with dash as its own options.

1 Like

And yes, this should be covered in the documentation…

Done: https://multipass.run/docs/exec-shells

1 Like

Ricardo, thanks. That makes sense, and yep, I should have clarified that I was running a Windows host (though FWIW I tend to work not in powershell but from the good ol’ CMD prompt, or Terminal running that, unless I hit a problem solved better with PS.)

Thanks also for adding that it may be clarified better (even if only briefly) in the docs. It’s easy for us to presume what we offered was passed in rather than being processed first.

Finally, thanks very much for the clarification on --. I’d seen other attempts to clarify it, which were not as satisfying as yours. :slight_smile: