
What typing ^D does on Unix (2009) - luu
https://utcc.utoronto.ca/~cks/space/blog/unix/TypingEOFEffects
======
telotortium
As a corollary, typing ^D twice on a non-empty line will also send EOF on the
input, allowing you to provide a program with input not ending with \n from
the command line. I knew about this behavior, but not why it worked, but this
article's explanation clarifies this behavior as well. The first ^D terminates
the read() call, passing the line so far, without terminating \n, to the
program. The second causes read() to return with 0 read bytes, which is EOF,
as explained in the article.

~~~
mVChr
I know this article says Unix specifically, but on Linux (Ubuntu) I just tried
typing `ls^D` in bash and got a terminal beep. I tried in zsh and got an
autocomplete as if I'd hit tab. In ksh and fish nothing happened. I'm curious
as to why there are different behaviors here?

~~~
eridius
As the article explained at the end, shells put the terminal into
uninterpreted mode, so ^D doesn't do anything special there. You have to be
using a program (such as cat) that doesn't do this to see ^D's behavior.

~~~
telotortium
Shells that want to implement features like tab completion or more elaborate
line editing than the basics, which only provide for deleting the last
character, word, or line (using Backspace/^W/^U) need to put the terminal into
raw mode when reading the user's command line so they can bypass the basic
line editing that the terminal provides (called "cooked mode").

However, you can write a shell that doesn't do this. In particular, on Linux,
you can experiment in dash. You'll find that the behavior is exactly the same
as this article, since the terminal remains in cooked mode. In particular, if
you type `l^Ds`, that will run `ls`, though you won't be able to delete the
`l` anymore since that was input before the `^D`. Also, if you type `ls^D^D`,
that will log out just like typing `^D` on an empty line does. Other shells
have different behaviors -- for example, Bash seems to take `ls^D` to be the
same as typing `ls` followed by pressing Enter.

Before any shell starts executing a program, however, it puts the terminal
back in cooked mode no matter what mode it's in when at the prompt, since
that's the default terminal mode expected by programs. If you want to
experiment with the cooked mode line editing commands to get the behavior
described in the article, you should run a command that receives standard
input from the terminal (running `cat` by itself should suffice).

~~~
eridius
I didn't realize Dash uses cooked mode. That's actually kind of surprising;
every other shell I've used always uses raw mode to give them more control
over line editing. Is Dash just trying to be as minimalistic as possible?

~~~
telotortium
Dash is really minimalistic. In its default mode, this is the only call to
ioctl it makes on standard input according to strace:

    
    
        ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
    

Notice that it's merely calling TCGETS and that icanon is set, which means the
"canonical input" mode with the default ^W/^U keybindings is enabled. Bash
disables icanon so that it can enable line editing. Interestingly, dash seems
to have vi and emacs editing modes, but they don't seem to cause dash to enter
raw mode, so I'm not sure they do anything.

------
userbinator
No TTY-related discussion is complete without a reference to Linus Akesson's
extremely lucid article on the whole thing, including what all the _other_
interesting Ctrl+ characters do:

[http://www.linusakesson.net/programming/tty/](http://www.linusakesson.net/programming/tty/)

...and the extensive previous HN discussion on it:

[https://news.ycombinator.com/item?id=10631513](https://news.ycombinator.com/item?id=10631513)

[https://news.ycombinator.com/item?id=10064657](https://news.ycombinator.com/item?id=10064657)

[https://news.ycombinator.com/item?id=8094186](https://news.ycombinator.com/item?id=8094186)

[https://news.ycombinator.com/item?id=4544318](https://news.ycombinator.com/item?id=4544318)

[https://news.ycombinator.com/item?id=4062981](https://news.ycombinator.com/item?id=4062981)

[https://news.ycombinator.com/item?id=658155](https://news.ycombinator.com/item?id=658155)

------
gtrubetskoy
^U will clear the buffer - super convenient for retrying botched passwords.

~~~
grive
In readline, ^U will only clear from the cursor to the start of the line,
leaving anything on the right. ^K is the converse.

I'm not sure you are speaking about readline though.

Another very useful thing I use daily is Alt-F/B, to move forward/backward in
the line one word at a time. Control does the same, one letter at a time. D
will delete an element instead.

~~~
Pete_D
There's a lot of unexpected gems in the readline defaults. I recommend people
try paging through "bind -p" in bash some time to see what's there.

Personal favorites are C-r to search back in history and C-x C-e to edit the
current command line in an external editor.

~~~
wruza
It is also very convenient to remap up/down keys to not just list history, but
to do completion based on already typed characters:

    
    
        if [[ $- == *i* ]]
        then
            bind '"\e[A": history-search-backward'
            bind '"\e[B": history-search-forward'
        fi
    

(fix: grammar)

~~~
nitrogen
Debian and Ubuntu typically have a commented-out mapping for this in
/etc/inputrc as pgup/pgdn that I always enable in my ~/.inputrc. Editline
supports something similar in .editrc IIRC.

~~~
wruza
Funny, I became FreeBSD user because default .cshrc contained it (iirc). I
didn't know about shells, readline, etc. back then. Wonder why they do not
enable it on up-down by default, cause I see too many linuxoids around bored
with up-up-up-enter up-up-up-enter to replay history, not even knowing that
completion exists (along with at least ^K ^U ^C).

What's the point to type a half-command and then replace input line with one
from history if I press up? That somewhat resembles ugly cmd.exe, which every
windows user imagines when someone says 'cli'.

------
felixgallo
Typing +++ will bring you back out to your modem's firmware, at which point
you can issue further AT commands

~~~
smlacy
+++ATH0

~~~
nitrogen
How did modems deal with pluses in data? Was there a way to escape the escape
sequence, or was there just an ATSnnn or similar sequence to disable escapes?

~~~
felixgallo
There were a bunch of different mechanisms depending on vendor. Some required
a specific time interval in between the + signs, so that typing very slowly,
or instantly, wouldn't trigger the escape.

Some vendors had bugs in which when +++ appeared _as part of the incoming
data_ it would also trigger the firmware escape.

And of course programmable terminals had hilarious exploits where you'd get
someone to run your program, it would program the terminal to emit a macro
when you hit enter, the macro would include +++ATZ, etc., etc.

My favorite modem story was when I worked for a company that shipped auto
parts warehouse management systems. They would have several SCO unix boxes
with serial port concentrator cards and port concentrators on those
concentrators, such that you'd have 256 modems on a single box and RS-232
cables everywhere. Frequently the operators at the auto parts warehouses
couldn't be bothered to do cable management, so at one facility, the entire
floor of the server closet was carpeted literally 2 feet thick with a random
mess of RS-232 cables, each one leading to a modem. When standing on the
cables killed a modem or two, they would just string a new wire rather than
try to fish out the old one. Kind of like Google's "let it sit in the rack"
policy for failed servers, but writ extremely small and pathologically badly.

~~~
somedangedname
> "let it sit in the rack"

So do they do scheduled collections of failed units? Or just let them sit
there indefinitely?

~~~
pixl97
The entire rack gets replaced with new equipment eventually. Probably get
replaced every few years or so.

For most of the SMB clients I have, I don't build new servers any longer. I
can buy rackmount servers pulled out of a data center that are a few years old
at about 1/3 the price. I just put new disks in them and a new OS.

------
peterwwillis
The author actually clouds what and why is really happening a bit. Ctrl+D
sends the EOF character to the terminal driver. The terminal driver then
determines, based on its settings, what to do next.

In the 'icanon' mode, the terminal driver implements a rustic line editor, so
you can do things like _Backspace_ to delete previous characters and so on.
Pressing _Enter_ during this mode returns the edited buffer line to the
program.

Where Ctrl+D, or EOF, comes in is when you want to return a line to the
program _without_ pressing _Enter_. This is where the terminal driver returns
the buffer to the read() function for the program immediately. Doing it again
with no additional input shows that you were done line-editing, so it simply
returns nothing, signaling the program that you're done editing or providing
input - the intended purpose of End Of File, the character sent by Ctrl+D. (If
you were reading a file and you received nothing on a read... you would be at
the end of a file, because why else would the file have nothing else to read?)

But this 'icanon' mode won't be active in your actual terminal shell, because
shells have their own line editing implementation, so they turn 'icanon' off
by default. You can, of course, turn on or off in the terminal with "stty".
Use "stty -a" to see all the other current settings. The terminal application,
by the way, sets the EOF to the same value as the terminal driver's by
default, in order to prevent confusion.

------
coretx
Directly after reading the title the question "what does ^D do" was fired at a
Old Unix Beard friend of Mine who happens to be here for coffee.

Instant reply Without any latency: "It originally was meant to terminate the
Tape drive"

~~~
kps
[citation needed] I suspect they're misremembering what the T in EOT stands
for.

~~~
brianpan
For the lazy: [https://en.wikipedia.org/wiki/End-of-
Transmission_character](https://en.wikipedia.org/wiki/End-of-
Transmission_character)

------
SteveNuts
Pressing enter then ~. will gracefully shutdown hung ssh sessions

~~~
zingermc
I am amazed by the fact that I never accidentally trigger this feature.

~~~
smhenderson
Hah, yeah I seem to remember Ctrl-S leaving me flustered for quite a while
when I first stumbled on it!

~~~
gonvaled
^-Q is your friend!

------
happyslobro
6 KB HTML. 4 KB CSS with comments preserved. No JS.

It's so beautiful. I think I'm going to cry :)

------
wruza
In general, CTRL-anything send corresponding control character by shifting
keys to control set of ASCII table [1] (shift-key shifts keys to upper set).
Many of them retained original meaning, e.g. ^I ^M ^J ^H ^D ^[.

[1] [http://ascii-table.com/](http://ascii-table.com/)

------
TorKlingberg
I wish articles like this would say what ^D means. I remember when I was new
to Unix it took me a while to realize that I wasn't supposed to type a literal
caret (^) followed by a D.

For reference it means Ctrl+D, unless you you have a strange keyboard from Sun
or something.

~~~
zeveb
^D, Ctrl+D and C-d are all different ways of saying the same thing. There's
nothing particularly neutral about Ctrl+D, any more than there is about using
the word 'water' to mean the substance of which the oceans are mostly
comprised — it's just another dialect's term.

~~~
zippergz
The button on the keyboard literally says "control" or "ctrl" on it. How could
that possibly not be easier to figure out than "^" or "C-"?

~~~
JdeBP
Look at the confusion that surrounds "Strg" for this very same key.

Also note that on some keyboards it has the U+2638 character engraved.

------
voidz
Expanding on this, ^\ (Ctrl+\\) means sigkill, and I don't think many people
know about this one.

~~~
verbatim
^\ is actually sigquit, which is a bit different from sigkill, since programs
can catch QUIT and perform some cleanup.

~~~
semi-extrinsic
Also ^| (as in, "control pipe") which is a synonym for ^\\. At least for me, |
is faster to hit than \

And since we're on this topic: if I write a for loop in bash that runs a slow
command 1000 times (ImageMagick comes to mind), and I realise something has
gone wrong, is there an easy way of breaking the outer loop?

~~~
dragonwriter
> Also ^| (as in, "control pipe") which is a synonym for ^\\. At least for me,
> | is faster to hit than \

How? Do you have a keyboard where | isn't Shift+\?

~~~
semi-extrinsic
I have a Norwegian keyboard, where as you can see, pipe has its own key to the
left of "1" (above Tab):

[https://en.m.wikipedia.org/wiki/File:Keyboard_Layout_Norwegi...](https://en.m.wikipedia.org/wiki/File:Keyboard_Layout_Norwegian.png)

------
yread
So many useful (and utterly undiscoverable) tidbits of knowledge in this
thread! Thanks!

------
Nilzor
This comment thread is worth much more than the actual article

------
VintageCool
Neat. So when my terminal is spewing output because I ran "cat massivefile"
instead of "cat massivefile | head", I can hit ctrl-D to stop it instead of
repeatedly hitting ctrl-C and despairing.

~~~
rfw1z
No, thats what ctrl-s and ctrl-q are for.

Here's the rest in one place:

    
    
                           eof          VEOF         EOF character
                           eol          VEOL         EOL character
                           eol2         VEOL2        EOL2 character
                           erase        VERASE       ERASE character
                           erase2       VERASE2      ERASE2 character
                           werase       VWERASE      WERASE character
                           intr         VINTR        INTR character
                           kill         VKILL        KILL character
                           quit         VQUIT        QUIT character
                           susp         VSUSP        SUSP character
                           start        VSTART       START character
                           stop         VSTOP        STOP character
                           dsusp        VDSUSP       DSUSP character
                           lnext        VLNEXT       LNEXT character
                           reprint      VREPRINT     REPRINT character
                           status       VSTATUS      STATUS character
    

cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>; eol2 = <undef>;
erase = ^?; intr = ^C; kill = ^U; lnext = ^V; min = 1; quit = ^\; reprint =
^R; start = ^Q; status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;

~~~
spudlyo
Back in the day, using a slow modem or teletype, discard (flush output) was
super important, but several years back when I hooked up an actual Wyse dumb
terminal to a Linux box I was dismayed to discover that it's actually not
implemented in Linux.

------
pantulis
If memory serves well, there was also the amazing Ctrl-O, which allowed you to
send control sequences over a busy terminal --for example when scrolling a lot
of text, seems to be mapped to the arcane VDISCARD and unsupported by Posix &
Linux.

------
djsumdog
huh. That's really interesting. I assume ^L is handled in the tty driver as
well?

~~~
kazinator
An important upshot is that TTY I/O never actually ends; EOF from a TTY is a
temporary condition, and input can continue.

In the C standard library, you just clearerr(stdin) and keep reading.

If a program takes two files as input and obeys the - convention for
specifying stdin (or has some other way of opening the tty more than once),
you can do:

    
    
      $ program - -
    

then give it two inputs, each followed by [Ctrl-D][Enter]. For instance "cat -
-".

This sort of thing subtly breaks with some utilities. Try coaxing GNU diff to
compare two pieces of tty input, haha.

~~~
philsnow
You can work around this with other shell trickery. This might work in your
shell:

    
    
        $ program <(cat) <(cat)
    

The program itself isn't involved in opening the TTY / reading from it, etc,
that is all handled by your shell before fork/execing program. program doesn't
know that any of these shenanigans have happened.

It probably uses more memory (the shell has to have both stdin's in memory
before it can exec the program), but overall I have mostly abandoned trying to
use '-' as an input because of the way some tools, perhaps less-steeped in
unix-y traditions, don't honor it correctly.

