packages bug fix; added pkgi and pkgr
This commit is contained in:
@@ -128,6 +128,8 @@ A bar-style prompt showing current time, execution time of the last command
|
||||
| `get_pkgmgr` | packages | Detect the active package manager of the running distribution (`apt`, `dnf`, `yum`, `zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`) |
|
||||
| `pinfo` | info | Display system information read from `/proc` and `/sys` only |
|
||||
| `pkgf` | packages | Find which installed package owns a given file (distro-aware via `get_pkgmgr`) |
|
||||
| `pkgi` | packages | Install packages with the distribution's manager (distro-aware via `get_pkgmgr`); `-y` assumes yes, `-n` only shows the command; uses `sudo` (or `doas`) when not root |
|
||||
| `pkgr` | packages | Uninstall packages; `-p`/`--purge` also deletes configuration files (apt, pacman, apk; other managers just remove), `-d`/`--deps` also removes dependencies no longer needed (`--auto-remove`, `-Rs`, `--clean-deps`, `depclean`, ... — apk always does, dnf/yum are told not to unless `-d`); `-y`, `-n` as `pkgi` |
|
||||
| `pkgs` | packages | Search for a pattern in installed package names (distro-aware via `get_pkgmgr`) |
|
||||
| `ppg` | processes | Look for the given pattern in running processes |
|
||||
| `ppn` | processes | List processes matching an exact command name |
|
||||
|
||||
@@ -11,6 +11,9 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
|
||||
|
||||
### Added
|
||||
|
||||
- `pkgi` (install) and `pkgr` (uninstall, with `-p`/`--purge` and `-d`/`--deps`)
|
||||
commands for apt, dnf/yum, zypper, pacman, apk, portage, xbps and nix; both
|
||||
accept `-y`/`--yes` and `-n`/`--dry-run` and use `sudo` (or `doas`) when needed.
|
||||
- `utaz -i` / `--ignore-case` and `-s` / `--case-sensitive` switches to match
|
||||
archive extensions case-insensitively or not; the default is set by the new
|
||||
`UTAZ_DEFAULT_IGNORE_CASE` key of the `[compress]` section (default `0`).
|
||||
@@ -106,6 +109,8 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
|
||||
403, ...) are now failures instead of saved error pages; `urlencode`
|
||||
mis-encoded accents under `LC_ALL` and now uses `%20` for spaces; `isipv4`
|
||||
leaked a variable.
|
||||
- `packages.sh`: `pkgf ls` now looks the command up in the `PATH`; `pkgs`
|
||||
accepts patterns starting with `-`; `suse` is recognised in `ID_LIKE`.
|
||||
- `compress.sh`: option flags and loop variables no longer leak into the
|
||||
interactive shell.
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@ help()
|
||||
printf "myextip\t\tGet information about your public IP address\n"
|
||||
printf "pinfo\t\tDisplay system information read from /proc and /sys only\n"
|
||||
printf "pkgf\t\tFind which installed package owns a given file (distro-aware)\n"
|
||||
printf "pkgi\t\tInstall packages (distro-aware, sudo/doas when needed)\n"
|
||||
printf "pkgr\t\tUninstall packages; -p purges configuration, -d removes unneeded dependencies\n"
|
||||
printf "pkgs\t\tSearch for a pattern in installed package names (distro-aware)\n"
|
||||
printf "ppg\t\tLook for the given pattern in running processes\n"
|
||||
printf "ppn\t\tList processes matching an exact command name\n"
|
||||
|
||||
+317
-2
@@ -71,7 +71,7 @@ get_pkgmgr()
|
||||
echo "yum"
|
||||
return 0
|
||||
;;
|
||||
opensuse*|sles|sled)
|
||||
opensuse*|suse|sles|sled)
|
||||
echo "zypper"
|
||||
return 0
|
||||
;;
|
||||
@@ -193,7 +193,8 @@ pkgs()
|
||||
;;
|
||||
esac
|
||||
|
||||
"${list_cmd[@]}" | grep ${grep_opt:+"$grep_opt"} "$pkg"
|
||||
# -e: a pattern starting with "-" must not be read as a grep option
|
||||
"${list_cmd[@]}" | grep ${grep_opt:+"$grep_opt"} -e "$pkg"
|
||||
}
|
||||
export -f pkgs
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -240,6 +241,15 @@ pkgf()
|
||||
disp E "Please specify a file path."
|
||||
return 1
|
||||
}
|
||||
# A bare command name that is not a file here ("pkgf ls") is looked up in
|
||||
# the PATH: the package managers expect a path, and would otherwise match
|
||||
# the name as a pattern against every file of every package.
|
||||
if [[ $file != */* && ! -e $file ]]; then
|
||||
local _found
|
||||
_found=$(command -v -- "$file" 2>/dev/null) && [[ $_found == /* ]] && file="$_found"
|
||||
fi
|
||||
# "./" keeps a name starting with "-" from being read as an option
|
||||
[[ $file == -* ]] && file="./$file"
|
||||
|
||||
local pkgmgr
|
||||
pkgmgr=$(get_pkgmgr) || {
|
||||
@@ -279,4 +289,309 @@ export -f pkgf
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Internal helper: run a package manager command, with root privileges when
|
||||
# needed (sudo, else doas), or only display it in dry-run mode.
|
||||
# Usage: _pkg_run <needs_root 0|1> <dry_run 0|1> <command> [args...]
|
||||
_pkg_run()
|
||||
{
|
||||
local needs_root="$1" dry="$2"
|
||||
shift 2
|
||||
local -a prefix=()
|
||||
|
||||
if (( needs_root )) && [[ $EUID -ne 0 ]]; then
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
prefix=(sudo)
|
||||
elif command -v doas >/dev/null 2>&1; then
|
||||
prefix=(doas)
|
||||
elif (( ! dry )); then
|
||||
disp E "Root privileges are required and neither sudo nor doas is available."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( dry )); then
|
||||
# %q gives a copy-pasteable command line
|
||||
printf '%q ' "${prefix[@]}" "$@"
|
||||
printf '\n'
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
disp E "'$1' is not installed."
|
||||
return 1
|
||||
fi
|
||||
"${prefix[@]}" "$@"
|
||||
}
|
||||
export -f _pkg_run
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Install packages with the package manager of the distribution.
|
||||
# Usage: pkgi [options] <package> [package...]
|
||||
pkgi()
|
||||
{
|
||||
local assume_yes=0 dry_run=0
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hyn --long help,yes,dry-run -n 'pkgi' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"pkgi --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
# Restore the getopt-normalised (shell-quoted) argument list
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "pkgi: Install packages (distro-aware via get_pkgmgr).\n\n"
|
||||
printf "Usage: pkgi [options] <package> [package...]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\tDisplay this help screen\n"
|
||||
printf "\t-y, --yes\tDo not ask for confirmation (where the manager asks)\n"
|
||||
printf "\t-n, --dry-run\tOnly display the command that would be run\n\n"
|
||||
printf "Root privileges are obtained with sudo (or doas) when needed.\n"
|
||||
return 0
|
||||
;;
|
||||
-y|--yes)
|
||||
assume_yes=1
|
||||
shift
|
||||
;;
|
||||
-n|--dry-run)
|
||||
dry_run=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
disp E "Please specify at least one package name."
|
||||
return 1
|
||||
fi
|
||||
local pkg
|
||||
for pkg in "$@"; do
|
||||
# Not passed after "--": not every manager understands it
|
||||
if [[ $pkg == -* ]]; then
|
||||
disp E "Invalid package name: '$pkg'."
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
local pkgmgr
|
||||
pkgmgr=$(get_pkgmgr) || {
|
||||
disp E "No usable package manager could be detected on this system."
|
||||
return 2
|
||||
}
|
||||
|
||||
local -a cmd=()
|
||||
local needs_root=1
|
||||
case "$pkgmgr" in
|
||||
apt)
|
||||
cmd=(apt-get install)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
dnf|yum)
|
||||
cmd=("$pkgmgr" install)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
zypper)
|
||||
cmd=(zypper install)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
pacman)
|
||||
cmd=(pacman -S)
|
||||
(( assume_yes )) && cmd+=(--noconfirm)
|
||||
;;
|
||||
apk)
|
||||
# apk add never asks for confirmation
|
||||
cmd=(apk add)
|
||||
;;
|
||||
portage)
|
||||
cmd=(emerge)
|
||||
(( assume_yes )) || cmd+=(--ask)
|
||||
;;
|
||||
xbps)
|
||||
cmd=(xbps-install -S)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
nix)
|
||||
# nix-env works on the user's profile: no root needed
|
||||
cmd=(nix-env -i)
|
||||
needs_root=0
|
||||
;;
|
||||
*)
|
||||
disp E "Package manager '$pkgmgr' is not supported by pkgi."
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
|
||||
_pkg_run "$needs_root" "$dry_run" "${cmd[@]}" "$@"
|
||||
}
|
||||
export -f pkgi
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Uninstall packages with the package manager of the distribution.
|
||||
# Usage: pkgr [options] <package> [package...]
|
||||
# -p, --purge also delete the configuration files (apt purge, pacman -n,
|
||||
# apk --purge); managers without such a mode just remove
|
||||
# -d, --deps also remove the dependencies no longer needed. Without it,
|
||||
# the managers that do it by default (dnf, yum) are told not to
|
||||
# (apk always cleans up: it has no way to prevent it)
|
||||
pkgr()
|
||||
{
|
||||
local purge=0 deps=0 assume_yes=0 dry_run=0
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hpdyn --long help,purge,deps,yes,dry-run -n 'pkgr' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"pkgr --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
# Restore the getopt-normalised (shell-quoted) argument list
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "pkgr: Uninstall packages (distro-aware via get_pkgmgr).\n\n"
|
||||
printf "Usage: pkgr [options] <package> [package...]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\tDisplay this help screen\n"
|
||||
printf "\t-p, --purge\tAlso delete configuration files (apt, pacman, apk)\n"
|
||||
printf "\t-d, --deps\tAlso remove dependencies that are no longer needed\n"
|
||||
printf "\t-y, --yes\tDo not ask for confirmation (where the manager asks)\n"
|
||||
printf "\t-n, --dry-run\tOnly display the command that would be run\n\n"
|
||||
printf "Root privileges are obtained with sudo (or doas) when needed.\n"
|
||||
return 0
|
||||
;;
|
||||
-p|--purge)
|
||||
purge=1
|
||||
shift
|
||||
;;
|
||||
-d|--deps)
|
||||
deps=1
|
||||
shift
|
||||
;;
|
||||
-y|--yes)
|
||||
assume_yes=1
|
||||
shift
|
||||
;;
|
||||
-n|--dry-run)
|
||||
dry_run=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
disp E "Please specify at least one package name."
|
||||
return 1
|
||||
fi
|
||||
local pkg
|
||||
for pkg in "$@"; do
|
||||
if [[ $pkg == -* ]]; then
|
||||
disp E "Invalid package name: '$pkg'."
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
local pkgmgr
|
||||
pkgmgr=$(get_pkgmgr) || {
|
||||
disp E "No usable package manager could be detected on this system."
|
||||
return 2
|
||||
}
|
||||
|
||||
local -a cmd=() post=()
|
||||
local needs_root=1 purge_done=0
|
||||
case "$pkgmgr" in
|
||||
apt)
|
||||
# --auto-remove also drops the automatically installed packages
|
||||
# nothing needs any more
|
||||
if (( purge )); then cmd=(apt-get purge); purge_done=1; else cmd=(apt-get remove); fi
|
||||
(( deps )) && cmd+=(--auto-remove)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
dnf|yum)
|
||||
cmd=("$pkgmgr" remove)
|
||||
# These managers remove the unneeded dependencies by default
|
||||
(( deps )) || cmd+=(--setopt=clean_requirements_on_remove=False)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
zypper)
|
||||
cmd=(zypper remove)
|
||||
(( deps )) && cmd+=(--clean-deps)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
pacman)
|
||||
# -n: no .pacsave backups of the configuration files;
|
||||
# -s: also remove dependencies nothing else needs
|
||||
local flags="-R"
|
||||
(( purge )) && { flags+="n"; purge_done=1; }
|
||||
(( deps )) && flags+="s"
|
||||
cmd=(pacman "$flags")
|
||||
(( assume_yes )) && cmd+=(--noconfirm)
|
||||
;;
|
||||
apk)
|
||||
cmd=(apk del)
|
||||
(( purge )) && { cmd+=(--purge); purge_done=1; }
|
||||
;;
|
||||
portage)
|
||||
cmd=(emerge --unmerge)
|
||||
(( assume_yes )) || cmd+=(--ask)
|
||||
# depclean removes everything no longer needed, as autoremove does
|
||||
(( deps )) && post=(emerge --depclean)
|
||||
;;
|
||||
xbps)
|
||||
cmd=(xbps-remove)
|
||||
(( deps )) && cmd+=(-R)
|
||||
(( assume_yes )) && cmd+=(-y)
|
||||
;;
|
||||
nix)
|
||||
cmd=(nix-env -e)
|
||||
needs_root=0
|
||||
# Dependencies live in the store: collect what is now unreachable
|
||||
(( deps )) && post=(nix-collect-garbage)
|
||||
;;
|
||||
*)
|
||||
disp E "Package manager '$pkgmgr' is not supported by pkgr."
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if (( purge && ! purge_done )); then
|
||||
disp W "$pkgmgr has no purge mode: the packages are removed normally."
|
||||
fi
|
||||
|
||||
_pkg_run "$needs_root" "$dry_run" "${cmd[@]}" "$@" || return $?
|
||||
if (( ${#post[@]} )); then
|
||||
# Only reached when the removal itself succeeded
|
||||
_pkg_run "$needs_root" "$dry_run" "${post[@]}"
|
||||
fi
|
||||
}
|
||||
export -f pkgr
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
Reference in New Issue
Block a user