Hacker News new | past | comments | ask | show | jobs | submit login

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.



A super easy way to see this is to type cat, and then type asdf and hit ^D. It immediately echoes back what you typed. Another ^D quits cat, because it gets a 0-byte read that time.


This was cool. Thanks for sharing!


I asked about this on StackOverflow a few years ago, and got a great answer.

http://stackoverflow.com/questions/15666923/sys-stdin-does-n...


...and i remember answering a similar question on SO.

http://stackoverflow.com/a/35537499/319204


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?


I believe your answer is in the article: "Note that modern shells are not good examples of this, because they don't do line-buffered input; to support command line editing, they switch terminal input into an uninterpreted mode. So they get the raw ^D and can do whatever they want with it, and they can let you edit as much of the pending line as they want."


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.


Actually bash (by default) does do something special with ^D: it's mapped to the "delete-the-char-after-the-cursor" functionality (just like the DEL key is), by analog to the delete-char binding in emacs.

But indeed, it's in raw mode so the kernel doesn't do anything special and just emits the 0x04 byte down the descriptor.


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).


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?


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.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: