I need to modify the output of a program that asks for tty input. When I pipe the output of the program to a utility such as sed however, lines of input don't show.

As a specific if trivial example: I'd like to take the normal Scala input, which includes a REPL prompt:

$ scala
Welcome to Scala 2.12.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions for evaluation. Or try :help.


And change all occurrences of Java to Mocha:

$ scala | sed 's/Java/Mocha/g'
Welcome to Scala 2.12.3 (Mocha HotSpot(TM) 64-Bit Server VM, Mocha 1.8.0_60).
Type in expressions for evaluation. Or try :help.

The problem is that neither the last line (scala>) nor any keyboard input typed is shown until after [Enter] is pressed. I'd like for the second version to behave as the first, only with the sed substitution (and not substitution of keyboard input, of course). Is this possible?

(A portable solution is best, of course, but in case the only solution is shell- or distro-specific, I'd ideally like Zsh and BSD. Thanks.)


Buffering will be a problem, as scala may use block-based buffering on account of the pipe (instead of the default terminal line-based buffering, see setvbuf(3)) and also buffering done by sed (or anything else in the pipeline). You could try slapping stdbuf on everything, toss portability, and pray that those LD_PRELOAD monkey patches work; another option would instead be to run the REPL under a PTY, feed user input to that, and substitute the output before sending it along. This here shows SBCL as scala was delightfully non-functional on my machine.

$ ./mochanichize sbcl 
This is SBCL 1.3.20, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (print "Java")

* (exit)

And the code for mochanichize.

#!/usr/bin/env expect

package require Tcl 8.5

proc mochanichize {fh} {
   global godot
   set somedata [read $fh]
   if {[eof $fh]} { close $fh; set godot 1; return; }
   regsub -all {\mJava\M} $somedata {Mocha} somedata
   puts -nonewline $somedata

proc sendtoprog {from to} {
   # TODO support ^D but that's more complicated
   puts -nonewline $to [read $from]

# no echo on PTY command we're running (soas not to duplicate what is
# echo'd back to the user via the user tty)
set stty_init -echo

if {[catch {spawn -noecho {*}$argv} err]} { puts stderr $err; exit 1 }

chan configure $spawn_id -blocking 0 -buffersize 1
chan event $spawn_id readable [list mochanichize $spawn_id]

chan configure stdin -blocking 0 -buffersize 1
chan configure stdout -blocking 0 -buffersize 1
chan event stdin readable [list sendtoprog stdin $spawn_id]

# TODO better handle ^Z this goes all meh on it

vwait godot

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.