lima-vm / lima

Linux virtual machines, with a focus on running containers
https://lima-vm.io/
Apache License 2.0
15.24k stars 600 forks source link

arguments of shell command doesn't support "raw" #251

Open robberphex opened 3 years ago

robberphex commented 3 years ago

limactl shell command actually take two responsibility:

  1. : lima act like ssh,

    # expected to get home dir in lima guest vm
    $ limactl shell default -- echo '$HOME'
    /home/robert.linux

    Case 1 is right.

  2. : lima act like command tunnel, in following example, as tunnel to pass arguments to nerdctl.

    
    # expected to get home path in container
    $ alias docker='limactl shell default -- nerdctl'
    docker run -it --rm alpine:latest sh -x -c 'echo $HOME'
    + echo
Case 2, **the behavior of limactl is not expected**.
Comparing with docker for desktop:
```shell
$ /usr/local/bin/docker run -it --rm alpine:latest sh -x -c 'echo $HOME'
+ echo /root
/root

I propose to add --raw flag to make limactl support act like command tunnel, which means what you pass to limactl, what inner command get.

jandubois commented 3 years ago

I propose to add --raw flag to make limactl support act like command tunnel,

Yes, I've struggled with this use case before too!

However, I think I would prefer to use a different command, e.g. limactl run instead of limactl shell --raw to provide the different semantics. Let's wait to hear what @AkihiroSuda says!

AkihiroSuda commented 3 years ago

I propose to add --raw flag to make limactl support act like command tunnel,

Yes, I've struggled with this use case before too!

However, I think I would prefer to use a different command, e.g. limactl run instead of limactl shell --raw to provide the different semantics. Let's wait to hear what @AkihiroSuda says!

I don't think I can remember the differences between run and shell 😅 , and I'd rather prefer to reserve run for something similar to start. So limactl shell --raw (or maybe limactl shell --escape, or limactl shell --verbatim) SGTM, but if it has to be a new subcommand (why?) it should be like limactl shell-raw / limactl shell-escape / limactl shell-verbatim.

jandubois commented 3 years ago

I think I'm mostly thrown by the name raw, which to me means kind of the opposite of what it does here, namely wrapping each argument in an extra set of quotes before joining all the arguments by a space. "Raw" to me means un-processed, not wrapped/formatted.

A descriptive name (instead of --raw) would be --auto-quoted, but I admit that it is a bit unwieldy, so --quoted seems like a reasonable alternative.

But effectively we want to be able to say: "Run this command on the guest, but do not do any shell processing of the arguments." From this I gather that shell is the wrong verb because our intend is run, without any processing by the shell. So if you insist that the command has to start with shell- then shell-but-no-shell sounds descriptive (not a serious suggestion). 😄

I find run much more intuitive than shell-verbatim or shell --quoted.

Using shell with a qualifier to say we are disabling the shell processing is a description of the implementation. Using run or exec matches the intention of what we expect the command to do. The user doesn't care that it is implemented via ssh internally.

It is the same reasoning why we have limactl copy, with an alias of cp, but not scp because scp is an implementation detail and has nothing to do with what the user wants to do.

As an alternative to run I could also go with exec. It also implies (to me) that the arguments will not be evaluated by a shell before being passed to the process.

So my order of preference would be:

limactl run              (best)
limactl exec             (also ok)
limactl shell --quoted   (grudgingly ok; looks busy)
limactl shell --verbatim (already feels somewhat wrong to me)

Side note about run being a variant of start: I think eventually we should have a complex create verb to create new instances, and use start just as the opposite of stop. But that is a discussion for a different issue (and a different time).

Update I think exec has a stronger association of not evaluating arguments (to me), so I think it is my top choice, followed by run.

jandubois commented 3 years ago

If you are confused about the difference between exec and sh, think of the following, and all will be clear: 😄

$ cd /var

# limactl exec
$ (exec 'ls' '*')
ls: *: No such file or directory

# limactl shell
$ (sh '-c' 'ls' '*')
agentx   db       jabberd  mail     root     select   yp
at       empty    lib      msgs     rpc      spool
audit    folders  log      netboot  run      tmp
backups  install  ma       networkd rwho     vm
AkihiroSuda commented 3 years ago

exec SGTM, as long as we can have good --help texts to explain the difference

@RobberPhex WDYT?

jandubois commented 3 years ago

as long as we can have good --help texts to explain the difference


limactl shell runs the command inside a shell on the INSTANCE, so all arguments are first evaluated by the local shell, and then again by the remote shell:

$ limactl shell INSTANCE echo $HOME
/User/username
$ limactl shell INSTANCE echo '$HOME'
/home/username.linux

Use the limactl exec command if you don't want the arguments to be evaluated again inside the INSTANCE:

$ limactl exec INSTANCE echo '$HOME'
$HOME

And correspondingly for limactl exec, which will have its own help, being a separate command.

dhinus commented 6 months ago

A note for other people who need to print the value of the $HOME variable inside the VM: wrapping the echo in sh -c gives me the result I want:

limactl shell default echo $HOME         # prints '/Users/username'
limactl shell default echo '$HOME'       # prints '$HOME'
limactl shell default sh -c 'echo $HOME' # prints /home/username.linux
jandubois commented 6 months ago

While wrapping a command in a shell inside the VM is the generic solutions to this problem, in the special case of just querying an environment variable you can use of the printenv command:

limactl shell default printenv HOME

And Lima includes the lima wrapper to run a shell in your default instance, so it can be just

lima printenv HOME
dhinus commented 6 months ago

@jandubois thanks, that's very useful! I didn't remember the existence of printenv. :)