5

Instead of waiting for slow commands to finish, I'd like to run them in the background. However, when they finish running and print to stdout, I get (where represents my cursor) this:

$ slowcmd &
$ cmd_output
█
  • There is no prompt string before my cursor.
  • and slowcmd can be any slow command (For the stuff I've tried, I just said alias slowcmd='sleep 1 && echo cmd_output')

Whereas I'd like something like this:

$ slowcmd && redrawPromptString &
$ cmd_output
$ █

Where after the command's output, a new Prompt String is printed out for me.

What I can do to make redrawPromptString do what I want? I've tried clear, kill $$ to send a ^C to the terminal, and finally printf "^C" (of course that didn't work). I'm running bash. (GNU bash, version 3.2.57).

3
  • My PS1 is colored and uses backslash-escaped special characters - like \@ for the time. (But yeah, that is a suitable workaround for any sane person who doesn't care about getting exactly what he wants...) Ahaha but for real, I hate not being able to do what I want out of ignorance! Out of lack of support in the program I'm using? Sure (for now). But out of ignorance? I don't like that. I will google and stack overflow and ask everyone who I think might now until the cows come home. Commented Sep 4, 2018 at 3:02
  • 1
    Why not to use separate, dedicated for slowcmd terminal? Use terminal multiplexer such as tmux, create new window and run there slowcmd while you can switch back to working terminal and continue your work.
    – Alex
    Commented Sep 4, 2018 at 3:05
  • @Alex I can also open up a new tab in iTerm. I'm not asking how to gain control of my terminal while doing commands that take some time, because I already know how to do that. And if I asked a question like that, I'd get rightfully downvoted. No, I'm not looking for a heuristic, I'm looking for a solution Commented Sep 4, 2018 at 5:28

2 Answers 2

2

Use redraw-current-line function of bind builtin. First check if it's already bound maybe:

bind -q redraw-current-line

I've never seen it bound by default, so you will probably need to bind it. Pick a key combination, let's say Ctrl+Y. Check if it's already taken:

bind -p | grep -F '"\C-y'

Empty output means the combination is unused. If so, let's bind redraw-current-line to it:

bind "\C-y":redraw-current-line

Now, whenever a background process messes with your command line, hit Ctrl+Y. Then your prompt will be redrawn along with whatever command you have just partially typed (if any), so you can continue as if nothing happened.

To make the binding permanent you could add the above command to your ~/.bashrc, but don't. The right approach is to modify ~/.inputrc (for user) or /etc/inputrc (system-wide). This way any program that uses readline(3) library will obey. The line to add to either file looks like this:

"\C-y":redraw-current-line

But if you create ~/.inputrc anew, make sure its first line says $include /etc/inputrc. This is because up to this point readline has used /etc/inputrc and maybe your workflow relies on what's in this file. From now on, the library will use your ~/.inputrc instead; the line $include /etc/inputrc makes it parse the system-wide file as well.

For more info see help bind and man 3 readline.

2
  • I've been using "\C-c", which sends a INTR character according to stty, and causes all input to be discarded and the prompt script to be presented again. But redraw-current-line is exactly what I was looking for! It doesn't lose my current input OR screen history. I don't mean to be greedy, but do you know of any CLI command that can invoke a Readline function? Commented Sep 5, 2018 at 18:10
  • +1 for the detailed explanation on bind, too. It's super super tough to find good documentation on it. Man bash has good stuff under Shell Builtin Commands (if you can find it through all the cruft). For anyone else reading, this link is only about bind, and I found it much more noob friendly. Commented Sep 5, 2018 at 18:14
1

If you press Ctrl+L, it will partially do what you want. It will redraw the current line including everything you typed up to that point including the cursor position, but it will clear the screen, so your previous output is lost (or in case of a terminal window in the scrollback buffer). On the other hand, you were willing to try clear, so maybe that is not a problem.

2
  • Yeah, clear and ^L don't do what I want. I want the command finishing to create a new prompt, so I don't have to manually do anything. There's gotta be a way! I've been using ^C or ^L or just plain <Enter>, they all work. But I'm doing a sub-optimal thing outta ignorance, and I hate that! Commented Sep 4, 2018 at 2:53
  • 1
    You won't get a new prompt, because the prompt has already been printed. But you can use set -b so that the shell will immediately print the ` [1]+ Done` and your cursor will be on a new line. You can also trap the SIGCHLD and use it to print a new prompt, but you would have to find out whether is was a background process that ended.
    – RalfFriedl
    Commented Sep 4, 2018 at 5:39

You must log in to answer this question.

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