You are not logged in.
The purpose of this script is to streamline the process of creating a system (not user data) backup then performing pacman update.
Features;
- Providing automatic shutdown option upon completion
- Keeping a human-readable log including application version changes (shows old version -> new version which is a bit extra from the /var/log/pacman.log)
- Automatic removal of orphaned packages
- Automatic cleaning of pacman package cache
This is a work in progress and I am open to suggestions on features or how to improve my code (as I know the code is not exactly optimised).
I am aware that there are shorter ways to perform logic funtions such as [ test ] && do_this_if_true || do_this_if_false for example, but I have done certain functions in long-hand to improve readability.
Anyway, here is the script;
#!/usr/bin/env bash
#
# Possibly integrate curl -o /etc/pacman.d/mirrorlist https://archlinux.org/mirrorlist/all/
# for auto updating mirrorlists
# https://archlinux.org/mirrorlist/?country=AU&protocol=https&ip_version=4
#
# Detect Linux kernel update and suggest reboot
#
# pacup - System update script by Ewan Cox.
# Version:
pacup_ver="* pacup version 1.10 *"
#
# This script will help to automate updating in your Arch based system. It supports automatic shutdown on completion, logging of package changes, error logging,
# automatic timeshift backups and support for AUR updating (using -a flag)
#
# Copy this script to your /usr/local/bin directory and use script with "sudo pacup" from your terminal CLI
#
# User definable:
logfile=/home/$SUDO_USER/logs/pacup.log
timeshiftgit="https://aur.archlinux.org/timeshift-bin.git"
# Declare variables:
aurpkgnum=$(pacman -Qm | wc -l)
orpkgnum=$(pacman -Qtdq | wc -w)
helper="unknown"
pkglist=""
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
nc='\033[0m' # No colour
unknownflag=0
helpflag=0
shutcomp=0
pkgup=0
aurupdate=0
rmorpflag=0
aurflag=0
shutflag=0
rmcache=0
# Declare Functions:
teelog(){
tee -a $logfile
}
blank() {
echo | teelog
}
ins_sep(){
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | teelog
}
timestamp() {
echo $(date) | teelog
}
completion_stamp() {
echo -ne ${yellow}
echo -n "${pacup_ver} script completed at: " | teelog
echo -ne ${nc}
timestamp
ins_sep
}
help_message() {
echo
echo -e "${yellow}Usage: pacup [options]${nc}"
echo "If multiple options are specified, the first option will take precedence and may exit the script before other options take effect."
echo "Eg. If options -h was combined with another option, any other options will have no effect as this help message will be printed and then exit."
echo
echo "Available options:"
echo
echo "-h = Display this help"
echo "-s = Request shutdown on completion of script"
echo "-a = Run AUR helper after completion (autodetect - yay/paru) - USE WITH CAUTION"
echo "-o = Check for and remove orphaned packages only and then quit (this is automatically done at the end of the script)"
echo "-r = Run paccache clean after completion"
echo
exit 0
}
check_upgradable() {
pacman -Sy
pkgup=$(pacman -Qu | wc -l)
if [ $pkgup -eq 0 ]; then
echo -e "${yellow}There are no updates available at this time. Nothing to do.${nc}" | teelog
completion_stamp
blank
exit 0
fi
echo
echo -n "There are " | teelog
echo -ne ${green}
echo -n $pkgup | teelog
echo -ne ${nc}
echo " packages that can be upgraded using pacman." | teelog
blank
sleep 1
pkglist=$(pacman -Qu)
echo "Packages with update available:" | teelog
echo -e ${yellow}
echo -e "$pkglist"
echo -ne ${nc}
sleep 1
echo "$pkglist" >> $logfile
blank
}
check_root(){
echo
if [ $EUID -ne 0 ]; then
echo -e "${red}Root access required, please re-run this script with sudo.${nc}"
echo
exit 101
else
echo -e "Root access detected... [${green}ok${nc}]"
echo
sleep 1
fi
}
check_timeshift(){
if ! command -v timeshift &> /dev/null; then
echo -e "${red}[ timeshift ] has not been detected!${nc}"
echo "This script relies on the package timeshift being installed on the system."
echo
echo -e "${yellow}Would you like to install timeshift from the AUR now? [y/N]${nc} "; read inst
case $inst in
"y")
echo
echo "Attempting to install timeshift from the AUR now..."
echo
install_ts;;
*)
echo "Please manually install timeshift and re-run this script."
blank
echo -ne ${red}
echo -n "Script cannot continue and will now exit." | teelog
echo -e ${nc}
exit 1
esac
else
echo -e "Package [ timeshift ] detected... [${green}ok${nc}]"
sleep 1
fi
}
install_ts() {
if ! command -v git &> /dev/null; then
echo -e "${red}Command [ git ] not found, attempting to install git using pacman...${nc}"
pacman -Sy git || exit 102
else
echo -e "${green}Command [ git ] found.${nc}"
fi
echo
if [ -e /home/$SUDO_USER/git/timeshift ] || [ -e /home/$SUDO_USER/git/timeshift-bin ]; then
echo "Found old [ timeshift ] git directory, removing..."
rm -rf /home/$SUDO_USER/git/{timeshift,timeshift-bin} &> /dev/null
else
echo "No previous [ timeshift ] git directory found."
fi
echo
echo "Creating directory for [ git ] if it doesn't exist..."
su $SUDO_USER <<EOF1
mkdir -p ~/git
EOF1
echo
echo "Cloning [ timeshift ] from git to local machine..."
echo
su $SUDO_USER <<EOF2
cd ~/git
git clone $timeshiftgit
EOF2
echo
echo "Installing git package [ timeshift ]..."
echo
echo "Switching out of sudo to run [ makepkg ] as user: $SUDO_USER"
su $SUDO_USER <<EOF3
cd ~/git/timeshift-bin
makepkg -si
EOF3
}
pac_orphans(){
echo "Checking for orphaned packages... " | teelog
echo -ne ${yellow}
echo -n "$orpkgnum orphaned packages have been found." | teelog
echo -e ${nc}
sleep 1
blank
if [ $orpkgnum -gt 0 ]; then
echo " Removing orphaned packages... " | teelog
if PAC_OR="$(pacman -Qtdq | pacman -Rns --noconfirm - )"; then
echo $PAC_OR | teelog
echo -e "${green}Done.${nc}"
echo
else
echo -ne ${red}
echo -n " Error removing orphans." | teelog
echo -ne ${nc}
echo " Check log: $logfile" | teelog
echo
exit 1
fi
else
echo -e "${green} No orphans found, nothing to do.${nc}"
blank
fi
}
shut_req(){
echo " ---> Shutdown requested on completion." | teelog
sleep 2
blank
shutcomp=1
}
do_shut(){
blank
echo -ne ${yellow}
echo -n "Shutdown requested. Sending shutdown request to system now." | teelog
echo -e ${nc}
shutdown +1 | teelog
}
aur_req(){
echo -en ${red}
echo -n "AUR helper auto update requested - Use with caution!" | teelog
echo -e ${nc}
blank
echo -ne "${yellow}Are you sure you want to update ALL of your AUR packages? [y/N] ${nc}"; read aurcheck
if [[ $aurcheck = "y" ]] || [[ $aurcheck = "Y" ]]; then
echo " Auto updating of AUR packages with AUR helper confirmed by user." | teelog
blank
echo " Checking which helper is in installed..."
if command -v paru &> /dev/null; then
echo -ne ${yellow}
echo -n " Helper [ paru ] detected, using that." | teelog
echo -e ${nc}
helper="paru"
aurupdate=1
blank
elif command -v yay &> /dev/null; then
echo -ne ${yellow}
echo -n " Helper [ yay ] detected, using that." | teelog
echo -e ${nc}
helper="yay"
aurupdate=1
blank
else
echo -ne ${yellow}
echo -n " AUR Helper could not be detected. Please re-run script without -a option or install [ paru ] or [ yay ]." | teelog
echo -n " Alternatively, you can simply update your AUR packages manually with [ makepkg -si ] after downloading the updated files."
echo -e ${nc}
echo
exit 2
fi
elif [[ $aurcheck = "n" ]] || [[ $aurcheck = "N" ]] || [[ -z $aurcheck ]]; then
echo " AUR option selected but cancelled by user." | teelog
blank
else
echo " AUR option selected but invalid answer given! Skipping AUR updates." | teelog
blank
fi
}
rmorp_req(){
ins_sep
echo
echo -en ${yellow}
echo -n "Flag [ -o ] used for removing of orphaned packages only" | teelog
echo -e ${nc}
pac_orphans
if [ $shutcomp = 1 ]; then
do_shut
fi
exit 0
}
cli_opts(){
case $option in
h) helpflag=1 ;;
o) rmorpflag=1 ;;
a) aurflag=1 ;;
s) shutflag=1 ;;
r) rmcache=1 ;;
?) unknownflag=1 ;;
esac
}
main_script(){
blank
ins_sep
echo -ne ${yellow}
echo -n "${pacup_ver} System update script initiated at: " | teelog
echo -ne ${nc}
timestamp
sleep 1
blank
# Call function to check that there is actually work to do
check_upgradable
echo -n "Creating new snapshot with [ " | teelog
echo -ne ${yellow}
echo -n "timeshift" | teelog
echo -ne ${nc}
echo " ]..." | teelog
blank
# Proceed if timeshift succeeds, otherwise exit with error code
if TIM_A="$(timeshift --create --quiet --scripted --comments pacup-script-update 2>&1)"; then
echo "$TIM_A" | teelog
blank
echo -n "Timeshift backup... [" | teelog
echo -ne ${green}
echo -n "ok" | teelog
echo -ne ${nc}
echo "]" | teelog
sleep 1
blank
else
echo -e "Timeshift backup...[${red}failed${nc}]"
echo $TIM_A | teelog
echo "An error occurred when running [ timeshift ], consult log file for details." | teelog
echo "Logfile located at: $logfile"
blank
completion_stamp
if [ $shutcomp = 1 ]; then
do_shut
fi
exit 3
fi
# Perform pacman system update, or exit with error code if pacman fails
echo -n "Updating system with [ " | teelog
echo -ne ${yellow}
echo -n "pacman" | teelog
echo -ne ${nc}
echo " ]..." | teelog
blank
if PAC_A="$(pacman -Sqyu --noconfirm --noprogressbar 2>&1)"; then
echo "$PAC_A" | teelog
blank
echo -n "Pacman update... [" | teelog
echo -ne ${green}
echo -n "ok" | teelog
echo -ne ${nc}
echo "]" | teelog
sleep 1
blank
else
blank
echo -e "Pacman update... [${red}failed${nc}]"
sleep 1
echo $PAC_A | teelog
echo
echo -e "${red}An error occurred when running [ ${yellow}pacman${red} ], consult log file for details." | teelog
echo -ne ${nc}
echo "Logfile located at: $logfile"
blank
echo "Do you want to try applying some fixes? [y/N]: "; read fix_pac
if [ $fix_pac = "Y" ] || [ $fix_pac = "y" ]; then
echo "Attempting fix 1:"
if PAC_B="$(pacman -Sy --needed archlinux-keyring && pacman -Su --noconfirm --noprogressbar 2>&1)"; then
echo "$PAC_B" | teelog
blank
echo -n "Fix 1 worked. Pacman update [" | teelog
echo -ne ${green}
echo -n "ok" | teelog
echo -ne ${nc}
echo "]" | teelog
exit 0
else
echo "Fix 1 " | teelog
echo -ne ${red}
echo -n "failed" | teelog
echo -ne ${nc}
fi
else
echo -n "Skipping fixes..." | teelog
fi
completion_stamp
if [ $shutcomp = 1 ]; then
do_shut
fi
exit 4
fi
# Check for orphaned packages and remove if any
pac_orphans
if [[ $rmcache = 1 ]]; then
echo "Pacman cache clean option selected."
echo "Removing all but last 3 versions of archived packages in cache..."
paccache -r | teelog
echo
fi
if [[ $aurupdate = 1 ]]; then
aurpkg_install
elif [ $aurpkgnum -gt 0 ]; then
echo -ne ${red}
echo -n $aurpkgnum | teelog
echo -ne ${yellow}
echo " foreign packages (likely installed from the AUR) have been detected, it might be a good idea to manually update these packages or re-run this script with -a flag." | teelog
echo -ne ${nc}
fi
blank
completion_stamp
echo
echo "Update script has completed successfully. Reboot recommended as soon as practical for changes to take effect and to check for stability of new kernel and packages."
echo
if [ $shutcomp = 1 ]; then
do_shut
fi
}
aurpkg_install(){
echo "Attempting AUR updates with ${helper}..." | teelog
echo "Switching out of sudo to run AUR updates..." | teelog
echo -e "${yellow}NOTE: User interaction will be required!${nc}"
if [ $helper = "paru" ]; then
su $SUDO_USER <<EOFSUDO
paru -Syqua --noconfirm
EOFSUDO
fi
if [ $helper = "yay" ]; then
su $SUDO_USER <<EOFSUDO1
yay -Syqua --noconfirm
EOFSUDO1
fi
echo "AUR package updates completed. Logging has not been recorded." | teelog
}
## Start of execution ##
########################
clear
# Command-line argument(s)
while getopts hsoar option; do
cli_opts
done
if [[ $unknownflag = 1 ]]; then
echo
echo -e "${red}Unknown flag(s) used!${nc}"
echo "Please use -h flag to view help which will describe possible usage of this script."
echo
exit 1
fi
if [[ $helpflag = 1 ]]; then
help_message
fi
# Check for root before processing other arguments
check_root
if [[ $shutflag = 1 ]]; then
shut_req
fi
if [[ $rmorpflag = 1 ]]; then
rmorp_req
fi
if [[ $aurflag = 1 ]]; then
aur_req
fi
# Check for log file directory
if [ ! -e /home/$SUDO_USER/logs ]
then
echo "Log directory not found, creating it..."
echo
mkdir -p /home/$SUDO_USER/logs
else
echo -e "Log directory found... [${green}ok${nc}]"
echo
fi
touch $logfile
# Check for timeshift
check_timeshift
#Main script runs here
main_script
Last edited by coxe87b (2024-10-26 10:39:42)
Desktop: Arch Linux | i3-gaps WM | AMD Ryzen 5700X | 32GB RAM | AMD Radeon RX 6700XT | Dual monitors @ 2560x1440
Laptop: Debian Linux | i3WM | Dell Latitude E7270 | Intel Core i5-6300U | 16GB RAM
~ Do or do not, there is no try ~
Offline
A couple of notes from a quick look over:
- Providing automatic shutdown option upon completion
What if there are pacnew files to merge? Or manual intervention communicated via front page news or post_install scriptlets? This is the main thing that people forget about when writing automatic update scripts.
pacman -Sy pkgup=$(pacman -Qu | wc -l)
This is dangerous and leaves the user at risk of partial upgrades if the script doesn't complete the update. Use checkupdates instead.
pacman -Sy git
Another partial upgrade. It'd be better to communicate to the user what prerequisites are needed for the script and abort when they are not present.
You're also making assumptions about sudo being present/used, where the calling users home directory is, where they store AUR/ABS build files, what flags they want to remove orphan packages with, etc.
Sakura:-
Mobo: MSI MAG X570S TORPEDO MAX // Processor: AMD Ryzen 9 5950X @4.9GHz // GFX: AMD Radeon RX 5700 XT // RAM: 32GB (4x 8GB) Corsair DDR4 (@ 3000MHz) // Storage: 1x 3TB HDD, 6x 1TB SSD, 2x 120GB SSD, 1x 275GB M2 SSD
Making lemonade from lemons since 2015.
Online
very cool project. and also WorMzy, it gives you the option
Offline
log including application version changes (shows old version -> new version which is a bit extra from the /var/log/pacman.log)
Already possible and shown in terminal and log.
VerbosePkgLists
Displays name, version and size of target packages formatted as a table for upgrade, sync and remove operations.
Maybe using that option can simplify your code ?
Disliking systemd intensely, but not satisfied with alternatives so focusing on taming systemd.
clean chroot building not flexible enough ?
Try clean chroot manager by graysky
Offline
coxe87b wrote:log including application version changes (shows old version -> new version which is a bit extra from the /var/log/pacman.log)
Already possible and shown in terminal and log.
man pacman.conf wrote:VerbosePkgLists
Displays name, version and size of target packages formatted as a table for upgrade, sync and remove operations.Maybe using that option can simplify your code ?
Thank you for the suggestion, I did not know about this option.
Desktop: Arch Linux | i3-gaps WM | AMD Ryzen 5700X | 32GB RAM | AMD Radeon RX 6700XT | Dual monitors @ 2560x1440
Laptop: Debian Linux | i3WM | Dell Latitude E7270 | Intel Core i5-6300U | 16GB RAM
~ Do or do not, there is no try ~
Offline
Thanks for the response.
What if there are pacnew files to merge? Or manual intervention communicated via front page news or post_install scriptlets? This is the main thing that people forget about when writing automatic update scripts.
Can you elaborate on how this could be handled within the script?
pacman -Sy pkgup=$(pacman -Qu | wc -l)
This is dangerous and leaves the user at risk of partial upgrades if the script doesn't complete the update. Use checkupdates instead.
Thanks. I will make this change on your advice. Can you elaborate on how it is dangerous to simply sync the package repo with -Sy by itself?
pacman -Sy git
Another partial upgrade. It'd be better to communicate to the user what prerequisites are needed for the script and abort when they are not present.
One of the improvements I have already decided upon it to simplify by doing this (including for the auto-install AUR helper section). I will be doing this, appreciate your input though.
You're also making assumptions about sudo being present/used, where the calling users home directory is, where they store AUR/ABS build files, what flags they want to remove orphan packages with, etc.
In the comments section of the script, I mention that it should be installed to /usr/local/bin and must be run with sudo. I also have a check function to prevent the script being run without sudo privileges. Yes, I am making some assumptions based on a typical Arch install, would you suggest using $HOME instead of /home/$SUDO_USER ? How would you suggest improving these things?
Desktop: Arch Linux | i3-gaps WM | AMD Ryzen 5700X | 32GB RAM | AMD Radeon RX 6700XT | Dual monitors @ 2560x1440
Laptop: Debian Linux | i3WM | Dell Latitude E7270 | Intel Core i5-6300U | 16GB RAM
~ Do or do not, there is no try ~
Offline
https://wiki.archlinux.org/title/System … nsupported
It's one of the most basic concepts in Arch.
Last edited by Scimmia (2024-10-28 12:30:36)
Offline