From ea76cc9ef2b14584dbe8bb578e25ae774fc35413 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 27 Oct 2026 13:13:37 +0000 Subject: [PATCH] update --- README.md | 23 ++++-- aliases.sh | 201 ++++++++--------------------------------------- commands.sh | 68 ++++++++++++++++ commands.tsv | 1 - hosts.sh | 16 ++++ interactive.sh | 20 +++++ platform/arch.sh | 56 +++++++++++++ 7 files changed, 210 insertions(+), 175 deletions(-) create mode 100644 commands.sh create mode 100644 hosts.sh create mode 100644 interactive.sh create mode 100644 platform/arch.sh diff --git a/README.md b/README.md index 2d7c181..1621e25 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ or Nushell. - Load environment setup that is portable at login, such as Cargo's `env` file. - Provide common aliases and small interactive functions that are useful across supported Bourne-family shells. +- Isolate Arch Linux commands from otherwise portable shell configuration. ## Boundaries @@ -23,7 +24,7 @@ The `profile` file must remain quiet, idempotent and safe for a POSIX shell to source during login. It must not print an alias catalogue, configure a prompt, perform interactive terminal operations or start background work. -Interactive shells should source `aliases.sh` from their own RC file. The +Interactive shells should source `interactive.sh` from their own RC file. The login profile deliberately does not source it, because login profiles may also run in non-interactive sessions. @@ -34,18 +35,26 @@ belong in the separate `systemd` repository. ## Files - `profile`: portable login environment and PATH construction. -- `aliases.sh`: common interactive aliases and helper functions. -- `commands.tsv`: curated command descriptions and startup visibility. +- `interactive.sh`: POSIX loader and manually curated alias reminder. +- `aliases.sh`: common POSIX interactive aliases and helper functions. +- `commands.sh`: command-catalogue display and validation functions. +- `hosts.sh`: SSH destination listing function. +- `platform/arch.sh`: Arch Linux applications, GNU options and Pacman/systemd + integration, written with POSIX shell syntax. +- `commands.tsv`: curated command descriptions and reminder visibility. - `commands.README.md`: format and maintenance instructions for `commands.tsv`. - `install.sh`: installs the `~/.profile` symlink. ## Alias portability -`aliases.sh` is intended to become POSIX-compatible for Bourne-family shells, -but it currently contains some Bash-oriented constructs. Before another shell -sources it, replace or isolate uses of `source`, `disown`, `local`, `echo -e`, -Bash-style `cd --`, and the transitional `debug.log` alias name. +The shared interactive files use POSIX shell syntax. Bash-only compatibility +aliases—including names that are deliberately being retired—live in the Bash +repository's `aliases.bash` instead. External commands remain platform-specific by design: aliases for Pacman, systemd, GNU utilities and local applications target this Arch Linux setup. Syntax portability does not imply operating-system portability. + +The Arch `update` function runs the local `archnews_wrap` Pacman workflow and, +after a successful upgrade, requests a non-blocking refresh from the systemd +user service `package-status-refresh.service`. diff --git a/aliases.sh b/aliases.sh index 180d013..046a397 100644 --- a/aliases.sh +++ b/aliases.sh @@ -1,27 +1,8 @@ -#!/usr/bin/env bash -# shellcheck shell=bash -ALIASES_GREEN='\033[0;32m' # Green -ALIASES_BLUE='\033[0;34m' # Blue -ALIASES_WHITE='\033[1;37m' # White -ALIASES_NC='\033[0m' # No Color +#!/bin/sh -echo "Remember aliases:" -echo -e "${ALIASES_WHITE} et en ens ee" -echo " dusort debuglog mkcd" -echo -e "${ALIASES_NC}" -echo "" +# Common interactive aliases and helpers using POSIX shell syntax. -alias backup='sudo borgmatic create --verbosity 1 --files' -alias disk-usage='printf "%s\n" "disk-usage is deprecated; use dusort instead." >&2' - -alias cs='lxc list -c ns4b,user.comment:comment' -alias pc='pcmanfm . & disown' - -# enable aliases for commands inside sudo -# TOOD check this is safe -alias sudo='sudo ' - -alias venv='source ./venv/bin/activate' +alias venv='. ./venv/bin/activate' alias e='nvim' alias er='nvim README.md' @@ -31,182 +12,68 @@ alias ens='nvim NEXT_STEPS.md' alias ea='nvim .autonote.md' alias ee='nvim .' -alias speak='/usr/local/src/kokora/speak' - debuglog() { clear : > debug.log tail -f debug.log } -alias debug.log='printf "%s\n" "debug.log is deprecated; use debuglog instead." >&2' +alias g='git status' -dusort() { - if [ "$#" -gt 1 ]; then - printf '%s\n' 'Usage: dusort [path]' >&2 - return 2 - fi - - _dusort_path=${1:-.} - if [ ! -e "$_dusort_path" ]; then - printf 'dusort: path does not exist: %s\n' "$_dusort_path" >&2 - unset _dusort_path - return 1 - fi - - if sudo du -h -d 2 -- "$_dusort_path" | sort -h; then - unset _dusort_path +gg() { + _gg_message=${*:-update} + if git add . && git commit -m "$_gg_message" && git push; then + unset _gg_message return 0 fi - unset _dusort_path + unset _gg_message return 1 } -alias g='git status' -alias gg='git add . && git commit -m "update" && git push' +wget() { + printf 'wget wrapper: using HSTS file %s\n' "$XDG_DATA_HOME/wget-hsts" >&2 + command wget --hsts-file="$XDG_DATA_HOME/wget-hsts" "$@" +} -alias ls='LC_COLLATE=C ls --color=auto -h --group-directories-first --time-style=+"%d.%m.%Y %H:%M" -F' - -alias cp='cp -irv' -alias mkdir='mkdir -vp' -alias mv='mv -iv' -alias rm='rm -dv' - -alias wget='wget --hsts-file="$XDG_DATA_HOME/wget-hsts"' - -alias grep='grep --colour=auto' -alias egrep='egrep --colour=auto' -alias fgrep='fgrep --colour=auto' alias bc='bc -q -l' alias ytdl='yt-dlp --write-info-json --write-description --no-playlist' alias ytdlb='yt-dlp --write-info-json --write-description --no-playlist -a' alias ytdl-mp3='yt-dlp -f bestaudio --extract-audio --audio-format mp3 --embed-thumbnail --add-metadata' -# Transitional aliases for destinations now managed by ~/.ssh/config. -alias mac='printf "%s\n" "mac is deprecated; use ssh mac instead." >&2' -alias pi4='printf "%s\n" "pi4 is deprecated; use ssh pi4 instead." >&2' -alias red='printf "%s\n" "red is deprecated; use ssh red instead." >&2' -alias alice='printf "%s\n" "alice is deprecated; use ssh alice instead." >&2' -alias phthalo='printf "%s\n" "phthalo is deprecated; use ssh phthalo instead." >&2' -alias zirconia='printf "%s\n" "zirconia is deprecated; use ssh zirconia instead." >&2' -alias zirconiarc='printf "%s\n" "zirconiarc is deprecated; use ssh zirconiarc instead." >&2' -alias datapasa='printf "%s\n" "datapasa is deprecated; use ssh datapasa instead." >&2' -alias ultra='printf "%s\n" "ultra is deprecated; use ssh ultra instead." >&2' -alias singsay='printf "%s\n" "singsay is deprecated; use ssh singsay instead." >&2' -alias kermit='printf "%s\n" "kermit is deprecated; use ssh kermit instead." >&2' -alias crafty='printf "%s\n" "crafty is deprecated; use ssh crafty instead." >&2' -alias lv='printf "%s\n" "lv is deprecated; use ssh lv instead." >&2' - -commands() { - awk -F '\t' ' - NF == 0 { next } - NF != 3 || ($1 != "yes" && $1 != "no") || $2 == "" || $3 == "" { - printf "Invalid command reminder on line %d.\n", NR > "/dev/stderr" - invalid = 1 - next - } - seen[$2]++ { - printf "Duplicate command reminder for %s on line %d.\n", $2, NR > "/dev/stderr" - invalid = 1 - next - } - { - count++ - reminder[count] = $1 - command[count] = $2 - description[count] = $3 - if (length($2) > command_width) command_width = length($2) - if (length($3) > description_width) description_width = length($3) - } - END { - if (invalid) exit 1 - if (length("COMMAND") > command_width) command_width = length("COMMAND") - if (length("DESCRIPTION") > description_width) description_width = length("DESCRIPTION") - printf "%-*s %-*s %s\n", command_width, "COMMAND", description_width, "DESCRIPTION", "REMINDER" - for (row = 1; row <= count; row++) { - printf "%-*s %-*s %s\n", command_width, command[row], description_width, description[row], reminder[row] - } - } - ' "${XDG_CONFIG_HOME:-$HOME/.config}/profile/commands.tsv" -} - -_command_reminder() { - awk -F '\t' -v width="${COLUMNS:-80}" ' - NF == 0 { next } - NF != 3 || ($1 != "yes" && $1 != "no") || $2 == "" || $3 == "" { - printf "Invalid command reminder on line %d.\n", NR > "/dev/stderr" - invalid = 1 - next - } - seen[$2]++ { - printf "Duplicate command reminder for %s on line %d.\n", $2, NR > "/dev/stderr" - invalid = 1 - next - } - $1 == "yes" { command[++count] = $2 } - END { - if (invalid) exit 1 - if (width < 40) width = 80 - line = " " - for (row = 1; row <= count; row++) { - separator = (line == " " ? "" : " ") - if (length(line) + length(separator) + length(command[row]) > width) { - print line - line = " " command[row] - } else { - line = line separator command[row] - } - } - if (line != " ") print line - } - ' "${XDG_CONFIG_HOME:-$HOME/.config}/profile/commands.tsv" -} - -# lf gets confused by TERMINFO env var when running in tmux +# lf gets confused by TERMINFO when running in tmux. # https://github.com/gokcehan/lf/issues/138#issuecomment-464477665 -if [ -n "$TERMINFO" ] ; then - alias lf="TERMINFO= lf -last-dir-path=~/.cache/lf-last-dir" +if [ -n "${TERMINFO:-}" ]; then + alias lf='TERMINFO= lf -last-dir-path="$HOME/.cache/lf-last-dir"' else - alias lf="lf -last-dir-path=~/.cache/lf-last-dir" + alias lf='lf -last-dir-path="$HOME/.cache/lf-last-dir"' fi cdlf() { - local last_dir [ -r "$HOME/.cache/lf-last-dir" ] || return 1 - IFS= read -r last_dir < "$HOME/.cache/lf-last-dir" - [ -n "$last_dir" ] || return 1 - cd -- "$last_dir" || return + IFS= read -r _cdlf_last_dir < "$HOME/.cache/lf-last-dir" + [ -n "$_cdlf_last_dir" ] || return 1 + + if cd "$_cdlf_last_dir"; then + unset _cdlf_last_dir + return 0 + fi + + unset _cdlf_last_dir + return 1 } lfcd() { - lf -last-dir-path="$HOME"/.cache/lf-last-dir && cdlf -} - -hosts() { - if [ ! -r "$HOME/.ssh/config.d/hosts.conf" ]; then - printf '%s\n' 'SSH host configuration is unavailable.' >&2 - return 1 - fi - - awk ' - tolower($1) == "host" { - for (field = 2; field <= NF; field++) { - if ($field !~ /[*?!]/) print $field - } - } - ' "$HOME/.ssh/config.d/hosts.conf" -} - -update() { - echo -e "Executing \"${ALIASES_GREEN}pacman -Syu${ALIASES_NC}\" via \"${ALIASES_BLUE}archnews_wrap${ALIASES_NC}\" wrapper" - - sudo ~/.src/archnews/archnews_wrap -Syu || return - systemctl --user start --no-block package-status-refresh.service >/dev/null 2>&1 || true + lf -last-dir-path="$HOME/.cache/lf-last-dir" && cdlf } mkcd() { - mkdir "$1" || return + if [ "$#" -ne 1 ]; then + printf '%s\n' 'Usage: mkcd ' >&2 + return 2 + fi + + mkdir -p "$1" || return cd "$1" || return } diff --git a/commands.sh b/commands.sh new file mode 100644 index 0000000..9480250 --- /dev/null +++ b/commands.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +# Display and validate the curated command catalogue. + +commands() { + awk -F '\t' ' + NF == 0 { next } + NF != 3 || ($1 != "yes" && $1 != "no") || $2 == "" || $3 == "" { + printf "Invalid command reminder on line %d.\n", NR > "/dev/stderr" + invalid = 1 + next + } + seen[$2]++ { + printf "Duplicate command reminder for %s on line %d.\n", $2, NR > "/dev/stderr" + invalid = 1 + next + } + { + count++ + reminder[count] = $1 + command[count] = $2 + description[count] = $3 + if (length($2) > command_width) command_width = length($2) + if (length($3) > description_width) description_width = length($3) + } + END { + if (invalid) exit 1 + if (length("COMMAND") > command_width) command_width = length("COMMAND") + if (length("DESCRIPTION") > description_width) description_width = length("DESCRIPTION") + printf "%-*s %-*s %s\n", command_width, "COMMAND", description_width, "DESCRIPTION", "REMINDER" + for (row = 1; row <= count; row++) { + printf "%-*s %-*s %s\n", command_width, command[row], description_width, description[row], reminder[row] + } + } + ' "${XDG_CONFIG_HOME:-$HOME/.config}/profile/commands.tsv" +} + +_command_reminder() { + awk -F '\t' -v width="${COLUMNS:-80}" ' + NF == 0 { next } + NF != 3 || ($1 != "yes" && $1 != "no") || $2 == "" || $3 == "" { + printf "Invalid command reminder on line %d.\n", NR > "/dev/stderr" + invalid = 1 + next + } + seen[$2]++ { + printf "Duplicate command reminder for %s on line %d.\n", $2, NR > "/dev/stderr" + invalid = 1 + next + } + $1 == "yes" { command[++count] = $2 } + END { + if (invalid) exit 1 + if (width < 40) width = 80 + line = " " + for (row = 1; row <= count; row++) { + separator = (line == " " ? "" : " ") + if (length(line) + length(separator) + length(command[row]) > width) { + print line + line = " " command[row] + } else { + line = line separator command[row] + } + } + if (line != " ") print line + } + ' "${XDG_CONFIG_HOME:-$HOME/.config}/profile/commands.tsv" +} diff --git a/commands.tsv b/commands.tsv index 15bdf6b..d50c3c6 100644 --- a/commands.tsv +++ b/commands.tsv @@ -7,7 +7,6 @@ yes qv4l2 Video4Linux control panel no cheat command-line cheatsheets no vimv batch rename files in Vim yes rclone sync files with cloud storage -yes mid3v2 edit ID3 audio metadata yes zathura document viewer yes tesseract optical character recognition yes remmina remote desktop client diff --git a/hosts.sh b/hosts.sh new file mode 100644 index 0000000..93adca4 --- /dev/null +++ b/hosts.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +hosts() { + if [ ! -r "$HOME/.ssh/config.d/hosts.conf" ]; then + printf '%s\n' 'SSH host configuration is unavailable.' >&2 + return 1 + fi + + awk ' + tolower($1) == "host" { + for (field = 2; field <= NF; field++) { + if ($field !~ /[*?!]/) print $field + } + } + ' "$HOME/.ssh/config.d/hosts.conf" +} diff --git a/interactive.sh b/interactive.sh new file mode 100644 index 0000000..c62f76e --- /dev/null +++ b/interactive.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Shared interactive setup for POSIX-compatible shells. + +_profile_interactive_dir=${XDG_CONFIG_HOME:-$HOME/.config}/profile + +. "$_profile_interactive_dir/aliases.sh" +. "$_profile_interactive_dir/commands.sh" +. "$_profile_interactive_dir/hosts.sh" + +if [ -r /etc/arch-release ]; then + . "$_profile_interactive_dir/platform/arch.sh" +fi + +printf '%s\n' 'Remember aliases:' +printf '\033[1;37m%s\n' ' et en ens ee' +printf '%s\n' ' dusort debuglog mkcd' +printf '\033[0m\n\n' + +unset _profile_interactive_dir diff --git a/platform/arch.sh b/platform/arch.sh new file mode 100644 index 0000000..ec51a32 --- /dev/null +++ b/platform/arch.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# Interactive commands specific to this Arch Linux workstation. + +_ARCH_GREEN='\033[0;32m' +_ARCH_BLUE='\033[0;34m' +_ARCH_NC='\033[0m' + +alias backup='sudo borgmatic create --verbosity 1 --files' +alias cs='lxc list -c ns4b,user.comment:comment' + +pc() { + nohup pcmanfm . >/dev/null 2>&1 & +} + +# Expand aliases for commands following sudo. +alias sudo='sudo ' + +alias speak='/usr/local/src/kokora/speak' + +alias ls='LC_COLLATE=C ls --color=auto -h --group-directories-first --time-style=+"%d.%m.%Y %H:%M" -F' +alias cp='cp -irv' +alias mkdir='mkdir -vp' +alias mv='mv -iv' +alias rm='rm -dv' +alias grep='grep --colour=auto' + +dusort() { + if [ "$#" -gt 1 ]; then + printf '%s\n' 'Usage: dusort [path]' >&2 + return 2 + fi + + _dusort_path=${1:-.} + if [ ! -e "$_dusort_path" ]; then + printf 'dusort: path does not exist: %s\n' "$_dusort_path" >&2 + unset _dusort_path + return 1 + fi + + if sudo du -h -d 2 -- "$_dusort_path" | sort -h; then + unset _dusort_path + return 0 + fi + + unset _dusort_path + return 1 +} + +update() { + printf 'Executing "%bpacman -Syu%b" via "%barchnews_wrap%b" wrapper\n' \ + "$_ARCH_GREEN" "$_ARCH_NC" "$_ARCH_BLUE" "$_ARCH_NC" + + sudo "$HOME/.src/archnews/archnews_wrap" -Syu || return + systemctl --user start --no-block package-status-refresh.service >/dev/null 2>&1 || true +}