Terminal: Das OS aliases and functions (0.9.3)
build / build (push) Successful in 23m2s

- das-shell 0.3.0: aliases and functions for every interactive bash
  (/usr/share/das/shell, loaded by /etc/bash/bashrc.d/das-aliases.sh,
  then the user's ~/.bash_aliases and ~/.bash_functions; off with
  ~/.config/das/no-aliases). Based on the author's own set, adapted to
  Void (xbps, nftables, das-update), with fixes (rmhost, netinfo,
  todo -r, dirsize, extract) and new ones (up, bigfiles, pkgowner,
  please, serve, weather, cheat, genpass, Docker shortcuts). lsfunc
  shows bilingual descriptions. Shell tests in tests/test_shell.py.
- Names that shadowed commands are gone: gh -> hgrep, mount -> mounts,
  no install or ed.
- README: "Aliases and functions" section; TODO: banner, dashboard
  service and prompt verified by the author on real hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
das
2026-10-21 19:35:58 +03:00
co-authored by Claude Opus 5.5
parent 6564d1eb17
commit e01e1786d6
9 changed files with 580 additions and 12 deletions
@@ -0,0 +1,18 @@
# Das OS aliases and functions (lsfunc lists the functions), then the user's
# own ~/.bash_aliases and ~/.bash_functions, which win. Off for one user:
# touch ~/.config/das/no-aliases (the user's own files are still read).
# shellcheck shell=bash
if [[ $- == *i* ]]; then
if [[ ! -e ${XDG_CONFIG_HOME:-$HOME/.config}/das/no-aliases ]]; then
for __das_f in /usr/share/das/shell/aliases.sh /usr/share/das/shell/functions.sh; do
# shellcheck source=/dev/null
[[ -r $__das_f ]] && . "$__das_f"
done
fi
for __das_f in ~/.bash_aliases ~/.bash_functions; do
# shellcheck source=/dev/null
[[ -r $__das_f ]] && . "$__das_f"
done
unset __das_f
fi
+85
View File
@@ -0,0 +1,85 @@
# Das OS aliases, loaded by /etc/bash/bashrc.d/das-aliases.sh in interactive
# shells. Your own ~/.bash_aliases is read after this file and wins.
# shellcheck shell=bash
# --- ls ---------------------------------------------------------------------
alias ls='ls --color=auto'
alias sl='ls'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias lsl='ls -lhFA --color=always | less -R'
alias lsz='ls --human-readable --size -1 -S --classify'
alias lst='ls -t -1'
alias lsd='ls -d */'
alias grep='grep --color=auto'
alias egrep='grep -E --color=auto'
alias fgrep='grep -F --color=auto'
alias diff='diff --color=auto'
alias ip='ip -color=auto'
# --- directories --------------------------------------------------------------
alias ..='cd ..'
alias cd..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias mkdir='mkdir -p'
alias path='echo "${PATH//:/$'\''\n'\''}"'
# --- files and disks ----------------------------------------------------------
alias cnt='find . -type f | wc -l'
alias fhere='find . -name'
alias df='df -h'
alias dft='df -Th --total -x tmpfs -x devtmpfs -x efivarfs'
alias dftl='df -Tha --total | less'
alias mounts='mount | column -t'
alias mnt="findmnt -rno SOURCE,TARGET,FSTYPE | grep '^/dev/' | sort | column -t"
alias snaps='sudo snapper -c root list'
# --- memory and processes -----------------------------------------------------
alias free='free -ht'
alias meminfo='command free -mlt'
alias cpuinfo='lscpu'
alias j='jobs -l'
alias psg='ps aux | grep -v grep | grep -i -e VSZ -e'
alias psmem='ps aux --sort=-%mem'
alias psmem10='ps aux --sort=-%mem | head -n 11'
alias pscpu='ps aux --sort=-%cpu'
alias pscpu10='ps aux --sort=-%cpu | head -n 11'
alias gpumeminfo='grep -i --color memory /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log 2>/dev/null'
# --- time ----------------------------------------------------------------------
alias now='date +"%T"'
alias nowtime='now'
alias nowdate='date +"%d-%m-%Y"'
# --- network -------------------------------------------------------------------
alias ping5='ping -c 5'
alias fastping='ping -c 100 -i 0.2'
alias oports='sudo ss -tulpn'
alias gport='sudo ss -tulpn | grep'
alias header='curl -I'
alias headerc='curl -I --compressed'
alias wget='wget -c'
alias nftlist='sudo nft list ruleset'
alias firewall='dfw status'
# --- history --------------------------------------------------------------
alias hgrep='history | grep'
alias histop10='histat 10'
# --- packages and updates (xbps) -------------------------------------------------
alias upd='das-update --cli'
alias updy='das-update --cli -y'
alias pkgclean='sudo xbps-remove -Oo'
# --- Docker and git ----------------------------------------------------------------
alias dps='docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"'
alias dlog='docker logs -f --tail 100'
alias dcu='docker compose up -d'
alias dcd='docker compose down'
alias dcl='docker compose logs -f --tail 100'
alias dcp='docker compose pull'
alias lzd='lazydocker'
alias lzg='lazygit'
+374
View File
@@ -0,0 +1,374 @@
# Das OS shell functions, loaded by /etc/bash/bashrc.d/das-aliases.sh in
# interactive shells. Your own ~/.bash_functions is read after this file.
# The first line of each function is its description for lsfunc:
# "# Українською | In English".
# shellcheck shell=bash
__das_uk() {
[[ ${LC_ALL:-${LC_MESSAGES:-${LANG:-}}} == uk* ]]
}
# __das_say UKRAINIAN ENGLISH
__das_say() {
if __das_uk; then echo "$1"; else echo "$2"; fi
}
# __das_usage UKRAINIAN ENGLISH: a usage line on stderr, returns 2
__das_usage() {
__das_say "використання: $1" "usage: $2" >&2
return 2
}
# __das_need COMMAND PACKAGE: is COMMAND there? If not, says which package has it
__das_need() {
command -v "$1" >/dev/null && return 0
__das_say "немає команди $1, встановіть: xi $2" "$1 is missing, install it: xi $2" >&2
return 127
}
lsfunc() {
# Список функцій з описами | List the functions with their descriptions
local name file line descr restore
restore=$(shopt -p extdebug)
shopt -s extdebug
while read -r name; do
[[ $name == _* ]] && continue
read -r _ line file < <(declare -F "$name")
descr=""
if [[ -r $file ]]; then
descr=$(sed -n "$((line + 1)),$((line + 2))p" "$file" | grep -m1 -E '^[[:space:]]*#' |
sed -E 's/^[[:space:]]*#[[:space:]]*//')
fi
if [[ $descr == *" | "* ]]; then
if __das_uk; then descr=${descr%% | *}; else descr=${descr#* | }; fi
fi
printf '%-12s %s\n' "$name" "$descr"
done < <(compgen -A function | sort)
eval "$restore"
}
# --- archives --------------------------------------------------------------------
extract() {
# Розпакувати архіви (tar.*, zip, rar, 7z, gz, xz, zst, xbps…) | Extract archives (tar.*, zip, rar, 7z, gz, xz, zst, xbps…)
(($#)) || { __das_usage "extract АРХІВ..." "extract ARCHIVE..."; return; }
local f rc=0
for f in "$@"; do
if [[ ! -f $f ]]; then
__das_say "'$f' — не файл" "'$f' is not a file" >&2
rc=1
continue
fi
case ${f,,} in
*.tar | *.tar.* | *.tgz | *.tbz | *.tbz2 | *.txz | *.tzst | *.xbps) tar -xvf "$f" ;;
*.zip | *.jar) __das_need unzip unzip && unzip "$f" ;;
*.rar) __das_need unrar unrar && unrar x "$f" ;;
*.7z) __das_need 7z 7zip && 7z x "$f" ;;
*.gz) gunzip -k "$f" ;;
*.bz2) bunzip2 -k "$f" ;;
*.xz) unxz -k "$f" ;;
*.zst) __das_need unzstd zstd && unzstd "$f" ;;
*.lz4) __das_need lz4 lz4 && lz4 -d "$f" ;;
*.z) uncompress "$f" ;;
*)
__das_say "'$f': невідомий тип архіву" "'$f': unknown archive type" >&2
false
;;
esac || rc=1
done
return $rc
}
compress() {
# Запакувати файли в архів; тип за назвою | Pack files into an archive, the type comes from the name
(($# >= 2)) || { __das_usage "compress АРХІВ.tar.gz ФАЙЛ..." "compress ARCHIVE.tar.gz FILE..."; return; }
local archive=$1
shift
case ${archive,,} in
*.tar.gz | *.tgz) tar -czvf "$archive" -- "$@" ;;
*.tar.bz2 | *.tbz2) tar -cjvf "$archive" -- "$@" ;;
*.tar.xz | *.txz) tar -cJvf "$archive" -- "$@" ;;
*.tar.zst | *.tzst) __das_need zstd zstd && tar --zstd -cvf "$archive" -- "$@" ;;
*.tar) tar -cvf "$archive" -- "$@" ;;
*.zip) __das_need zip zip && zip -r "$archive" -- "$@" ;;
*.7z) __das_need 7z 7zip && 7z a "$archive" -- "$@" ;;
*)
__das_say "невідомий тип архіву: $archive (tar.gz, tar.xz, tar.zst, zip, 7z…)" \
"unknown archive type: $archive (tar.gz, tar.xz, tar.zst, zip, 7z…)" >&2
return 1
;;
esac
}
bu() {
# Резервна копія файлу з датою в назві | Back up a file with the date in its name
(($#)) || { __das_usage "bu ФАЙЛ..." "bu FILE..."; return; }
local f stamp
stamp=$(date +%Y%m%d%H%M%S)
for f in "$@"; do
cp -a -- "$f" "$f-$stamp.backup" && echo "$f-$stamp.backup"
done
}
# --- directories and files -----------------------------------------------------------
mkcd() {
# Створити каталог і перейти в нього | Make a directory and go into it
(($# == 1)) || { __das_usage "mkcd КАТАЛОГ" "mkcd DIR"; return; }
mkdir -p -- "$1" && cd -- "$1" || return
}
cl() {
# Перейти в каталог і показати його вміст | Change to a directory and list it
cd -- "${1:-$HOME}" && ls
}
up() {
# Піднятися на N каталогів вгору | Go N directories up
local n=${1:-1} dir=""
[[ $n =~ ^[0-9]+$ ]] || { __das_usage "up [N]" "up [N]"; return; }
while ((n-- > 0)); do dir+="../"; done
cd -- "${dir:-.}" || return
}
f() {
# Знайти файли за назвою (шаблон, без регістру) | Find files by name (a pattern, any case)
(($#)) || { __das_usage "f ШАБЛОН [КАТАЛОГ]" "f PATTERN [DIR]"; return; }
local pattern=$1
[[ $pattern == *[*?[]* ]] || pattern="*$pattern*"
find "${2:-.}" -iname "$pattern" 2>/dev/null
}
dirsize() {
# Розміри всього в каталозі, за зростанням | Sizes of everything in a directory, smallest first
local dir=${1:-.}
du -shx -- "$dir"/* "$dir"/.[!.]* 2>/dev/null | sort -h
}
whatsize() {
# Розміри підкаталогів (типово /) на тій самій ФС, за спаданням | Subdirectory sizes (/ by default) on the same filesystem, largest first
du -hx --max-depth=1 -- "${1:-/}" 2>/dev/null | sort -hr
}
bigfiles() {
# N найбільших файлів у каталозі | The N largest files in a directory
find "${1:-.}" -xdev -type f -printf '%s\t%p\n' 2>/dev/null | sort -nr | head -n "${2:-10}" |
numfmt --to=iec --field=1
}
ednt() {
# Редагувати файл, не змінюючи часу зміни | Edit a file keeping its modification time
(($# == 1)) || { __das_usage "ednt ФАЙЛ" "ednt FILE"; return; }
local mtime
mtime=$(stat -c %y -- "$1") || return
"${VISUAL:-${EDITOR:-vi}}" "$1"
touch -d "$mtime" -- "$1"
}
devid() {
# Пристрої: lsblk, blkid і lsusb разом | Devices: lsblk, blkid and lsusb at once
echo '--- lsblk ---'
lsblk -f -e 7
echo '--- blkid ---'
sudo blkid | grep -v '^/dev/loop'
echo '--- lsusb ---'
lsusb
}
# --- processes and history -------------------------------------------------------------
psa() {
# Процеси, в рядку яких є текст | Processes whose line contains the text
(($#)) || { __das_usage "psa ТЕКСТ" "psa TEXT"; return; }
ps aux | awk -v p="$1" 'NR == 1 || (tolower($0) ~ tolower(p) && !/awk -v p=/)'
}
pst() {
# Те саме деревом процесів | The same as a process tree
(($#)) || { __das_usage "pst ТЕКСТ" "pst TEXT"; return; }
# shellcheck disable=SC2009 # pgrep has no tree
ps auxf | grep -i -B 10 -A 5 -- "$1" | grep -v -e 'grep -i -B' -e 'ps auxf'
}
histat() {
# N найчастіших команд з історії | The N most used commands from history
history | awk -v n="${1:-10}" '
{ sub(/^ *[0-9]+\*? +/, ""); split($0, w, " "); c = w[1]; if (c != "") { cnt[c]++; total++ } }
END { for (c in cnt) printf "%d\t%.1f%%\t%s\n", cnt[c], cnt[c] * 100 / total, c }' |
sort -nr | head -n "${1:-10}" | nl
}
EC() {
# Код завершення попередньої команди | The previous command's exit code
local code=$?
printf '\e[1;33mcode %d\e[m\n' "$code"
return "$code"
}
please() {
# Повторити попередню команду через sudo | Run the previous command again with sudo
local cmd
cmd=$(fc -ln -1)
cmd=${cmd#"${cmd%%[![:space:]]*}"}
echo "sudo $cmd" >&2
sudo bash -c "$cmd"
}
# --- packages (xbps) --------------------------------------------------------------------
pkgshow() {
# Встановлені пакети, з фільтром за назвою | Installed packages, filtered by name
xbps-query -l | awk '{print $2}' | grep -i -- "${1:-.}" | sort
}
pkgfind() {
# Шукати пакети в репозиторіях | Search the repositories for packages
(($#)) || { __das_usage "pkgfind ТЕКСТ" "pkgfind TEXT"; return; }
xbps-query -Rs "$1"
}
pkgowner() {
# Якому пакету належить файл чи команда | Which package owns a file or a command
(($#)) || { __das_usage "pkgowner ФАЙЛ|КОМАНДА" "pkgowner FILE|COMMAND"; return; }
local path=$1
[[ $path == */* ]] || path=$(type -P -- "$1") || path=$1
xbps-query -o "$(readlink -f -- "$path")"
}
# --- network -----------------------------------------------------------------------------
myip() {
# Зовнішня IP-адреса | The public IP address
curl -fsS --max-time 5 https://ifconfig.me/ip && echo
}
netinfo() {
# Адреси, шлюз, DNS і зовнішня IP | Addresses, gateway, DNS and the public IP
echo "--- $(__das_say "Адреси" "Addresses") ---"
command ip -br address | grep -v '^lo '
echo "--- $(__das_say "Шлюз" "Gateway") ---"
command ip route show default
echo "--- DNS ---"
awk '/^nameserver/ {print $2}' /etc/resolv.conf
echo "--- $(__das_say "Зовнішня IP" "Public IP") ---"
myip
}
listen() {
# Слухати порт TCP і показувати, що прийшло | Listen on a TCP port and print what arrives
(($# == 1)) || { __das_usage "listen ПОРТ" "listen PORT"; return; }
__das_need nc openbsd-netcat || return
nc -lvn "$1"
}
rmhost() {
# Прибрати хост або рядок N з ~/.ssh/known_hosts | Remove a host or line N from ~/.ssh/known_hosts
(($# == 1)) || { __das_usage "rmhost ХОСТ|НОМЕР_РЯДКА" "rmhost HOST|LINE_NUMBER"; return; }
if [[ $1 =~ ^[0-9]+$ ]]; then
sed -i.bak "${1}d" ~/.ssh/known_hosts && echo "$HOME/.ssh/known_hosts.bak"
else
ssh-keygen -R "$1"
fi
}
serve() {
# Роздати поточний каталог по HTTP (типово порт 8000) | Serve the current directory over HTTP (port 8000 by default)
__das_need python3 python3 || return
python3 -m http.server "${1:-8000}"
}
weather() {
# Погода з wttr.in (місто з аргументу або за IP) | Weather from wttr.in (the city given, or by IP)
local lang=en
__das_uk && lang=uk
curl -fsS --max-time 10 "https://wttr.in/${1// /+}?lang=$lang"
}
cheat() {
# Шпаргалка до команди з cheat.sh | A command's cheat sheet from cheat.sh
(($#)) || { __das_usage "cheat КОМАНДА" "cheat COMMAND"; return; }
curl -fsS --max-time 10 "https://cheat.sh/$1"
}
# --- small tools -------------------------------------------------------------------------
calc() {
# Порахувати вираз | Calculate an expression
(($#)) || { __das_usage "calc ВИРАЗ" "calc EXPRESSION"; return; }
if command -v bc >/dev/null; then
echo "scale=3; $*" | bc -l
else
awk "BEGIN { print $* }"
fi
}
epoch() {
# Поточний час Unix або перетворити його на дату | The current Unix time, or turn one into a date
if (($#)); then
date -d "@$1" -R
else
date +%s
fi
}
genpass() {
# Випадковий пароль (типово 20 символів) | A random password (20 characters by default)
local n=${1:-20}
[[ $n =~ ^[0-9]+$ ]] || { __das_usage "genpass [ДОВЖИНА]" "genpass [LENGTH]"; return; }
LC_ALL=C tr -dc 'A-Za-z0-9_@%+=.,-' </dev/urandom | head -c "$n"
echo
}
csrc() {
# Скомпілювати й запустити програму на C | Compile and run a C source file
(($#)) || { __das_usage "csrc ФАЙЛ.c [АРГУМЕНТИ]" "csrc FILE.c [ARGS]"; return; }
[[ -r $1 ]] || { __das_say "немає файлу $1" "no such file: $1" >&2; return 1; }
__das_need gcc gcc || return
local src=$1 bin rc
shift
bin=$(mktemp) || return
gcc "$src" -o "$bin" && "$bin" "$@"
rc=$?
rm -f -- "$bin"
return $rc
}
note() {
# Нотатки в ~/.notes: без аргументів показати, -c очистити | Notes in ~/.notes: no arguments shows them, -c clears
local file=$HOME/.notes
if ! (($#)); then
[[ -f $file ]] && cat -- "$file"
elif [[ $1 == -c ]]; then
: >"$file"
else
printf '%s\n' "$*" >>"$file"
fi
}
todo() {
# Справи в ~/.todo: -l з номерами, -r N виконано, -c очистити | To-dos in ~/.todo: -l numbered, -r N done, -c clears
local file=$HOME/.todo
case ${1:-} in
"") [[ -f $file ]] && cat -- "$file" ;;
-l) [[ -f $file ]] && nl -b a -- "$file" ;;
-c) : >"$file" ;;
-r)
local n=${2:-}
if [[ -z $n ]]; then
nl -b a -- "$file"
read -r -p "$(__das_say "Номер, що прибрати: " "Number to remove: ")" n
fi
[[ $n =~ ^[0-9]+$ ]] || return 1
sed -i "${n}d" -- "$file"
;;
*) printf '%s\n' "$*" >>"$file" ;;
esac
}
man() {
# man з кольоровими заголовками | man with coloured headings
LESS_TERMCAP_mb=$'\e[1;31m' LESS_TERMCAP_md=$'\e[1;31m' LESS_TERMCAP_me=$'\e[0m' \
LESS_TERMCAP_so=$'\e[1;44;33m' LESS_TERMCAP_se=$'\e[0m' \
LESS_TERMCAP_us=$'\e[1;32m' LESS_TERMCAP_ue=$'\e[0m' \
GROFF_NO_SGR=1 command man "$@"
}
@@ -0,0 +1,54 @@
import os
import subprocess
import tempfile
import unittest
from tests.common import FILES, SCRATCH
ALIASES = os.path.join(FILES, "shell", "aliases.sh")
FUNCTIONS = os.path.join(FILES, "shell", "functions.sh")
def bash(script, cwd=None):
"""Runs script in bash with the Das OS aliases and functions loaded."""
full = f"shopt -s expand_aliases\n. {ALIASES}\n. {FUNCTIONS}\n{script}"
return subprocess.run(["bash", "--norc", "-c", full], cwd=cwd, capture_output=True, text=True,
env={**os.environ, "HOME": SCRATCH})
class ShellTest(unittest.TestCase):
def test_syntax(self):
for path in (ALIASES, FUNCTIONS, os.path.join(FILES, "bashrc.d", "das-aliases.sh")):
self.assertEqual(subprocess.run(["bash", "-n", path]).returncode, 0, path)
def test_every_function_has_a_description(self):
out = bash("lsfunc").stdout.splitlines()
self.assertIn("extract", [line.split()[0] for line in out])
for line in out:
name, _, descr = line.partition(" ")
self.assertTrue(descr.strip(), f"{name} has no description")
self.assertNotIn(" | ", descr, name)
def test_descriptions_follow_the_language(self):
out = bash("LANG=uk_UA.UTF-8 lsfunc | grep '^mkcd '").stdout
self.assertIn("Створити каталог", out)
def test_compress_and_extract(self):
with tempfile.TemporaryDirectory(dir=SCRATCH) as d:
r = bash("mkdir -p src/sub && echo hi >src/sub/x.txt && compress a.tar.gz src >/dev/null &&"
" mkdir out && cd out && extract ../a.tar.gz >/dev/null && cat src/sub/x.txt", cwd=d)
self.assertEqual(r.stdout, "hi\n", r.stderr)
r = bash("extract nope.zip", cwd=d)
self.assertEqual(r.returncode, 1)
def test_up_and_usage(self):
with tempfile.TemporaryDirectory(dir=SCRATCH) as d:
r = bash("mkcd a/b/c && up 2 && pwd", cwd=d)
self.assertEqual(r.stdout.strip(), os.path.join(os.path.realpath(d), "a"))
r = bash("up x")
self.assertEqual(r.returncode, 2)
self.assertIn("usage: up", r.stderr)
if __name__ == "__main__":
unittest.main()
+7 -3
View File
@@ -1,17 +1,18 @@
# Template file for 'das-shell'
# In-tree package: sources are in files/ (FILESDIR).
pkgname=das-shell
version=0.2.0
version=0.3.0
revision=1
create_wrksrc=yes
hostmakedepends="python3"
checkdepends="bash"
depends="bash python3 python3-textual figlet"
short_desc="Das OS terminal: bash prompt, dprompt and dbanner (SSH login banner)"
short_desc="Das OS terminal: bash prompt, aliases, dprompt and dbanner (SSH banner)"
maintainer="Das <anry.das@gmail.com>"
license="MIT"
homepage="https://git.das.org.ua/das/Das-OS"
conf_files="/etc/bash/bashrc.d/das-prompt.sh /etc/profile.d/das-banner.sh"
conf_files="/etc/bash/bashrc.d/das-prompt.sh /etc/bash/bashrc.d/das-aliases.sh
/etc/profile.d/das-banner.sh"
make_dirs="/var/cache/das/banner 0755 root root"
pycompile_dirs="usr/lib/das/python"
python_version=3
@@ -29,6 +30,9 @@ do_check() {
do_install() {
local f
vinstall "${FILESDIR}/bashrc.d/das-prompt.sh" 644 etc/bash/bashrc.d
vinstall "${FILESDIR}/bashrc.d/das-aliases.sh" 644 etc/bash/bashrc.d
vinstall "${FILESDIR}/shell/aliases.sh" 644 usr/share/das/shell
vinstall "${FILESDIR}/shell/functions.sh" 644 usr/share/das/shell
vinstall "${FILESDIR}/profile.d/das-banner.sh" 644 etc/profile.d
vinstall "${FILESDIR}/prompt-lib.sh" 644 usr/lib/das
vinstall default-prompt.sh 644 usr/share/das/prompt default.sh