X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;ds=sidebyside;f=brc;h=4a6f67274a44f98b5be8b9b88b5e11fbf1327d6b;hb=refs%2Fheads%2Fmaster;hp=2a81b25fa369f557d373f431d3c56400f61e9707;hpb=9a0f77b0495e6f2643d5646c54b4c99cf3118c67;p=distro-setup diff --git a/brc b/brc index 2a81b25..222e0f8 100644 --- a/brc +++ b/brc @@ -1,6 +1,25 @@ #!/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 @@ -225,34 +244,12 @@ if [[ $- == *i* ]]; then use_color=true fi - if [[ $KONSOLE_PROFILE_NAME ]]; 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 @@ -272,15 +269,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 @@ -296,6 +284,7 @@ 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 @@ -326,9 +315,6 @@ export SL_INFO_DIR=/p/sshinfo if [[ -s $bashrc_dir/path-add-function ]]; then source $bashrc_dir/path-add-function if [[ $SSH_CLIENT ]]; then - if grep -qF /home/iank/.iank/e/e /etc/exports &>/dev/null; then - export EMACSDIR=/home/iank/.iank/e/e - fi path-add $bashrc_dir fi fi @@ -347,6 +333,9 @@ if [[ $SOE ]]; then fi fi +# go exists here +path-add --ifexists /usr/local/go/bin + mysrc() { local path dir file @@ -365,6 +354,7 @@ mysrc() { mysrc /a/bin/small-misc-bash/ll-function mysrc /a/bin/distro-functions/src/package-manager-abstractions +mysrc /a/bin/fai/fai/config/distro-install-common/bash-misc-funcs # things to remember: # ALT-C - cd into the selected directory @@ -693,10 +683,10 @@ jdo() { fi # -q = quiet journalctl -qn2 -f -u "$cmd_name" & + jr_pid=$! # 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. @@ -778,6 +768,50 @@ EOF done } +screenrtp() { + + local ip port xoffset + read -r ip port xoffset <<<"$@" + + setxenv + + if [[ ! $port ]]; then + port=9999 + fi + + while true; do + # By default, plugged in screen goes to the right side, so we need an + # offset that is the same as the laptop's x resolution. If we are in + # mirror mode, then we don't need an offset. + if [[ ! $xoffset ]]; then + xoffset=0 + laptop_x=$(xrandr | awk '$1 == "LVDS-1" {print $4}' | sed 's/x.*//') || { sleep 1; continue; } + total_x=$(xdpyinfo| awk '$1 == "dimensions:" {print $2}' | sed 's/x.*//') || { sleep 1; continue; } + screen2_res=$(xrandr | awk '$2 == "connected" && $1 != "LVDS-1" { print $3 }' | sed 's/+.*//') + if (( laptop_x < total_x )); then + xoffset=$laptop_x + fi + fi + + m ffmpeg -probesize 50M -thread_queue_size 50 \ + -video_size $screen2_res -f x11grab -framerate 30 -i :0.0+$xoffset.0 \ + -vcodec libx264 -g 1 -tune zerolatency -preset ultrafast -pix_fmt yuv420p -x264-params repeat-headers=1 \ + -f rtp_mpegts rtp://$ip:$port ||: + + + sleep 1 + done +} + +setxenv() { + if [[ ! $DISPLAY ]]; then + export DISPLAY=:0.0 + fi + if [[ ! $XAUTHORITY ]]; then + export XAUTHORITY=$HOME/.Xauthority + fi +} + #### end fsf section @@ -819,25 +853,40 @@ fpst() { # file paste } _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" ||: ) + local host ip port file key tmp ssh_host alias + ssh_host=$1 + { + read -r host ip port + read -r alias; + # note ":graph:" is needed or else we get a trailing \r out of ssh, + # dunno why. web search says terminals add \r, so I tried adding -T + # to turn off psuedo terminal, but it didnt help. + } < <(timeout -s 9 2 ssh -TN -oBatchMode=yes -oControlMaster=no -oControlPath=/ -v $ssh_host |& + sed -rn "s/debug1: Connecting to ([^ ]+) \[([^\]*)] port ([0-9]+).*/\1 \2 \3/p; +s/^debug1: using hostkeyalias: ([[:graph:]]*).*/\1/p" ||: ) file=$(readlink -f ~/.ssh/known_hosts) if [[ ! $ip ]]; then echo "khfix: ssh failed" return 1 fi + ip_entry=$ip + host_entry=$host + if [[ $alias ]]; then + host_entry="$alias" + fi if [[ $port != 22 ]]; then ip_entry="[$ip]:$port" - host_entry="[$host]:$port" - else - ip_entry=$ip - host_entry=$host + if [[ ! $alias ]]; then + host_entry="[$host]:$port" + fi fi - if [[ $host != "$ip" ]]; then + if [[ $host_entry != "$ip_entry" ]]; 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) + else + echo "khfix WARNING: did not find host entry:$host_entry in known_hosts" fi rm $tmp if [[ $key ]]; then @@ -849,30 +898,43 @@ _khfix-common() { ssh-keygen -F "$ip_entry" -f $file >$tmp || [[ $? == 1 ]] if [[ -s $tmp ]]; then key=$(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp) + else + echo "khfix WARNING: did not find ip entry:$ip_entry in known_hosts" fi rm $tmp if [[ $key ]]; then grep -Fv "$key" "$file" | sponge "$file" fi - ll ~/.ssh/known_hosts } -khfix-r() { # known hosts fix + root +khfix-r() { # known hosts fix without syncing to root user _khfix-common "$@" || return 1 ssh $1 : - rootsshsync } 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} @@ -979,10 +1041,13 @@ caf() { -o -name .hg -prune -o -name .editor-backups -prune \ -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" + hr "$file" + v "$file" + # if the file is nonempty and the last char is nonempty, it is not + # newline terminated. + if [[ -s "$file" && "$(tail -c 1 "$file")" ]]; then + echo + fi done } ccomp cat cf caf @@ -1257,9 +1322,13 @@ etailm() { tail -F /var/log/exim4/mainlog -n 200 "$@" } etail2() { - tail -F /var/log/exim4/mymain -n 200 "$@" + tail -F /var/log/exim4/nondmain -n 200 "$@" +} +# shortcut for tail -F +ta() { + tail -F "$@" } -ccomp tail etail etail2 +ccomp tail etail etail2 ta # ran into this online, trying it out detach() { @@ -1367,7 +1436,12 @@ egrinid() { sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog} } etailin() { - tail -F /var/log/exim4/mainlog | sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' + local -a tail_arg + tail_arg=(-n500) + if [[ $1 ]]; then + tail_arg=($@) + fi + tail "${tail_arg[@]}" -F /var/log/exim4/mainlog | sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' } @@ -1382,6 +1456,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 \ @@ -1545,53 +1620,6 @@ and works in older versions of git which did not have that." echo "${p%%/.git}" } -g() { - - local args gdb=false - - if [[ $EMACSDIR ]]; then - path-add "$EMACSDIR/lib-src" "$EMACSDIR/src" - fi - - if [[ $DISPLAY ]]; then - args=-n - fi - - if (( $# == 0 )); then - args+=" -c" - fi - # duplicate -c, but oh well - if ! pgrep -u $EUID emacsclient; then - if (( $# == 0 )) && type -p gdb &>/dev/null; then - gdb=true - else - args+=" -c" - 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 - PATH="$EMACSDIR/lib-src:$EMACSDIR/src:$PATH" EHOME=$HOME HOME=$EMACSDIR m emacsclient -a "" $args "$@" - else - if $gdb; then - # due to a bug, we cant debug from the start unless we get a new gdb - # https://sourceware.org/bugzilla/show_bug.cgi?id=24454 - # m gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacs --daemon - m emacsclient -a "" $args "$@" - sleep 1 - cd "/a/opt/emacs-$(distro-name)$(distro-num)" - s gdb -p "$(pgrep -f 'emacs --daemon')" -ex c - cd - - else - m emacsclient -a "" $args "$@" - fi - fi -} # g pipe. like: cmd | emacs. save cmd output to tmp file, then edit. gp() { @@ -1609,8 +1637,9 @@ gl() { "$@" &> /a/tmp/gtmp g /a/tmp/gtmp } -# g command substitution +# g command substitution. gc() { + # shellcheck disable=SC2046 # i want word splitting for this hackery g $("$@") } @@ -1636,9 +1665,9 @@ grr() { # grep recursive # Don't return 1 on nonmatch because this is meant to be # interactive, not in a conditional. if [[ ${#@} == 1 ]]; then - grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto "$@" . || [[ $? == 1 ]] + grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -rniIP --color=auto "$@" . || [[ $? == 1 ]] else - grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto "$@" || [[ $? == 1 ]] + grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -rniIP --color=auto "$@" || [[ $? == 1 ]] fi } ccomp grep gr grr @@ -1658,10 +1687,21 @@ re() { # horizontal row. used to break up output hr() { - local blocks - # 180 is long enough. - blocks=██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ - printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)${blocks:0:${COLUMNS:-180}}$(tput sgr0 2>/dev/null||:)" + local start end end_count arg + # 180 is long enough. 5 for start. + start=█████ end=█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ + end_count=$(( ${COLUMNS:-180} - 5 )) + arg="$*" + if [[ $arg ]]; then + end_count=$(( end_count - 2 - ${#arg} )) + start="$start $arg " + fi + if (( end_count >= 1 )); then + end=${end:0:$end_count} + else + end= + fi + printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)$start$end$(tput sgr0 2>/dev/null||:)" } # highlight hl() { @@ -1720,7 +1760,7 @@ go-github-install() { file_prefix=$2 file_suffix=$3 tmp="${file_prefix##*[[:alnum:]]}" - targetf="${file_prefix%$tmp}" + targetf="${file_prefix%"$tmp"}" echo targetf: $targetf github-release-dl "$@" files=(./*) @@ -1974,6 +2014,8 @@ ksu() { # history search unique grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history} | uniq || [[ $? == 1 ]]; } +# remove lines from history matching $1 +# # todo: id like to do maybe a daily or hourly cronjob to # check that my history file size is increasing. Ive had it # inexplicably truncated in the past. @@ -2036,9 +2078,9 @@ nags() { /usr/bin/nagstamon & } -# profanity screen +# profanity tmux profsrc() { - screen -RD -S profanity + screen -L profanity a } # i dont want to wait for konsole to exit... @@ -2103,7 +2145,7 @@ 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 m cp $cached . else @@ -2145,7 +2187,7 @@ grep ps and output in a nice format" fi x=$(ps -eF) # final grep is because some commands tend to have a lot of trailing spaces - y=$(echo "$x" | grep -iP "$@" | grep -o '.*[^ ]') ||: + y=$(echo "$x" | sed -r 's,//[^[:space:]:@/]+:[^[:space:]:@/]+@,//REDACTED_URL_USER@PASS/,g' | grep -iP "$@" | grep -o '.*[^ ]') ||: if [[ $y ]]; then echo "$x" | head -n 1 || [[ $? == 141 ]] echo "$y" @@ -2375,6 +2417,19 @@ serstat() { systemctl -n 40 status "$@" } +# assume last arg is a service and we want to tail its log. +serj() { + local service jr_pid ret + ret=0 + service="${*: -1}" + journalctl -qn2 -f -u "$service" & + sleep 3 + s systemctl "$@" || ret=$? + sleep .5 + kill %% + (( ret == 0 )) || return $ret +} + seru() { systemctl --user "$@"; } # like restart, but do nothing if its not already started srestart() { @@ -2427,18 +2482,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,2032,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() { @@ -2447,15 +2508,41 @@ skq() { shellcheck -W 999 -x -e $others "$@" || return $? } -skgit() { +# sk on all modified & new files in current git repo. must git add for new files. +skmodified() { local f - for f in $(i s | awk '$1 == "modified:" {print $2}'); do - if istext "$f" && [[ $(head -n1 "$f" 2>/dev/null) == '#!/bin/bash'* ]]; then + for f in $(i s | awk '$1 == "modified:" {print $2}; $1 == "new" {print $3}'); do + 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. @@ -2689,8 +2776,15 @@ sl() { slr() { sl --rsync "$@" } -sss() { # ssh solo - sl -oControlMaster=no -oControlPath=/ "$@" + + +# ssh solo +# +# WARNING: If you are trying to use -i, remember that keys added to +# agent previously will still be tried. Use ssh-add -D to remove all +# keys from the agent. +sss() { + ssh -oControlMaster=no -oControlPath=/ "$@" } # kill off old shared socket then ssh ssk() { @@ -2700,11 +2794,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 "$@" } @@ -2817,13 +2907,23 @@ psoff() { # however, DEBUG is not inherited, so we need to run it outside a function. # And we want to run set -x afterwards to avoid spam, so we cram everything # in here, and then it will run after this function is done. - # # set as array to satisfy shellcheck, but it is equivalent to setting it as non-array - PROMPT_COMMAND=('trap DEBUG; unset PROMPT_COMMAND; PS1="\w \$ "') + # shellcheck disable=SC2178 # intentional + PROMPT_COMMAND='trap DEBUG; unset PROMPT_COMMAND; PS1=" \w \$ "' +} + +pskde() { + # shellcheck disable=SC2178 # intentional + PROMPT_COMMAND='trap DEBUG; unset PROMPT_COMMAND' + PS1='\[\e]133;L\a\]\[\e]133;D;$?\]\[\e]133;A\a\]\w \$ \[\e]133;B\a\]' ; + PS2='\[\e]133;A\a\]'$PS2'\[\e]133;B\a\]' ; + PS0='\[\e]133;C\a\]' + } + pson() { PROMPT_COMMAND=(prompt-command) if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then - trap 'settitle "$BASH_COMMAND"' DEBUG + trap 'auto-window-title "$BASH_COMMAND"' DEBUG fi } @@ -2863,34 +2963,8 @@ 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 -# updates $reload to true if file updated is in /etc/systemd/system -u() { - local tmp tmpdir dest="$1" - local base="${dest##*/}" - local dir="${dest%/*}" - if [[ $dir != "$base" ]]; then - # dest has a directory component - mkdir -p "$dir" - fi - # shellcheck disable=SC2034 # see comment at top of function - ur=false # u result - tmpdir="$(mktemp -d)" - cat >$tmpdir/"$base" - tmp=$(rsync -ic $tmpdir/"$base" "$dest") - if [[ $tmp ]]; then - printf "%s\n" "$tmp" - # shellcheck disable=SC2034 # see comment at top of function - ur=true - if [[ $dest == /etc/systemd/system/* ]]; then - # shellcheck disable=SC2034 # see comment at top of function - reload=true - fi - fi - rm -rf $tmpdir -} uptime() { @@ -3015,7 +3089,7 @@ spark() for n in $numbers do - _spark_echo -n ${ticks[$(( (((n-min)<<8)/f) ))]} + _spark_echo -n ${ticks[$(( ((n-min)<<8)/f ))]} done _spark_echo } @@ -3080,8 +3154,9 @@ n() { } catnew() { - local dir file + local dir file _ dir="$1" + # shellcheck disable=SC2030 inotifywait -m "$dir" -e create -e moved_to | while read -r _ _ file; do hr cat "$dir/$file" @@ -3125,6 +3200,12 @@ 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 $? @@ -3139,9 +3220,13 @@ if $use_color && type -p tput &>/dev/null; then # 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 + export LS_COLORS="ex=1:ln=00;31" term_bold="$(tput bold)" term_red="$(tput setaf 1)" @@ -3195,29 +3280,46 @@ 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 if [[ $HISTFILE ]]; then history -a # save history + if [[ -e $HOME/.iank-stream-on ]]; then + if [[ $HISTFILE == $HOME/.bh ]]; then + ps_char="HISTP " + fi + elif [[ $HISTFILE == /a/bin/data/stream_hist ]]; then + ps_char="HISTS " + fi 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="$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)" @@ -3227,7 +3329,7 @@ if [[ $- == *i* ]]; then fi jobs_char= if [[ $(jobs -p) ]]; then - jobs_char='j\j ' + jobs_char="$(jobs -p)"'j\j ' fi @@ -3260,6 +3362,8 @@ 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 @@ -3272,6 +3376,16 @@ if [[ $- == *i* ]]; then # set titlebar. instead, using more advanced # titelbar below #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007" + + # version 211203 does not have this feature. + if [[ $KONSOLE_VERSION && $KONSOLE_VERSION == [3456789]* || $KONSOLE_VERSION == 2[2456789]* ]]; then + # from konsole, output via ctrl-alt-] + if [[ ! $PS1 =~ 133 ]] ; then + PS1='\[\e]133;L\a\]\[\e]133;D;$?\]\[\e]133;A\a\]'$PS1'\[\e]133;B\a\]' ; + PS2='\[\e]133;A\a\]'$PS2'\[\e]133;B\a\]' ; + PS0='\[\e]133;C\a\]' ; fi + fi + } PROMPT_COMMAND=(prompt-command) @@ -3284,7 +3398,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 @@ -3309,13 +3423,85 @@ 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 fi + +lp22viewers() { + v=0 + roomv=(0 0) + rooms=(jupiter saturn) + for ip in 209.51.188.25 live.fsf.org; do + out=$(curl -sS --insecure https://$ip/) + for i in 0 1 2; do + room=${rooms[i]} + while read -r n; do + v=$((v+n)) + # shellcheck disable=SC2004 # false positive + roomv[$i]=$(( ${roomv[$i]} + n )) + done < <(printf "%s\n" "$out" | grep -Po "$room.*?current[^0-9]*[0-9]*" | grep -o '[0-9]*$' ) + done + done + printf "total: %s " $v + for i in 0 1; do + room=${rooms[i]} + printf "$room: %s " "${roomv[$i]}" + done + echo +} + +arpflush() { + local default_route_dev + default_route_dev=$(ip r show default | sed 's/.*dev \([^ ]*\).*/\1/' | head -n1) + m s ip n flush dev "$default_route_dev" +} + +dsh() { + command dsh -c "$@" +} + +# cat or bat with color if we have it +v() { + if type -t batcat >/dev/null; then + # note: another useful useful style is "header" + batcat --color always --style plain --theme Coldark-Cold -P "$@" + else + cat "$@" + fi +} + +# Combine files $@ into a single file with comments between them which +# allow splitting them back with fsplit. +# +# Assumes file names do not have newlines in them. +fcomb() { + local f comment out + # generated with cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c8 + comment='# jvvuyUsq ' + out=~/fcomb + rm -f $out + for f; do + echo "$comment$f" >>$out + cat "$f" >>$out + done +} +fsplit() { + local f fin line fin_lines + fin=~/fcomb + line=1 + fin_lines=$(wc -l "$fin" | awk '{print $1}') + comment='# jvvuyUsq ' + while (( line <= fin_lines )); do + f=$(sed -n "${line}s/^$comment//p" "$fin") + sed -n "$line,/^$comment/{/^$comment/d;p}" "$fin" >"$f" + line=$(( line + 1 + $(wc -l "$f" | awk '{print $1}') )) + done +} + # * stuff that makes sense to be at the end