X-Git-Url: https://iankelling.org/git/?p=distro-setup;a=blobdiff_plain;f=brc;h=08ad4067ce22bdb7d4742cbeb8aa0e8d7ba1d8c7;hp=b78e08ff865d1779f3a3065dadb483f4496821c7;hb=HEAD;hpb=c300392f92092b3a89281df462d42710f25d0351 diff --git a/brc b/brc index b78e08f..7731b9c 100644 --- a/brc +++ b/brc @@ -1,22 +1,41 @@ #!/bin/bash -# Copyright (C) 2019 Ian Kelling -# SPDX-License-Identifier: AGPL-3.0-or-later +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to switch +# its license to GPL. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # this gets sourced. shebang is just for file mode detection # Use source ~/.bashrc instead of doing bash -l when running a script # so this can set extdebug and avoid the bash debugger. -if [[ -s /a/bin/errhandle/err ]]; then - # shellcheck source=/a/bin/errhandle/err - source /a/bin/errhandle/err +if [[ -s /a/bin/bash-bear-trap/bash-bear ]]; then + # shellcheck source=/a/bin/bash-bear-trap/bash-bear + source /a/bin/bash-bear-trap/bash-bear # wtf, shellcheck doesn't allow disabling warnings in elifs else # bleh shellcheck can't handle disabling in an elif, so nesting this if. # shellcheck disable=SC2154 # set in .bashrc - if [[ -s $bashrc_dir/err ]]; then - # shellcheck source=/a/bin/errhandle/err - source $bashrc_dir/err + if [[ -s $bashrc_dir/bash-bear ]]; then + # shellcheck source=/a/bin/bash-bear-trap/bash-bear + source $bashrc_dir/bash-bear fi fi @@ -128,19 +147,79 @@ fi export SSH_CONFIG_FILE_OVERRIDE=/root/.ssh/confighome + + # emacs has a different default search path than the info command. This -# adds the info defaults to emacs, but not the reverse, because I dun +# adds the info defaults to emacs. This is commented because after +# various upgrades this is no longer a problem: for the directories that +# exist on my system, emacs already includes the ones that info +# searches. +# +# but not the reverse, because I dun # care much about the cli. The search path is only on the cli if you run # "info xxx", or in emacs if you run '(info xxx)', so not that -# important, but might as well fix it. +# important and i don't bother fixing it. + +# # info info says this path is what was compiled, and its not documented +# # anywhere. Through source grepping, i found it in files.h of the info +# # source in trisquel flidas. +# # +# # Trailing : means for emacs to add its own stuff on to the end. +# # +# # A problem with this is that directories which are not readable breaks info. And of course, this hard coding is not nice. +# # I removed PATH from the start, because I've never seen an info file in PATH. And removed ".", because I can just specify the full file name in that case. +# # +# # https://raw.githubusercontent.com/debian-tex/texinfo/master/info/filesys.h +# # + +# # note: to split up the var like this, do: +# # IFS=:; printf '%s\n' $INFOPATH + +# dirs=( +# /usr/local/info +# /usr/info +# /usr/local/lib/info +# /usr/lib/info +# /usr/local/gnu/info +# /usr/local/gnu/lib/info +# /usr/gnu/info +# /usr/gnu/lib/info +# /opt/gnu/info +# /usr/share/info +# /usr/share/lib/info +# /usr/local/share/info +# /usr/local/share/lib/info +# /usr/gnu/lib/emacs/info +# /usr/local/gnu/lib/emacs/info +# /usr/local/lib/emacs/info +# /usr/local/emacs/info +# ) + +# for d in ${dirs[@]}; do +# if [[ -r $d ]]; then +# INFOPATH="$d:$INFOPATH" +# fi +# done +# unset d dirs + + +# note: guix bash config does this automatically. +if [[ $INFOPATH != *: ]]; then + INFOPATH="$INFOPATH:" +fi -# info info says this path is what was compiled, and its not documented -# anywhere. Through source grepping, i found it in filesys.h of the info -# source in trisquel flidas. +# info parameter expansion # -# Traling : means for emacs to add its own stuff on to the end. +# info cheat sheet: +# H: see keybinds +# / search, {, }: next/prev match +# ctrl/alt-v scroll forward/backward within this node +# l: go to previous node +# +info-pe() { + info bash 'Basic Shell Features' 'Shell Expansions' 'Shell Parameter Expansion' +} -export INFOPATH=$PATH:/usr/local/info:/usr/info:/usr/local/lib/info:/usr/lib/info:/usr/local/gnu/info:/usr/local/gnu/lib/info:/usr/gnu/info:/usr/gnu/lib/info:/opt/gnu/info:/usr/share/info:/usr/share/lib/info:/usr/local/share/info:/usr/local/share/lib/info:/usr/gnu/lib/emacs/info:/usr/local/gnu/lib/emacs/info:/usr/local/lib/emacs/info:/usr/local/emacs/info:.: # for openwrt system that has no stty, this is easier than # guarding every time i use it. @@ -169,30 +248,12 @@ if [[ $- == *i* ]]; then TERM=xterm-256color fi - if [[ $TERM == alacritty && ! -e /usr/share/terminfo/a/alacritty ]]; then - # todo: we should try installing the alacritty terminfo if it is not found - # https://github.com/alacritty/alacritty/issues/2838 - TERM=xterm-256color - fi - - # copying from the alacritty example above, - if [[ $TERM == xterm-kitty ]]; then - if [[ ! -e /usr/share/terminfo/x/xterm-kitty ]]; then - TERM=xterm-256color - else - if [[ -e /a/opt/kitty/shell-integration/bash/kitty.bash ]]; then - KITTY_SHELL_INTEGRATION=t - source /a/opt/kitty/shell-integration/bash/kitty.bash - fi - fi - fi - # todo: not sure this works in sakura #stty werase undef #bind "\C-w": kill-region # sakura == xterm-256color # konsole == xterm - if [[ $TERM != xterm-kitty && $TERM == xterm* ]]; then + if [[ $TERM == xterm* ]]; then # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash bind '"\e[1;5C": shell-forward-word' 2>/dev/null bind '"\e[1;5D": shell-backward-word' 2>/dev/null @@ -212,15 +273,6 @@ if [[ $- == *i* ]]; then fi -case $TERM in - # fixup broken backspace in chroots - xterm-kitty|alacritty) - chroot() { - TERM=xterm-256color command chroot "$@" - } - ;; -esac - export BC_LINE_LENGTH=0 # ansible option @@ -236,12 +288,32 @@ export PROFILE_TASKS_TASK_OUTPUT_LIMIT=100 export LESS=RXij12 export SYSTEMD_LESS=$LESS + export NNN_COLORS=2136 export SL_FILES_DIR=/b/ds/sl/.iank export SL_INFO_DIR=/p/sshinfo +### begin pyenv ### + +# this is adapted from things printed to term after install +# pyenv. commented for now since I'm not actually using pyenv. + +# export PYENV_ROOT="$HOME/.pyenv" +# command -v pyenv &>/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" +# command -v pyenv &>/dev/null && eval "$(pyenv init -)" + + +# output showed this example for pyenv-virtualenv, which i have no idea +# what it is, but leaving it as a comment in case I end up doing python +# dev. + +#eval "$(pyenv virtualenv-init -)" +### end begin pyenv ### + + + # * include files if [[ -s $bashrc_dir/path-add-function ]]; then @@ -263,11 +335,14 @@ fi # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory # bash: warning: cannot start debugger; debugging mode disabled if [[ $SOE ]]; then - if [[ -e /a/bin/errhandle/err ]]; then - source /a/bin/errhandle/err + if [[ -e /a/bin/bash-bear-trap/bash-bear ]]; then + source /a/bin/bash-bear-trap/bash-bear fi fi +# go exists here +path-add --ifexists /usr/local/go/bin + mysrc() { local path dir file @@ -300,6 +375,16 @@ mysrc /a/bin/distro-functions/src/package-manager-abstractions # * functions + +# temporary functions +y() { + m "${@//spring/fall}" +} +h() { + e "${@//spring/fall}" +} + + ### begin FSF section ### # Comments before functions are meant to be good useful @@ -307,10 +392,12 @@ mysrc /a/bin/distro-functions/src/package-manager-abstractions # note. ## copy bash completion -# Usage: ORIGINAL_COMMAND TARGET_COMMAND... # # It copies how the bash completion works from one command to other -# commands. +# commands. Generally just use within a .bashrc. +# +# Usage: ORIGINAL_COMMAND TARGET_COMMAND... +# ccomp() { local c src src=$1 @@ -325,17 +412,21 @@ ccomp() { eval $c $* } -## directory history tracking and navigation. +## BEGIN functions to change directory better than cd ## +# +# The functions: # -# cd becomes a function, also aliased to c. b to go back, f to go -# forward, cl to list recent directories and choose one. +# c: acts like cd, but stores directory history: you could alias to cd if you wanted. +# b: go back +# f: go forward +# cl: list recent directories and optionally choose one. # -# The finer details you may want to skip: +# Finer details you may want to skip: # -# We also define bl to print the list of back and forward directories. +# bl: print the list of back and forward directories. # -# We keep 2 stacks, forward and back. Unlike with a web browser, the -# forward stack is not erased when going somewhere new. +# We keep 2 stacks of directories, forward and back. Unlike with a web +# browser, the forward stack is not erased when going somewhere new. # # Recent directories are stored in ~/.cdirs. # @@ -437,7 +528,7 @@ f() { # printf "%s\n" "${_dir_forward[-1]}" # fi } -# cd list +# cl = cd list cl() { local i line input start local -A buttondirs alines @@ -467,7 +558,9 @@ cl() { alines[$line]=t buttondirs[${buttons[i]}]="$line" printf "%s %s\n" ${buttons[i]} "$line" - if (( i == ${#buttons[@]} - 1 )); then + # the LINES bit is for when we have a short terminal, just dont print all + # the directories. alternative would be to do something like less the list. + if (( i == ${#buttons[@]} - 1 )) || { [[ $LINES ]] && (( i == LINES - 3 )); }; then break fi i=$(( i + 1 )) @@ -482,7 +575,8 @@ cl() { c "${buttondirs[$input]}" fi } -# back list +# bl = back list. lists the back and forward directories. i tend to +# forget this exists and use cl instead. bl() { local start i j max max=10 @@ -523,6 +617,22 @@ bl() { fi done } +# like running cl a +cla() { + local line + mapfile -t lines <~/.cdirs + start=$(( ${#lines[@]} - 1 )) + for (( j=start; j >= 0; j-- )); do + line="${lines[$j]}" + if [[ ! $line || ! -d "$line" || $line == "$PWD" || line == "$HOME" ]]; then + continue + fi + e "$line" + c "$line" + break + done +} +## END functions to change directory better than cd ## # pee do. run args as a command with output copied to syslog. # @@ -594,6 +704,76 @@ jdo() { (( ret == 0 )) || return $ret } ccomp time jdo + +# standard date as used in logs +datelog() { + date +%Y-%m-%d "$@" +} + +# date in log appropriate format +dtl() { + date "+%F %T" "$@" +} + +# ts formatted +tsf() { + command ts "%F %T" "$@" +} + +# ts log. log command to log file. +# usage: tsl LOG_PATH_PREFIX COMMAND... +# example: tsl /root/command +# log file will be like /root/command-2024-02-10.log +tsl() { + local log_prefix log_path appending ret + if (( $# < 2 )); then + echo "tsl: error: expected >= 2 arguments, got $#" >&2 + return 1 + fi + log_prefix="$1" + if [[ $log_prefix == */* && ! -d ${log_prefix%*/} ]]; then + echo "tsl: error: expected directory at ${log_prefix%*/}" >&2 + return 1 + fi + log_path=$log_prefix-$(date +%Y-%m-%d).log + appending=false + if [[ -s $log_path ]]; then + appending=true + fi + shift + printf "%s\n" "CWD: $PWD, log: $log_path, running $*" | ts "%F %T" | tee -a "$log_path" + ret=0 + "$@" |& ts "%F %T" | tee -a "$log_path" || ret=$? + printf "%s\n" "exit code $ret from command: $*" | ts "%F %T" | tee -a "$log_path" + if $appending; then + printf "%s\n" "note: this log file contains logs before those of previous command" | ts "%F %T" | tee -a "$log_path" + fi +} + +disk-info() { + local cmds cmd + mapfile -t cmds <<'EOF' +tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab +lsblk +blkid +ls -la /dev/disk/by-id +EOF + + for cmd in "${cmds[@]}"; do + cat <$tmp || [[ $? == 1 ]] # 1 when it doesnt exist in the file + if [[ -s $tmp ]]; then + key=$(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp) + fi + rm $tmp if [[ $key ]]; then grep -Fv "$key" "$file" | sponge "$file" fi + key= fi - key=$(ssh-keygen -F "$ip_entry" -f $file | sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/') + tmp=$(mktemp) + ssh-keygen -F "$ip_entry" -f $file >$tmp || [[ $? == 1 ]] + if [[ -s $tmp ]]; then + key=$(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp) + fi + rm $tmp if [[ $key ]]; then grep -Fv "$key" "$file" | sponge "$file" fi ll ~/.ssh/known_hosts - rootsshsync } -khfix() { # known hosts fix - _khfix_common "$@" || return 1 +khfix-r() { # known hosts fix without syncing to root user + _khfix-common "$@" || return 1 ssh $1 : } -khcopy() { - _khfix_common "$@" - ssh-copy-id $1 -} - -# ya, hacky hardcoded hostnames in 2023. we could do better -hssh-update() { - local -a failed_hosts hosts - case $HOSTNAME in - sy|kd) - hosts=( - kd x3.office.fsf.org syw - ) - ;; - x3) - hosts=( - b8.nz sywg.b8.nz - ) - ;; - esac - for host in ${hosts[@]}; do - e $host - if ! scp /b/fai/fai/config/files/usr/local/bin/hssh/IANK root@$host:/usr/local/bin/hssh; then - failed_hosts+=($host) - fi - done - if (( ${#failed_hosts[@]} >= 1 )); then - echo failed_hosts=${failed_hosts[*]} - return 1 - fi +khfix() { + _khfix-common "$@" || return 1 + ssh $1 : + rootsshsync } +# copy path into clipboard a() { local x x=$(readlink -nf "${1:-$PWD}") - # yes, its kinda dumb that xclip/xsel cant do this in one invocation - echo -n "$x" | xclip -selection clipboard - echo -n "$x" | xclip + # yes, its kinda dumb that xclip/xsel cant do this in one invocation. + # And, summarizing this: + # https://askubuntu.com/questions/705620/xclip-vs-xsel + # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions. + cbs "$x" +} + +# clipboard a string (into selection & clipboard buffer) +cbs() { + # yes, its kinda dumb that xclip/xsel cant do this in one invocation. + # And, summarizing this: + # https://askubuntu.com/questions/705620/xclip-vs-xsel + # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions. + printf "%s" "$*" | xclip -selection clipboard + printf "%s" "$*" | xclip } # a1 = awk {print $1} @@ -712,7 +889,7 @@ for field in {1..20}; do done # h1 = head -n1 for num in {1..9}; do - eval h$num"() { head -n$num; }" + eval h$num"() { head -n$num || [[ \$? == 141 ]]; }" done @@ -804,12 +981,17 @@ cf() { done } caf() { - # shellcheck disable=SC2033 + + local file find -L "$@" -type f -not \( -name .svn -prune -o -name .git -prune \ -o -name .hg -prune -o -name .editor-backups -prune \ - -o -name .undo-tree-history -prune \) \ - -exec bash -c '. ~/.bashrc; hr; echo "$1"; hr; cat "$1"' _ {} \; 2>/dev/null - + -o -name .undo-tree-history -prune \) -printf '%h\0%d\0%p\n' | sort -t '\0' -n \ + | awk -F '\0' '{print $3}' 2>/dev/null | while read -r file; do + hr + printf "%s\n" "$file" + hr + cat "$file" + done } ccomp cat cf caf @@ -821,6 +1003,10 @@ clc() { echo "scale=3; $x" | bc -l } +cx() { + chmod +X "$@" +} + cam() { git commit -am "$*" } @@ -1204,6 +1390,7 @@ fa() { done < <(find "$@" -print0); } +# shellcheck disable=SC2120 faf() { # find all files. use -L to follow symlinks find "$@" -not \( -name .svn -prune -o -name .git -prune \ -o -name .hg -prune -o -name .editor-backups -prune \ @@ -1369,9 +1556,6 @@ and works in older versions of git which did not have that." g() { - # todo: patch emacs so it will look elsewhere. this is kinda sad: - # https://emacs.stackexchange.com/questions/4253/how-to-start-emacs-with-a-custom-user-emacs-directory - local args gdb=false if [[ $EMACSDIR ]]; then @@ -1394,6 +1578,10 @@ g() { fi fi if [[ $EMACSDIR ]]; then + + # todo: we don't have to alter HOME since emacs 29+, we can set + # user-emacs-directory with the flag --init-directory + # Alter the path here, otherwise the nfs mount gets triggered on the # first path lookup when emacs is not being used. # shellcheck disable=SC2098 disable=SC2097 # false positive @@ -1414,6 +1602,28 @@ g() { fi } +# g pipe. like: cmd | emacs. save cmd output to tmp file, then edit. +gp() { + cat &>/a/tmp/gtmp + g "$@" /a/tmp/gtmp +} +# g log +#like cmd &> tempfile; emacs tempfile +# +# note: a useful workflow for doing mass replace on my files: +# gc rem REGEX +## remove any false positives, or manually edit them. rename files if needed. +# sedi 's/REGEX/REPLACEMENT/' $(gr '^/' /a/tmp/gtmp) +gl() { + "$@" &> /a/tmp/gtmp + g /a/tmp/gtmp +} +# g command substitution. +gc() { + # shellcheck disable=SC2046 # i want word splitting for this hackery + g $("$@") +} + # force terminal version gn() { g -n "$@" @@ -1481,7 +1691,86 @@ hlm() { hl "$*"; "$@"; } hrcat() { local f; for f; do [[ -f $f ]] || continue; hr; echo "$f"; cat "$f"; done } +# example usage: +# github-release-dl restic/restic restic_ _linux_amd64.bz2 +# gets a url like: +# https://github.com/restic/restic/releases/download/v0.16.3/restic_0.16.3_linux_amd64.bz2 +github-release-dl() { + local github_path file_prefix file_suffix latest_prefix version redir_path + github_path=$1 + file_prefix=$2 + file_suffix=$3 + if (( $# != 3 )); then + echo "$0: error, expected 3 arguments" >&2 + return 1 + fi + redir_path="https://github.com/$github_path/releases/latest/download/" + latest_prefix=$(curl -s -I "$redir_path" | awk 'tolower($1) == "location:" {print $2}') + # it has a trailing /r at the end. just kill any whitespace. + latest_prefix="${latest_prefix//[$'\t\r\n ']}" + if [[ ! $latest_prefix ]]; then + echo "failed to find latest path. Tried to find case insensitive 'location:' in the curl output:" + m curl -s -I "$redir_path" + return 1 + fi + version="${latest_prefix##*/}" + version="${version#v}" + m wget -- "$latest_prefix/$file_prefix$version$file_suffix" +} + +# examples. +# go-github-install restic/restic restic_ _linux_amd64.bz2 +# go-github-install restic/rest-server rest-server_ _linux_amd64.tar.gz + +# common pattern among go binaries on github +go-github-install() { + local tmpd targetf tmp files src + tmpd=$(mktemp -d) + cd $tmpd + file_prefix=$2 + file_suffix=$3 + tmp="${file_prefix##*[[:alnum:]]}" + targetf="${file_prefix%"$tmp"}" + echo targetf: $targetf + github-release-dl "$@" + files=(./*) + case $file_suffix in + *.bz2) + bunzip2 -- ./* + ;; + *.tar.gz|*.tgz) + tar -vxzf ./* + ;; + esac + rm -f -- "${files[@]}" + files=(./*) + # Here we detect and handle 2 cases: either we extracted a single + # binary which we have to rename or a folder with a binary named + # $targetf in it which is all we care about. + if (( ${#files[@]} == 1 )) && [[ -f ${files[0]} ]]; then + chmod +x ./* + mv -- ./* /usr/local/bin/$targetf + else + files=(./*/$targetf) + if [[ -f $targetf ]]; then + src=$targetf + elif [[ -f ${files[0]} ]]; then + src="${files[0]}" + fi + chmod +x "$src" + mv -- "$src" /usr/local/bin + fi + cd - >/dev/null + rm -rf $tmpd +} +## 2024: I'm using gh instead of hub, but leaving this just in case. +## I tried the github cli tool (gh) and it seems easier than +## I remember hub. +## +## hub predated github's 2020 official cli tool gh. +## more info at +## https://raw.githubusercontent.com/cli/cli/trunk/docs/gh-vs-hub.md # get latest hub and run it # main command to use: # hub pull-request --no-edit @@ -1590,6 +1879,8 @@ pst() { pstree -apnA } +# journalctl with times in the format the --since= and --until= options accept +jrt() { journalctl -e -n100000 -o short-full "$@"; } jr() { journalctl -e -n100000 "$@" ; } jrf() { journalctl -n1000 -f "$@" ; } jru() { @@ -1749,12 +2040,28 @@ mkdir() { command mkdir -p "$@"; } nags() { # https://github.com/HenriWahl/Nagstamon/issues/357 - if ! pgrep -f /usr/lib/notification-daemon/notification-daemon >/dev/null; then - /usr/lib/notification-daemon/notification-daemon & + if ! pgrep -f /usr/bin/dunst >/dev/null; then + /usr/bin/dunst & fi /usr/bin/nagstamon & } +# profanity screen +profsrc() { + screen -RD -S profanity +} + +# i dont want to wait for konsole to exit... +prof() { + command prof &>/dev/null & +} +# self chat +sc() { + while read -r l; do + printf '\033[1A\033[K'; printf "%s\n" "$l"| ts "%F %T" | tee -a /p/self-chat.log + done +} + nmt() { # cant use s because sudo -i doesnt work for passwordless sudo command case $EUID in @@ -1806,15 +2113,15 @@ pkx() { # package extract c "$(mktemp -d)" pkg=$1 # shellcheck disable=SC2012 - cached=$(ls -t /var/cache/apt/archives/$pkg* | tail -n1 2>/dev/null) ||: + cached=$(ls -t /var/cache/apt/archives/${pkg}_* 2>/dev/null | tail -n1 2>/dev/null) ||: if [[ $cached ]]; then - cp $cached . + m cp $cached . else - aptitude download $pkg || return 1 + m aptitude download $pkg || return 1 fi tmp=(*); f=${tmp[0]} # only 1 expected - ex $f - rm -f $f + m ex $f + m rm -f $f } # pgrep and kill @@ -1990,6 +2297,18 @@ sedi() { sed -i --follow-symlinks "$@" } + + +# todo: test variable assignment with newlines here. +# https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash + +# beware that it only works on the assumption that any special +# characters in the input string are intended to be escaped, not to work +# as special chacters. +shellescape() { + LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' +} + rmstrips() { ssh fencepost head -n 300 /gd/gnuorg/EventAndTravelInfo/rms-current-trips.txt | less } @@ -2118,18 +2437,24 @@ sgu() { systemctl list-unit-files | rg "$@" } +# check whether we generally want to do sk on the file +sk-p() { + [[ ! -L $f ]] && istext "$1" && [[ $(head -n1 "$1" 2>/dev/null) == '#!/bin/bash'* ]] +} sk() { - # disable a warning with: - # shellcheck disable=SC2206 # reasoning - - # see bash-template/style-guide.md for justifications - - local quotes others + # see https://savannah.gnu.org/maintenance/fsf/bash-style-guide/ for justifications + local quotes others ret quotes=2048,2068,2086,2206,2254 - others=2029,2033,2054,2164 - shellcheck -W 999 -x -e $quotes,$others "$@" || return $? + others=2029,2032,2033,2054,2164 + shellcheck -x -W 999 -e $quotes,$others "$@" || ret=$? + if (( ret >= 1 )); then + echo "A template comment to disable is now in clipboard. eg: # shellcheck disable=SC2206 # reason" + cbs "# shellcheck disable=SC" + return $ret + fi } + # sk with quotes. For checking scripts that we expect to take untrusted # input in order to verify we quoted vars. skq() { @@ -2138,15 +2463,41 @@ skq() { shellcheck -W 999 -x -e $others "$@" || return $? } -skgit() { +# sk on all modified files in current git repo +skmodified() { local f for f in $(i s | awk '$1 == "modified:" {print $2}'); do - if [[ $(head -n1 "$f") == '#!/bin/bash'* ]]; then - sk $f + if sk-p "$f"; then + sk $f ||: fi done } + +# sk on all the files in current git repo +skgit() { + local f toplevel orig_dir tmp + local -a ls_files sk_files + toplevel=$(git rev-parse --show-toplevel) + if [[ $PWD != "$toplevel" ]]; then + orig_dir=$PWD + cd $toplevel + fi + # tracked & untracked files + tmp=$(git ls-files && git ls-files --others --exclude-standard) + mapfile -t ls_files <<<"$tmp" + for f in "${ls_files[@]}"; do + if sk-p "$f"; then + sk_files+=("$f") + fi + done + sk "${sk_files[@]}" + if [[ $orig_dir ]]; then + cd $orig_dir + fi +} + + # sl: ssh, but firsh rsync our bashrc and related files to a special # directory on the remote host if needed. @@ -2391,11 +2742,7 @@ ssk() { ccomp ssh sl slr sss ssk # plain ssh ssh() { - if [[ $TERM == alacritty || $TERM == xterm-kitty ]]; then - TERM=xterm-256color LC_USEBASHRC=t command ssh "$@" - else - LC_USEBASHRC=t command ssh "$@" - fi + LC_USEBASHRC=t command ssh "$@" } @@ -2514,7 +2861,7 @@ psoff() { pson() { PROMPT_COMMAND=(prompt-command) if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then - trap 'settitle "$BASH_COMMAND"' DEBUG + trap 'auto-window-title "$BASH_COMMAND"' DEBUG fi } @@ -2554,6 +2901,7 @@ nonet() { } m() { printf "%s\n" "$*"; "$@"; } +m2() { printf "%s\n" "$*" >&2; "$@"; } # update file. note: duplicated in mail-setup. # updates $ur u result to true or false @@ -2618,14 +2966,19 @@ vmunshare() { } myiwscan() { - # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines. - # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h} - sudo iw dev wls1 scan | sed -rn " + local i + interfaces=$(iw dev | awk '$1 == "Interface" {print $2}') + for i in $interfaces; do + echo "myiwscan: considering $i" + # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines. + # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h} + sudo iw dev $i scan | sed -rn " s/^\Wcapability: (.*)/\1/;Ta;h;b :a;s/^\Wsignal: -([^.]+).*/\1/;Tb;H;b # padded to min width of 20 :b;s/\WSSID: (.*)/\1 /;T;s/^(.{20}(.*[^ ])?) */\1/;H;g;s/(.*)\n(.*)\n(.*)/\2 \3 \1/gp;b "|sort -r + done } # Run script by copying it to a temporary location first, @@ -2732,14 +3085,114 @@ leap-year() { # on-battery on-bat() { if [[ -e /sys/class/power_supply/AC/online && $(/dev/null; then + echo "$0: error: missing dependency: sudo apt install moreutils" >&2 + return 1 + fi + + for f; do + echo "adding header to $f" + if [[ -s $f ]]; then + f_maybe=("$f") + else + f_maybe=() + fi + cat - "${f_maybe[@]}" < + +Copyright (C) $(date +%Y) Free Software Foundation + +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without any warranty. + +Contributions are welcome. See . + +EOF + done +} + +# note, there is also the tool gron which is meant for this, but +# this is good enough to not bother installing another tool +jq-lines() { + # https://stackoverflow.com/questions/59700329/how-to-print-path-and-key-values-of-json-file-using-jq + jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' "$@" +} + +tsr() { # ts run + "$@" |& ts || return $? +} + + # * misc stuff if $use_color && type -p tput &>/dev/null; then + # this is nice for a dark background terminal: + # https://github.com/trapd00r/LS_COLORS + # I would like if there was something similar for light. + + # https://www.bigsoft.co.uk/blog/2008/04/11/configuring-ls_colors + # change the hard to read turqouise. + # defaults dircolors --print-database. + + # the default bold green is too light. + # this explains the codes: https://gist.github.com/thomd/7667642 + export LS_COLORS="ex=1:ln=00;31" + term_bold="$(tput bold)" term_red="$(tput setaf 1)" term_green="$(tput setaf 2)" @@ -2792,6 +3245,16 @@ if [[ $- == *i* ]]; then # so I've thrown a bunch of things at the wall to speed it up. prompt-command() { local return=$? # this MUST COME FIRST + + # all usable colors: + # black + # green nonzero exit (pri 1) + # purple default + # purple bold + # red pwd different owner & group & not writable (pri 2) + # red bold pwd different owner & group & writable (pri 2) + # yellow + local ps_char ps_color unset IFS @@ -2799,22 +3262,21 @@ if [[ $- == *i* ]]; then history -a # save history fi - case $return in - 0) ps_color="$term_purple" - ps_char='\$' - ;; - *) ps_color="$term_green" - ps_char="$return \\$" - ;; - esac + ps_color="$term_purple" + ps_char='\$' if [[ ! -O . ]]; then # not owner if [[ -w . ]]; then # writable ps_color="$term_bold$term_red" else - ps_color="$term_bold$term_green" + ps_color="$term_red" fi fi + if [[ $return != 0 ]]; then + ps_color="$term_green" + ps_char="$return \\$" + fi + # faster than sourceing the file im guessing if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then eval "$(< /dev/shm/iank-status)" @@ -2826,6 +3288,26 @@ if [[ $- == *i* ]]; then if [[ $(jobs -p) ]]; then jobs_char='j\j ' fi + + + # allow a function to specify a command to run after we run the next + # command. Use case: a function makes a persistent notification. If + # we happen to be using that terminal, we can just keep working by + # entering our next command, even a noop in order to dismiss the + # notification, instead of having to explicitly dismiss it. + if [[ ${_psrun[*]} ]]; then + if (( _psrun_count >= 1 )); then + + "${_psrun[@]}" ||: + _psrun_count=0 + unset _psrun + else + _psrun_count=$(( _psrun_count + 1 )) + fi + else + _psrun_count=0 + fi + # We could test if sudo is active with sudo -nv # but then we get an email and log of lots of failed sudo commands. # We could turn those off, but seems better not to. @@ -2837,6 +3319,15 @@ if [[ $- == *i* ]]; then fi PS1="${PS1%"${PS1#*[wW]}"} $jobs_char$psudo\[$ps_color\]$ps_char\[$term_nocolor\] " + # copy of what is automatically added by guix. + # adds [env] to PS1 if GUIX_ENVIRONMENT is set and PS1 contains '$'; + if [ -n "$GUIX_ENVIRONMENT" ]; then + if [[ $PS1 =~ (.*)"\\$" ]]; then + PS1="${BASH_REMATCH[1]} [env]\\\$ " + fi + fi + + # set titlebar. instead, using more advanced # titelbar below #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007" @@ -2852,7 +3343,7 @@ if [[ $- == *i* ]]; then fi # make the titlebar be the last command and the current directory. - settitle () { + auto-window-title () { # These are some checks to help ensure we dont set the title at @@ -2877,7 +3368,7 @@ if [[ $- == *i* ]]; then # condition from the screen man page i think. # note: duplicated in tx() if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then - trap 'settitle "$BASH_COMMAND"' DEBUG + trap 'auto-window-title "$BASH_COMMAND"' DEBUG else trap DEBUG fi