3

Suppose I run a command like this in the terminal:

~$ echo 'sleep 2; echo "hello!"' | sh

then start typing the next line. After two seconds the words "hello!\n" will be inserted into whatever I'm writing. I know there is a workaround to this (pressing up then down which refreshes the prompt), however on other systems that don't have history---eg, using a MUD through telnet---this is not possible.

Does anybody know of an ncurses app or terminal emulator that separates stdin from stdout? This seems pretty easy to make in ncurses, you just have to use some clever dup2s, but before I make it I want to know if someone has done it before.

Any other solutions to the main problem are welcome, as well.

2
  • 1
    There's a famous joke: A man goes to the doctor and says, "It hurts when I do this" (demonstrating some action).  The doctor replies, "Well, then, don't do that!" :-) ⁠ ⁠ ⁠ Jun 10, 2015 at 6:25
  • It occurs to me that I can solve my main problem that playing MUDs with vanilla telnet is excruciating by downloading a cli MUD client. I am going to keep this open for a few days, however, to see if there are any more ideas, because I still have this problem while running a server (that outputs request info) in the background of a terminal I want to keep using Jun 10, 2015 at 7:02

4 Answers 4

6

This isn't as easy as it sounds to change. It's related to the terminal cooked vs. raw mode and whether echo is enabled or not.

When the terminal is in cooked mode (the default), the kernel reads everything that comes in as input and processes it using rudimentary line editing capabilities which include echoing normal text immediately, processing the erase and kill characters which erase a single character and the whole of the current line respectively, and a few other things. Lines of text only actually appear on the terminal's input when you press enter. During the whole time up until you press enter, everything happens entirely inside the kernel and no process running on the terminal receives a single byte, therefore the foreground application doesn't even know that the user is in the process of typing anything. A process running on the tty cannot suppress this echo even if it wants to just because the echo would come at an inopportune time (e.g. intermixed with output) because such a process is not even aware that the input is happening.

You can set the terminal to raw mode instead with no echo to suppress this (stty raw, or with termios), but then you lose the kernel's line editing capabilities completely — which means for example that you cannot correct a typo by pressing Ctrl-u and starting over. More importantly, you will have a lot of trouble using any program that depends on the kernel's cooked processing (basically anything that doesn't use readline or ncurses) because you will be typing completely blind at such programs! Oh, and also: without the terminal cooked processing you lose the kernel's interception of job control shortcuts to interrupt and suspend (by default Ctrl-c and Ctrl-z respectively).

2
  • I'm not so much interested in suppressing output. Instead I'm interested in an application that allows me to type my input in a different location before sending it as input to the terminal, sort of like how an IRC client has a separate field for where you type vs where the messages appear. Jun 10, 2015 at 6:56
  • I see. That isn't a bad idea actually, although I'm concerned that in practice it would mean a weird user experience for many non-readline non-curses applications which don't expect this to happen. But most of my answer stands: because all of this cooked vs. raw processing happens in the kernel I think it will be hard to implement nicely. You'd basically have to create a new kind of device that looks like a PTY but is implemented differently.
    – Celada
    Jun 10, 2015 at 8:00
1

You can include some ANSI escapes in the backgrounded echo to send its output to the top of the screen:

printf %b\\n 'sleep 2; printf "\0337\033[H\033[Khello!\0338"' |sh

Should probably do it. It requires a basically-capable ANSI compatible terminal to work, but if you haven't got one of those then you probably should. Everyone else has been using them since the 80s.

In every case the leading \033 is the octal escape for the literal <ESC> character - such as you might press at the top left of your keyboard. All of the rest of these sequences are interpreted by the terminal on input as cursor addressing commands.

They do these things:

  • 7
    • Save the cursor state for later restoration.
  • [H
    • Home the cursor - move it to the first column in the first row on the screen.
  • [K
    • Erase the text on the current line.
  • 8
    • Restore the last saved cursor state.

The result is that the background process addresses the cursor to the top left of the screen, clears that line, writes out hello!, and then puts your cursor right back where it found it. Much more complicated combinations are possible - and could be used to develop more robust solutions, but that's what I got.

1

The only solution I know is to press ^R. It recovers the inputted text. It's not perfect, but it's better than holding backspace for some time and starting over.

0

For the playing a game over telnet use-case. I used to use a separate terminal for input.

Create input terminal by doing:

xterm -e bash -c "cat > '$(readlink -f /dev/stdin)'"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .