erlang / otp

Erlang/OTP
http://erlang.org
Apache License 2.0
11.43k stars 2.96k forks source link

ERL-331: Erlang shell should handle EOF signal (Ctrl+D) #4414

Open OTP-Maintainer opened 7 years ago

OTP-Maintainer commented 7 years ago

Original reporter: eproxus Affected versions: OTP-19.1.1, OTP-19.2, OTP-20.0 Component: stdlib Migrated from: https://bugs.erlang.org/browse/ERL-331


The Erlang shell should handle the EOF signal sent by pressing Ctrl+D in a terminal. Currently there are two short ways to exit the Erlang shell:

* Typing {{q().}} (which will trigger the init system to quit, short for {{init:stop()}})
* Pressing Ctrl+C which will kill the Erlang emulator

The first doing the right thing, but is unintuitive (because it is Erlang only). The second is intuitive (after trying Ctrl+D) but does the "wrong" thing for the common use case. When Ctrl+C is used, the init system is not properly stopped including teardown of applications, but is instead instantly killing the VM. This seems to be the current recommendation in the Erlang and Elixir community.

It would also lessen confusion when using a remote shell, since detaching feels a lot less destructive that killing the VM. Today a user has to ask herself if Ctrl+C will kill the attached node, or the node it is attached to? The answer is almost never straightforward, and it also depends on the method of connection. The Erlang shell also prints out the text {{(abort with ^G)}} which doesn't do what a normal user expects it to, but instead opens up another prompt with an even less intuitive interface.

Ctrl+D is supported by almost all common language shells (including Python, Ruby, Node.js, Lisp, Clojure etc.). Not supporting it in Erlang makes Erlang look weird and dated.
OTP-Maintainer commented 7 years ago

egil said:

This was discussed here: https://github.com/erlang/otp/pull/983 and rejected.

Perhaps we should revisit it.
OTP-Maintainer commented 7 years ago

eproxus said:

The argument about accidents discussed in https://github.com/erlang/otp/pull/983 applies equally to Ctrl+C, which is becoming the de-facto "standard" way of closing the shell, as far as the community is concerned. At least Ctrl+D should do the intuitive thing in all attached shells (i.e. disconnect the shell). It's only if you are in the root shell that it would close the VM (gracefully, mind you) but Ctrl+C does that as well today (and not even gracefully).
OTP-Maintainer commented 7 years ago

eproxus said:

And learning the "proper" way via {{q().}} or {{init:stop().}} is even worse, because that would definitely kill the root VM and not the attached console.
OTP-Maintainer commented 7 years ago

essen said:

If production accidents are a concern, perhaps the only thing that's needed is an option to disable shutting down the VM through keyboard shortcuts entirely. You'd enable it in production, to be safe, if needed, and keep the expected behavior otherwise.
OTP-Maintainer commented 7 years ago

ddharry said:

Hi, May i suggest?
When opening an Erlang shell, we have {{(abort with ^G)}} written. Hence, this should be enough to quit by controlling at the same time your jobs and shells. We could access the help menu from the start.

Clearly, once you type {{^G}}, why not displaying directly the help menu to let you choose how to quit :
{quote}
User switch command
 -->
  c [nn]            - connect to job
  i [nn]            - interrupt job
  k [nn]            - kill job
  j                 - list all jobs
  s [shell]         - start local shell
  r [node [shell]]  - start remote shell
  q                 - quit erlang
  ? | h             - this message
 -->
{quote}

So you know how to control your exit, gracefully or not, by considering killing your remote nodes, jobs ...etc. And that seems to be more Erlanguish?
OTP-Maintainer commented 7 years ago

eproxus said:

@DDHarry: I agree that the ^G menu is a bit obtuse, but I think that should be a separate issue (i.e. showing help by default when entering the menu)
OTP-Maintainer commented 6 years ago

kelvinst said:

Well, this is something that would be a really nice change to the language. I really agree with @eproxus in his arguments to use Ctrl-D:

- `q()` or `init:stop()` are counter intuitive
- Ctrl-D is a standard everywhere, not having it makes Erlang (and therefore Elixir) weird and dated
- The use of Ctrl-D on shell attached to existing nodes can also apply to Ctrl-C, that's becoming a standard for Erlang and Elixir

And the solution:

- Ctrl-D would still only disconnect the shell on attached nodes
- Ctrl-D would close the VM gracefully whenever on the root shell

I do not know if there is a way to identify that, but if you don't mind helping me to find that, I would gladly open up a PR with these changes.
OTP-Maintainer commented 5 years ago

josevalim said:

For completeness, there are three scenarios to consider:

1. The node is started with the shell (erl)
2. A remote shell (erl -remsh)
3. A shell attached to a live node (to_erl/run_erl)

Ctrl+C kills the node on 1, it **does not kill** the remote shell on 2, but it does **kill the live node** on 3.

As Adam said, almost every one today grows into a habit of using `q()` or Ctrl+C to shutdown a node, and `q()` will never do what you want when connected to a live system and Ctrl+C is a poor choice on a `to_erl` shell.

IMO, adding support for Ctrl+D is a great opportunity to have something that always disconnects, working safely on both 2 and 3. It would arguably be the safest command to shutdown a live system.

If accidental shutdowns are really a concern (do people even use the direct shell nowadays on a production system?), maybe it could show a warning saying "Shut down live node? [Y/n] " for case 1 above? This means shutdown is Ctrl+D then Enter on regular nodes, and only Ctrl+D when connected.
OTP-Maintainer commented 5 years ago

lelf said:

{quote}As Adam said, almost every one today grows into a habit of using `q()` or Ctrl+C,{quote}

Not really.  {{^g q}}

Also, {{^c}} is (almost) always wrong in normal situations.
OTP-Maintainer commented 5 years ago

dogweather said:

What are the semantics of `ctrl-\`? 

I've hacked my development environment to remap `ctrl-d` to `ctrl-\`, which exits immediately.
OTP-Maintainer commented 5 years ago

josevalim said:

The same as pressing `Ctrl+C` twice I outlined in the previous comment.
OTP-Maintainer commented 5 years ago

josevalim said:

@Antonio Nikishaev, pressing `^g+q` has the same issues as I mentioned on Ctrl+C. It works for regular and remote nodes but not for the attached `run_erl`/`to_erl` shell. So in this sense, it has the same impact as Ctrl+C (unless I am missing something).
OTP-Maintainer commented 5 years ago

dogweather said:

What about `ctrl-l` as a synonym for `clear<enter>`? Is that uncontroversial? It'd be a step towards better compatibility.
OTP-Maintainer commented 5 years ago

dogweather said:

Can we agree that ctrl-d on an empty line should simply be an alias to the safest exit method: Ctrl+g, q?
tobia commented 3 years ago

I agree, as a new user I baffled when erl did not handle Ctrl-D on an empty line, because it is a standard way of quitting interpreters or repl applications.

It should be an alias for the recommended way to exit the VM.

wojtekmach commented 1 year ago

Another thing to maybe consider is for ctrl+d to signal EOF for functions like io:get_line. Then code such as this could finish:

1> Fun = fun F(eof) -> eof ; F(Line) -> io:put_chars(Line), F(io:get_line(">>> ")) end.
2> Fun("").

This would be similar to for example:

# $ python3 --version
# Python 3.11.5
>>> import sys
>>> sys.stdin.readlines()
foo
bar
# ctrl+d
['foo\n', 'bar\n']
# ruby -v
# ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin20]
irb(main):004:0> $stdin.read
foo
bar
# ctrl+d
=> "foo\nbar\n"