4

This question already has an answer here:

Without knowing ahead of time that I'll want to do this, is it possible to wait for a job to finish and automatically begin executing another one?

For example, I jump into a tmux session and execute sudo apt update && sudo apt upgrade. Then I create a new pane and begin doing something, only to realize I need another package on my system I don't currently have. I have to wait for my update/upgrade process to finish, but it's chugging away and I don't want to have to wait for it, especially since I could be doing something else while it's going, rather than having to wait for it.

I'd be fine with ^Z and bg-ing the job, but my question is, is there a way to insert a job after that job is finished, so that I can && sudo apt install tree for example?

EDIT: this question is different from the linked question and answer, I think, because job1 && job2 ^Z fg; echo "done" will see echo "done" executed after job1 finishes: job2 apparently gets discarded. The way around it is to use a semicolon between job1 and job2, but that's not a great solution for me.

marked as duplicate by Michael Homer, Rui F Ribeiro, jordanm, Prvt_Yadv, Christopher May 2 at 22:17

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 1
    FWIW, you can always stop the output of the command1 with ^S, blindly enter command2 followed by a newline, and then press ^Q. When command1 will finish, command2 will be executed. I'm not expecting you to like this solution ;-) – mosvy Apr 25 at 23:36
  • 1
    @mosvy only if command1 won’t read its input any more ;-). – Stephen Kitt Apr 26 at 7:57
1

In general, there is no better solution than that given in Basic job control: stop a job, add a job onto the stack, and `fg`, but as you point out, that fails when used with && (because the command placed in the background “exits” with a non-zero status code), and won’t always have the desired effect with compound commands anyway (the currently-running command is placed in the background, and following commands are run immediately).

For this particular use-case, there is a solution — you can use Diomidis Spinellis’ lockf.py to create a queue of commands waiting for the current apt command to finish:

lockf /var/lib/apt/lock apt install tree

This can be generalised to any command whose completion is externally observable in a predictable manner.

The queue is unordered, and can cause issues — if you run

sudo apt update && sudo apt upgrade

and queue an installation while apt update is running, you might end up with apt install running after apt update, and apt upgrade failing because it can’t get the lock.

  • I don't think that that python script does anything beyond the flock(1) utility from linux (except for opening the file in write mode, why?) or that the flock(2) system call guarantees any order in which the waiting processes will be unlocked. – mosvy Apr 26 at 11:01
  • The difference between lockf.py and flock(1) is that the former uses fcntl(2) via lockf(3) (hence the name, and the requirement to open the file in write mode); apt itself uses fcntl. But yes, the queue is unordered. – Stephen Kitt Apr 26 at 11:03
  • sorry, missed that. – mosvy Apr 26 at 11:08
1

Using the flock(1) utility from Linux (or the lockf.py script from the other answer, which is similar in this respect) does not guarantee that the "queued" commands will be run in the order in which they were submitted.

$ sh -c 'for i in 1 2 3 4 5 6 7 8 9; do sleep .1; flock / sh -c "echo $i; sleep .3" & done; wait'
1
2
3
5
4
...

A silly "solution" for creating a job queue could be something like:

$ (echo > jobq; tail -f jobq | sh) &
[2] 6641
$ echo echo 1 >> jobq
1
$ echo echo 2 >> jobq
$ 2
etc
1

You should use the task-spooler (tsp is not ts) to execute a multiples apt commands:

tsp apt command 1 -y

Then use -d option to add second apt command to the queue list:

tsp -d apt command 2 -y

use tsp to check the stat of the list.

man tsp:

ts will run by default a per user unix task queue. The user can add commands to the queue, watch that queue at any moment, and look at the task results (actually, standard output and exit error).

-d

Run the command only if the command before finished well (errorlevel = 0). This new task enqueued depends on the result of the previous command. If the task is not run, it is considered as failed for further dependencies.

  • 1
    Wouldn't this require me to know ahead of time that I'll want to insert a job into the queue? – Harv Apr 26 at 22:06
0

If you get into the habit of always doing your update/upgrade is braces,

( sudo apt update && sudo apt upgrade )

then job control correctly suspends the sub-process allowing you to type

fg ; echo "Example new command"
  • Hmm. Yeah, but there's no reason for me to do that unless I know I'm going to want to execute another command afterwards, in which case I wouldn't need to do this. That's why in my question, I mentioned not foreseeing needing to append a command at the outset. Also, I can't generalize this solution, since I'd just end up habitually running all of my commands in subshells on the off chance I want to append something. I appreciate you taking the time to contribute though, thank you. – Harv Apr 27 at 8:50

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