Created
January 28, 2026 01:33
-
-
Save jjaimealeman/570b19efd1d41d373de0c6ad0e6958dd to your computer and use it in GitHub Desktop.
pkg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Manage packages using Pacman (Arch) and Yay (AUR) repositories. | |
| # | |
| # SAVE AS: | |
| # ~/.local/bin/pkg | |
| # | |
| # SOURCE FROM: | |
| # https://github.com/nickjj/dotfiles/blob/master/.local/bin/pkg | |
| # | |
| # Examples: | |
| # pkg --help | |
| set -o errexit | |
| set -o pipefail | |
| set -o nounset | |
| if [ -r /etc/os-release ]; then | |
| # shellcheck disable=SC1091 | |
| OS_DISTRO="$(. /etc/os-release && echo "${ID_LIKE:-${ID}}")" | |
| if [ "${OS_DISTRO}" != "arch" ]; then | |
| echo "This script is only available on Arch Linux, sorry!" >&2 | |
| exit 1 | |
| fi | |
| fi | |
| POSITION=0 | |
| CMD= | |
| AUR= | |
| usage() { | |
| cat <<EOF | |
| Usage examples: | |
| pkg CMD [-a|--aur] | |
| pkg install # Install / search new packages | |
| ^ pacman -Syu --needed | |
| pkg install --aur # Install / search new AUR packages | |
| ^ yay -S --answerclean N --needed | |
| pkg list # List locally installed packages | |
| ^ pacman -Qei | |
| pkg list --aur # List locally installed AUR packages | |
| ^ yay -Qm | |
| pkg remove # Remove installed packages | |
| ^ pacman -Rns | |
| pkg remove --aur # Remove installed AUR packages | |
| ^ pacman -Rns | |
| pkg news [q] # Show Arch news (optionally headlines only) | |
| ^ yay -Pww[q] | |
| EOF | |
| } | |
| while [[ "${#}" -gt 0 ]]; do | |
| case "${1}" in | |
| -h | --help | help) | |
| usage | |
| exit 0 | |
| ;; | |
| -a | --aur) | |
| AUR="true" | |
| shift | |
| ;; | |
| *) | |
| case "${POSITION}" in | |
| 0) | |
| CMD="${1}" | |
| POSITION=1 | |
| shift | |
| ;; | |
| 1) | |
| [[ "${CMD}" == "news" && "${1}" == "q" ]] && break | |
| printf "Unknown argument: %s\n\n" "${1}" >&2 | |
| usage >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| ;; | |
| esac | |
| done | |
| [[ -z "${CMD}" ]] && usage >&2 && exit 1 | |
| fzf_args() { | |
| local prompt="${1}" | |
| local preview="${2}" | |
| local tiebreak="${3:-}" | |
| local args=( | |
| --multi | |
| --prompt "${prompt} " | |
| --delimiter "::" | |
| --preview "${preview}" | |
| --preview-window "down:55%:wrap" | |
| --preview-label-pos "bottom" | |
| --preview-label " tab: multi-select, shift+tab: deselect " | |
| ) | |
| [ -n "${tiebreak}" ] && args+=(--nth "1,2" --tiebreak "begin,index") | |
| printf "%s\n" "${args[@]}" | |
| } | |
| install() { | |
| local packages= | |
| # shellcheck disable=SC2016 | |
| mapfile -t fzf_opts \ | |
| < <(fzf_args "Search packages to install..." 'pacman -Si $(echo {} | cut -d " " -f 1)' "tiebreak") | |
| packages="$( | |
| pacman -Ss "" | awk ' | |
| /^[a-z]+\/[^\s]+/ { | |
| split($1,a,"/") | |
| pkg = a[2] | |
| getline desc | |
| sub(/^[ \t]+/, "", desc) | |
| printf "%s :: %s\n", pkg, desc | |
| }' | | |
| fzf "${fzf_opts[@]}" | cut -d " " -f 1 | |
| )" | |
| if [ -n "${packages}" ]; then | |
| # shellcheck disable=SC2086 | |
| sudo pacman -Syu --needed ${packages} | |
| fi | |
| } | |
| install_aur() { | |
| local packages= | |
| local query packages= | |
| read -rp "Enter AUR search query (at least 3 characters): " query | |
| if [[ ${#query} -lt 3 ]]; then | |
| echo "'${query}' must be 3 or more characters, aborting!" >&2 | |
| exit 1 | |
| fi | |
| # shellcheck disable=SC2016 | |
| mapfile -t fzf_opts \ | |
| < <(fzf_args "Filter AUR packages to install..." 'yay -Si $(echo {} | cut -d " " -f 1)' "tiebreak") | |
| packages="$( | |
| yay -Ssa "$query" | awk ' | |
| /^[^ ]/ { | |
| split($1, a, "/") | |
| pkg = a[2] | |
| next | |
| } | |
| /^[ ]/ { | |
| desc = $0 | |
| sub(/^[ \t]+/, "", desc) | |
| printf "%s :: %s\n", pkg, desc | |
| } | |
| ' | | |
| fzf "${fzf_opts[@]}" | cut -d " " -f 1 | |
| )" | |
| if [ -n "${packages}" ]; then | |
| # shellcheck disable=SC2086 | |
| yay -S --answerclean N --needed ${packages} | |
| fi | |
| } | |
| list() { | |
| local packages= | |
| # shellcheck disable=SC2016 | |
| mapfile -t fzf_opts \ | |
| < <(fzf_args "Search installed packages..." 'pacman -Qi $(echo {} | cut -d " " -f 1)' "tiebreak") | |
| packages="$( | |
| pacman -Qei | awk ' | |
| BEGIN { name = ""; desc = "" } | |
| /^Name\s*:/ { name = $3 } | |
| /^Description\s*:/ { desc = substr($0, index($0,$3)) } | |
| /^$/ && name != "" && desc != "" { | |
| printf "%s :: %s\n", name, desc | |
| name = ""; desc = "" | |
| }' | fzf "${fzf_opts[@]}" | |
| )" | |
| } | |
| list_aur() { | |
| local packages= | |
| # shellcheck disable=SC2016 | |
| mapfile -t fzf_opts \ | |
| < <(fzf_args "Search installed AUR packages..." 'yay -Qi $(echo {} | cut -d" " -f1)' "tiebreak") | |
| packages="$( | |
| yay -Qm | awk ' | |
| { | |
| pkg = $1 | |
| cmd = "yay -Qi " pkg " 2>/dev/null | grep -m1 \"^Description\"" | |
| desc = "" | |
| if ((cmd | getline line) > 0) { | |
| sub(/^[^:]*:\s*/, "", line) | |
| desc = line | |
| } | |
| close(cmd) | |
| printf "%s :: %s\n", pkg, desc | |
| }' | fzf "${fzf_opts[@]}" | |
| )" | |
| } | |
| remove() { | |
| local packages= | |
| # shellcheck disable=SC2016 | |
| mapfile -t fzf_opts \ | |
| < <(fzf_args "Search installed packages to remove..." 'pacman -Qi $(echo {} | cut -d " " -f 1)' "tiebreak") | |
| packages="$( | |
| pacman -Qei | awk ' | |
| BEGIN { name = ""; desc = "" } | |
| /^Name\s*:/ { name = $3 } | |
| /^Description\s*:/ { desc = substr($0, index($0,$3)) } | |
| /^$/ && name != "" && desc != "" { | |
| printf "%s :: %s\n", name, desc | |
| name = ""; desc = "" | |
| }' | fzf "${fzf_opts[@]}" | cut -d " " -f 1 | |
| )" | |
| if [ -n "${packages}" ]; then | |
| # shellcheck disable=SC2086 | |
| sudo pacman -Rns ${packages} | |
| fi | |
| } | |
| remove_aur() { | |
| local packages= | |
| # shellcheck disable=SC2016 | |
| mapfile -t fzf_opts \ | |
| < <(fzf_args "Search installed AUR packages to remove..." 'yay -Qi $(echo {} | cut -d " " -f 1)' "tiebreak") | |
| # This is the same code as listing from the AUR plus an added cut pipe. This | |
| # could be refactored at some point, for now it's not the end of the world. | |
| packages="$( | |
| yay -Qm | awk ' | |
| { | |
| pkg = $1 | |
| cmd = "yay -Qi " pkg " 2>/dev/null | grep -m1 \"^Description\"" | |
| desc = "" | |
| if ((cmd | getline line) > 0) { | |
| sub(/^[^:]*:\s*/, "", line) | |
| desc = line | |
| } | |
| close(cmd) | |
| printf "%s :: %s\n", pkg, desc | |
| }' | fzf "${fzf_opts[@]}" | cut -d " " -f 1 | |
| )" | |
| if [ -n "${packages}" ]; then | |
| # shellcheck disable=SC2086 | |
| sudo pacman -Rns ${packages} | |
| fi | |
| } | |
| show_news() { | |
| local headlines="${1}" | |
| # This occasionally breaks when Arch is getiting attacked but the main site | |
| # is sometimes still up so let's always allow this to work to display a link | |
| # to the main site in all cases. | |
| yay -Pww"${headlines}" || true | |
| cat <<EOF | |
| --- | |
| View the official Arch news site: https://archlinux.org/news/ | |
| EOF | |
| } | |
| if [[ "${CMD}" = "install" && -z "${AUR}" ]]; then | |
| install | |
| elif [[ "${CMD}" = "install" && -n "${AUR}" ]]; then | |
| install_aur | |
| elif [[ "${CMD}" = "list" && -z "${AUR}" ]]; then | |
| list | |
| elif [[ "${CMD}" = "list" && -n "${AUR}" ]]; then | |
| list_aur | |
| elif [[ "${CMD}" = "remove" && -z "${AUR}" ]]; then | |
| remove | |
| elif [[ "${CMD}" = "remove" && -n "${AUR}" ]]; then | |
| remove_aur | |
| elif [[ "${CMD}" = "news" ]]; then | |
| show_news "${1:-}" | |
| else | |
| printf "Unknown command: %s\n\n" "${CMD}" >&2 | |
| usage >&2 | |
| exit 1 | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment