I have a command that I run every time a new terminal is opened or a new login is made.

This program produces output (colored) which should be positioned before the command prompt. It can take a few seconds to run which will prevent me from using the terminal until then (unless ran in background).

Given than zsh has some advanced ways of redrawing the terminal without clobbering existing text, I would like to know how can I run this command in a way that I don't have to wait for it to finish before I can use the terminal but that once it finishes it prints the output as if it was not backgrounded in the first place.

In practice I would like something that could do:

Command output:
... (running on background)
me@computer: somecommand
me@computer: someothercommand

and once the command finishes I would get:

Command output:
 * Output foo
 * Multiple lines bar
 * and some yada
me@computer: somecommand
me@computer: someothercommand

I tried putting the process in background at start but then it does not display the output cleanly. I get something like:

Command output:
[2] 32207
me@computer: somecommand
me@computer: someother * Output foo
 * Multiple lines bar
 * and some yada
[2]  + done       command
me@computer: someothercommand

So, is this possible? If not with zsh is there any solution out there that could do it?

Any pointers or information is welcome.

  • It's not possible if the program is running directly in the terminal, because there's only one cursor position and zsh and the program would be competing for it. You could do something with zpty: have the command run in a terminal created by zsh, with its output relayed to the real terminal under zsh's control. – Gilles 'SO- stop being evil' May 31 '11 at 21:13
  • @Gilles Could you elaborate on the usage of zpty ? I am aware that you can't have two programs writing to the same terminal without some kind of competition over the cursor. My question comes from the fact that zsh already rewrites the prompt in some configurations. Obviously this program would have to write to some temporary in-memory buffer that would then be "printed" to the specific location once the command finishes. All of the latter under control of zsh. – unode Jun 1 '11 at 16:29
  • @Gilles: maybe this is worth a real answer? – Stéphane Gimenez Feb 15 '12 at 19:13
  • @StéphaneGimenez Sure, it is. I don't know how to do it without some research, and I don't have time for this research right now. – Gilles 'SO- stop being evil' Feb 15 '12 at 19:48
  • An idea would be to do this with screen with a startup script that splits the screen and runs the long runner in the upper part and the normal command line in the lower part. – vasquez Feb 16 '12 at 15:08

This is a simple solution if you're willing to accept output just above the current prompt line.

TRAPUSR1 () { zle -I; unfunction TRAPUSR1 }  # invalidate prompt on signal USR1

bufferout () {
    local buffer
    while read -r line; do                   # buffer lines from stdin
    print -rn -- $terminfo[dl1]              # delete current line
    print -rn -- $terminfo[cr]               # move cursor to BOL
    printf "$buffer"                         # print buffer
    kill -USR1 $$                            # send USR1 when we're done

unsetopt monitor                             # don't monitor this job
./testout |& bufferout & disown              # bg and disown to suppress notification
setopt monitor                               # restore job monitoring

When the job is done, the current prompt and input buffer will be removed and the entirety of the command's stdout and stderr will be printed.

You can get far more fancy than that with the zsh/curses module, but I doubt it would offer an advantage significant enough to merit the effort.

  • This works. But how hard would it be to prevent the shell from printing the process pid when sent to background and the "done" message upon completion? (I mean temporarily deactivate that feature). Also, before putting the output on the terminal, how hard would it be to clear the existing prompt line or lines? – unode Feb 16 '12 at 19:22
  • I think I might have some other prompt rewrite config that is colliding with the deletion of the initial prompt. Mine doesn't go away once the command finishes. I also still get the PIDs of the processes when sent to background but the "done" line is gone. In any case I'm accepting your answer as it's pretty close to what I had in mind. Thanks. – unode Feb 16 '12 at 22:32
  • I'm using xterm and I have the VCS module enabled and some other customizations to the prompt. I haven't tried but I'm quite sure it's related with that. Some time ago I tried using the vi mode notifier similar to this one (stackoverflow.com/a/3791786/125801) and when active I lost some of my custom prompt behavior. It's not a big deal, at the moment I'm happy with the solution. I'll try to cleanup my prompt one of these days and check if the PIDs still show up. – unode Feb 17 '12 at 0:22
  • Zsh-4.3.10 , after disabling pretty much every customization, I now no longer get the prompt (i.e. it's properly removed) but I still get the PIDs of the processes in background, like [1] 27911 27912 in a single line and as the first line on the terminal. – unode Feb 17 '12 at 0:51
  • Can I upvote again? :) It's perfect now! Thanks a bunch. – unode Feb 20 '12 at 19:07

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.