cron jobs are useful if you have a repeated job. For one shot runs, you can use at as well; it takes a time specification, and a list of jobs on STDIN
$ at now+1
your job here
This would run your job one minute from now.
One option would be screen, if it is available. (You mentioned tmux, but not screen)
Another option would be to run the script with "nohup" which will disassociate it from your shell. You would then need to use its pid to monitor it. Redirecting the output to files would also be recommended.
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.
ts will run by default a per user unix task queue. The user can add commands to the queue, watch that queue at ...
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"
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'
A silly "solution" for ...
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-...
In a non-interactive sh, commands run asynchronously with & have their SIGINT and SIGQUIT signals ignored.
That's a POSIX requirement, and goes back to the time when terminal job control (tcsetpgrp()...) didn't exist. Today, it generally gets in the way.
Another POSIX requirement and that comes from the same origin is that when a signal was ignored ...
The easiest thing you can do is to trap the SIGINT (^C) and kill the background process from the handler:
trap 'kill "$!"; exit 1' INT QUIT
zenity --info &
See this and other similar answers for an explanation for this behavior.
When calling your script from some shells like bash which implement the so-called "Wait and Cooperative Exit"...