+# like running cl <enter> a <enter>
+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.
+#
+# Usage: pd [-t TAG] COMMAND...
+#
+# -t TAG Override the tag in the syslog. The default is COMMAND with
+# any path part is removed, eg. for /bin/cat the tag is cat.
+#
+# You can view the log via "journalctl -t TAG"
+pd() {
+ local tag ret
+ ret=0
+ tag=${1##*/}
+ case $1 in
+ -t) tag="$2"; shift 2 ;;
+ esac
+ echo "PWD=$PWD command: $*" | logger -t $tag
+ "$@" |& pee cat "logger -t $tag" || ret=$?
+ echo "exited with status=$ret" | pee cat "logger -t $tag"
+ # this avoids any err-catch
+ (( ret == 0 )) || return $ret
+}
+ccomp time pd
+
+# jdo = journal do. Run command as transient systemd service, tailing
+# its output in the journal until it completes.
+#
+# Usage: jdo COMMAND...
+#
+# Compared to pd: commands recognize this is a non-interactive shell.
+# The service is unaffected if our ssh connection dies, no need to run
+# in screen or tmux.
+#
+# Note: The last few lines of any existing entries for a unit by that
+# name will be output first, and there will be a few second delay at the
+# start of the command, and a second or so at the end.
+#
+# Note: Functions and aliases obviously won't work, we resolve the
+# command to a file.
+#
+# Note: requires running as root.
+jdo() {
+ local cmd cmd_name jr_pid ret
+ ret=0
+ cmd="$1"
+ shift
+ if [[ $EUID != 0 ]]; then
+ echo "jdo: error: rerun as root"
+ return 1
+ fi
+ cmd_name=${cmd##*/}
+ if [[ $cmd != /* ]]; then
+ cmd=$(type -P "$cmd")
+ fi
+ # -q = quiet
+ journalctl -qn2 -f -u "$cmd_name" &
+ # Trial and error of time needed to avoid missing initial lines.
+ # .5 was not reliable. 1 was not reliable. 2 was not reliable
+ sleep 4
+ jr_pid=$!
+ systemd-run --unit "$cmd_name" --wait --collect "$cmd" "$@" || ret=$?
+ # The sleep lets the journal output its last line
+ # before the prompt comes up.
+ sleep .5
+ kill $jr_pid &>/dev/null ||:
+ unset jr_pid
+ fg &>/dev/null ||:
+ # this avoids any err-catch
+ (( 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 <<EOF
+### $cmd
+
+\`\`\`
+EOF
+ $cmd
+ cat <<'EOF'
+
+```
+
+EOF
+ done
+}
+
+#### end fsf section
+
+
+..() { c ..; }
+...() { c ../..; }
+....() { c ../../..; }
+.....() { c ../../../..; }
+......() { c ../../../../..; }
+
+chere() {
+ local f path
+ for f; do
+ path=$(readlink -e "$f")
+ echo "cat >$path <<'EOF'"
+ cat "$f"
+ echo EOF
+ done
+}
+
+
+# file cut copy and paste, like the text buffers :)
+# I havnt tested these.
+_fbufferinit() { # internal use
+ ! [[ $my_f_tempdir ]] && my_f_tempdir="$(mktemp -d)"
+ rm -rf "${my_f_tempdir:?}"/*
+}
+fcp() { # file cp
+ _fbufferinit
+ cp "$@" "$my_f_tempdir"/
+}
+fct() { # file cut
+ _fbufferinit
+ mv "$@" "$my_f_tempdir"/
+}
+fpst() { # file paste
+ [[ $2 ]] && { echo too many arguments; return 1; }
+ target=${1:-.}
+ cp "$my_f_tempdir"/* "$target"
+}
+
+_khfix-common() {
+ local host ip port file key tmp
+ read -r host ip port < <(timeout -s 9 2 ssh -oBatchMode=yes -oControlMaster=no -oControlPath=/ -v $1 |& sed -rn "s/debug1: Connecting to ([^ ]+) \[([^\]*)] port ([0-9]+).*/\1 \2 \3/p" ||: )
+ file=$(readlink -f ~/.ssh/known_hosts)
+ if [[ ! $ip ]]; then
+ echo "khfix: ssh failed"
+ return 1
+ fi
+ if [[ $port != 22 ]]; then
+ ip_entry="[$ip]:$port"
+ host_entry="[$host]:$port"
+ else
+ ip_entry=$ip
+ host_entry=$host
+ fi
+ if [[ $host != "$ip" ]]; then
+ tmp=$(mktemp)
+ ssh-keygen -F "$host_entry" -f $file >$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
+ 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
+}
+khfix-r() { # known hosts fix + root
+ _khfix-common "$@" || return 1
+ ssh $1 :
+ rootsshsync
+}
+khfix() {
+ _khfix-common "$@" || return 1
+ ssh $1 :
+}
+
+# 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.
+ # 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}
+for field in {1..20}; do
+ eval a$field"() { awk '{print \$$field}'; }"
+done
+# h1 = head -n1
+for num in {1..9}; do
+ eval h$num"() { head -n$num || [[ \$? == 141 ]]; }"
+done
+
+
+hexipv4() {
+ # shellcheck disable=SC2046 disable=SC2001 disable=SC2183 # hacks, expected
+ printf '%d.%d.%d.%d\n' $(echo $1 | sed 's/../0x& /g')
+}
+
+vp9() {
+ local f out outdir in fname origdir skip1
+ origdir="$PWD"
+ outdir=vp9
+ skip1=false
+ while [[ $1 == -* ]]; do
+ case $1 in
+ # if we got interrupted after 1st phase
+ -2)
+ skip1=true
+ shift
+ ;;
+ --out)
+ outdir=$2
+ shift 2
+ ;;
+ esac
+ done
+ m mkdir -p $outdir
+ # first pass only uses about 1 cpu, so run in parallel
+ for f; do
+ {
+ fname="${f##*/f}"
+ if [[ $f == /* ]]; then
+ in="$f"
+ else
+ in=$origdir/$f
+ fi
+ out="$origdir/$outdir/$fname"
+ mkdir -p /tmp/vp9/$fname
+ cd /tmp/vp9/$fname
+ if ! $skip1 && [[ ! -s ffmpeg2pass-0.log ]]; then
+ # -nostdin or else wait causes ffmpeg to go into stopped state. dunno why, random stackoverflow answer.
+ m ffmpeg -nostdin -hide_banner -loglevel error -i $in -g 192 -vcodec libvpx-vp9 -vf scale=-1:720 -max_muxing_queue_size 9999 -b:v 750K -pass 1 -an -f null /dev/null
+ fi
+ if [[ -e $out ]]; then rm -f $out; fi
+ m ffmpeg -nostdin -hide_banner -loglevel error -y -i $in -g 192 -vcodec libvpx-vp9 -tile-rows 2 -vf scale=-1:720 -max_muxing_queue_size 9999 -b:v 750K -pass 2 -c:a libvorbis -qscale:a 5 $out
+ } &
+ done
+ wait -f
+ cd "$origdir"
+}
+
+utcl() { # utc 24 hour time to local hour 24 hour time
+ echo "print( ($1 $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
+}
+
+bwm() {
+ s bwm-ng -T avg -d
+}
+
+
+# for running in a fai rescue. iank specific.
+kdrescue() {
+ d=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
+ for f in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V; do
+ cryptsetup luksOpen --key-file /p /dev/$f/root crypt-$f-root
+ cryptsetup luksOpen --key-file /p /dev/$f/o crypt-$f-o
+ done
+ mount -o subvol=root_trisquelaramo /dev/mapper/crypt-$d-root /mnt
+ mount -o subvol=a /dev/mapper/crypt-$d-root /mnt/a
+ mount -o subvol=o /dev/mapper/crypt-$d-o /mnt/o
+ mount -o subvol=boot_trisquelaramo /dev/sda2 /mnt/boot
+ cd /mnt
+ chrbind
+}
+