Reuse the `apt up` part of the `apt update` and `apt upgrade` commands to execute both in sequence in just one line

How to reuse the apt up part of the apt update and apt upgrade commands to execute both commands in sequence in just one line without an alias.

Something like: apt up{date,grade}.


Solution 1:

The simple way ( and more efficient) is via for loop (on one line but here it's shown in multiple lines for clarity):

for i in {date,grade}; do
     apt up${i}
done

The smart way is to make an alias in ~/.bashrc for these two commands and forget about retyping it ever again:

alias upgrade=' sudo apt update && sudo apt upgrade'

The elaborate way is to do this:

$ echo sudo" "apt" "up{"date","grade"}" && " :
sudo apt update &&  sudo apt upgrade &&  :

See what's happening ? We're building a valid string of text to be run by shell. Now all you need to do is to use eval instead of echo. But eval is generally not recommended in shell scripting and this command is much more convoluted and non portable than the for loop. Variation on the theme without eval (and shortest so far, but not efficient due to pipe and multiple commands echo,two apt,sudo and sh so lots of forking):

$ echo "apt "{update,upgrade}";"|sudo sh

We can also use set to turn the desired sequence into positional parameters and just execute apt with $1 and $2 args:

$ set {update,upgrade}; echo apt $1 && echo apt $2
apt update
apt upgrade

Or alternatively use for loop again, since it defaults to processing positional parameters if you don't specify a sequence in the for variable (sequence)

$ set {update,upgrade} ; for i; do echo apt $i ;done
apt update
apt upgrade

Using set is a very common technique I see among professionals who work with shells a lot, and for a good reason - this is very portable and will work in /bin/sh where you have no arrays or {a,b,c} expansion. So POSIX-ly portable way would be set update upgrade ; for i; do echo apt $i ;done. Additionally, this should be efficient.


Or we can go the while loop route:

$ echo {date,$'\n',grade}|while read i;do echo apt up${i}; done 
apt update
apt upgrade

Of course remember to run it without echo for actual command execution to take place

Solution 2:

You can also use bash history expansion to get parts of the current command line and modify it:

$ apt update; !#:s/date/grade
apt update; apt upgrade;
Reading package lists... Done
...
  • !# is the current line
  • :s/date/grade is a modifier that substitutes date with grade

Solution 3:

Another simple option using xargs instead of for:

echo up{dat,grad}e | xargs -n1 apt

Test:

$ echo up{dat,grad}e | xargs -n1 echo apt
apt update
apt upgrade

However, I’d prefer a shell alias just like Sergiy because these “simple” ways are not simple enough to be actually useful.

Solution 4:

Create a function like this:

repeat() { 
    for suffix in "${@:2}";
    do
        eval "$1""$suffix";
    done
}

Then you can run repeat 'apt up' {date,grade}. This has the benefit of being reusable for totally different commands, although it's not saving you any typing for this one.

I'd certainly agree with some of the other answers that something like this is probably more useful:

alias apt-up='apt update && apt upgrade'