2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
9 # Copyright 2024 Ian Kelling
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
15 # http://www.apache.org/licenses/LICENSE-2.0
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
23 # this gets sourced. shebang is just for file mode detection
25 # Use source ~/.bashrc instead of doing bash -l when running a script
26 # so this can set extdebug and avoid the bash debugger.
29 if [[ -s /a
/bin
/bash-bear-trap
/bash-bear
]]; then
30 # shellcheck source=/a/bin/bash-bear-trap/bash-bear
31 source /a
/bin
/bash-bear-trap
/bash-bear
32 # wtf, shellcheck doesn't allow disabling warnings in elifs
34 # bleh shellcheck can't handle disabling in an elif, so nesting this if.
35 # shellcheck disable=SC2154 # set in .bashrc
36 if [[ -s $bashrc_dir/bash-bear
]]; then
37 # shellcheck source=/a/bin/bash-bear-trap/bash-bear
38 source $bashrc_dir/bash-bear
42 # In t8, it runs clear_console for login shells by default. I don't want
43 # my console cleared. And linux ttys get cleared without this.
44 if shopt login_shell
>/dev
/null
&& [[ -e ~
/.bash_logout
]]; then
48 # if [[ -s /usr/share/bash-completion/completions/git ]]; then
49 # source /usr/share/bash-completion/completions/git
51 # if [[ -s /usr/share/bash-completion/completions/gitk ]]; then
52 # source /usr/share/bash-completion/completions/gitk
55 # for testing error catching:
71 # remove all aliases. aliases provided by the system tend to get in the way,
72 # for example, error happens if I try to define a function the same name as an alias
75 # remove gnome keyring warning messages
76 # there is probably a more proper way, but I didnt find any easily on google
77 # now using xfce+xmonad instead of vanilla xmonad, so disabling this
78 #unset GNOME_KEYRING_CONTROL
80 # use extra globing features.
82 # include .files when globbing, but ignore files name . and ..
83 # setting this also sets dotglob.
84 export GLOBIGNORE
="*/.:*/.."
86 # Useful info. see man bash.
90 # broken with bash_completion package. Saw a bug for this once. dont anymore.
91 # still broken in wheezy
92 # still buggered in latest stable from the web, version 2.1
93 # perhaps its fixed in newer git version, which fails to make for me
94 # this note is from 6-2014.
95 # still broken in flidas.
98 # make tab on an empty line do nothing
99 shopt -s no_empty_cmd_completion
101 # fix spelling errors for cd, only in interactive shell
103 # append history instead of overwritting it
105 # for compatibility, per gentoo/debian bashrc
106 shopt -s checkwinsize
107 # attempt to save multiline single commands as single history entries.
114 if [[ $LC_INSIDE_EMACS ]]; then
115 # EMACS is used by bash on startup, but we dont need it anymore.
116 # plus I hit a bug in a makefile which inherited it
118 export LC_INSIDE_EMACS
121 # scp completion does not work, but this doesnt fix it. todo, figure this out
122 #complete -r scp &> /dev/null
123 # todo, remote file completion fails, figure out how to turn it off
124 export NODE_DISABLE_COLORS
=1
125 # This gets rid of ugly terminal escape chars in node repl
126 # sometime, Id like to have completion working in emacs shell for node
127 # the offending chars can be found in lib/readline.js,
128 # things that do like:
129 # stream.write('\x1b[' + (x + 1) + 'G');
130 # We can remove them and keep readline, for example by doing this
132 #!/usr/bin/env nodejs
133 # var readline = require('readline');
134 # readline.cursorTo = function(a,b,c) {};
135 # readline.clearScreenDown = function(a) {};
136 # const repl = require('repl');
137 # var replServer = repl.start('');
139 # no prompt, or else readline complete seems to be confused, based
140 # on our column being different? node probably needs to send
141 # different kind of escape sequence that is not ugly. Anyways,
142 # completion doesnt work yet even with the ugly prompt, so whatever
144 export NODE_NO_READLINE
=1
148 export SSH_CONFIG_FILE_OVERRIDE
=/root
/.ssh
/confighome
152 # emacs has a different default search path than the info command. This
153 # adds the info defaults to emacs. This is commented because after
154 # various upgrades this is no longer a problem: for the directories that
155 # exist on my system, emacs already includes the ones that info
158 # but not the reverse, because I dun
159 # care much about the cli. The search path is only on the cli if you run
160 # "info xxx", or in emacs if you run '(info xxx)', so not that
161 # important and i don't bother fixing it.
163 # # info info says this path is what was compiled, and its not documented
164 # # anywhere. Through source grepping, i found it in files.h of the info
165 # # source in trisquel flidas.
167 # # Trailing : means for emacs to add its own stuff on to the end.
169 # # A problem with this is that directories which are not readable breaks info. And of course, this hard coding is not nice.
170 # # 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.
172 # # https://raw.githubusercontent.com/debian-tex/texinfo/master/info/filesys.h
175 # # note: to split up the var like this, do:
176 # # IFS=:; printf '%s\n' $INFOPATH
181 # /usr/local/lib/info
183 # /usr/local/gnu/info
184 # /usr/local/gnu/lib/info
189 # /usr/share/lib/info
190 # /usr/local/share/info
191 # /usr/local/share/lib/info
192 # /usr/gnu/lib/emacs/info
193 # /usr/local/gnu/lib/emacs/info
194 # /usr/local/lib/emacs/info
195 # /usr/local/emacs/info
198 # for d in ${dirs[@]}; do
199 # if [[ -r $d ]]; then
200 # INFOPATH="$d:$INFOPATH"
206 # note: guix bash config does this automatically.
207 if [[ $INFOPATH != *: ]]; then
208 INFOPATH
="$INFOPATH:"
211 # info parameter expansion
215 # / search, {, }: next/prev match
216 # ctrl/alt-v scroll forward/backward within this node
217 # l: go to previous node
220 info bash
'Basic Shell Features' 'Shell Expansions' 'Shell Parameter Expansion'
224 # for openwrt system that has no stty, this is easier than
225 # guarding every time i use it.
226 if ! type -p stty
>/dev
/null
; then
232 if [[ $
- == *i
* ]]; then
233 # for readline-complete.el
234 if [[ $LC_INSIDE_EMACS ]]; then
235 # all for readline-complete.el
237 bind 'set horizontal-scroll-mode on'
238 bind 'set print-completions-horizontally on'
239 bind '"\C-i": self-insert'
243 if [[ $TERM != dumb
]] && test -t 1; then
247 # todo: not sure this works in sakura
249 #bind "\C-w": kill-region
250 # sakura == xterm-256color
252 if [[ $TERM == xterm
* ]]; then
253 # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
254 bind '"\e[1;5C": shell-forward-word' 2>/dev
/null
255 bind '"\e[1;5D": shell-backward-word' 2>/dev
/null
257 # make ctrl-backspace work. for konsole, i fixed it through
258 # /home/iank/.local/share/konsole/default.keytab
260 bind '"\eOc": shell-forward-word'
261 bind '"\eOd": shell-backward-word'
263 # i cant remember why i did this, probably to free up some keys to bind
264 # to other things in bash.
265 # other than C-c and C-z, the rest defined by stty -a are, at least in
266 # gnome-terminal, overridden by bash, or disabled by the system
267 stty lnext undef stop undef start undef
272 export BC_LINE_LENGTH
=0
275 export PROFILE_TASKS_TASK_OUTPUT_LIMIT
=100
277 # note, if I use a machine I dont want files readable by all users, set
278 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
280 # i for insensitive. the rest from
281 # X means dont remove the current screenworth of output upon exit
282 # R means to show colors n things
283 # a useful flag is -F aka --quit-if-one-screen
285 export SYSTEMD_LESS
=$LESS
288 export NNN_COLORS
=2136
290 export SL_FILES_DIR
=/b
/ds
/sl
/.iank
291 export SL_INFO_DIR
=/p
/sshinfo
296 # this is adapted from things printed to term after install
297 # pyenv. commented for now since I'm not actually using pyenv.
299 # export PYENV_ROOT="$HOME/.pyenv"
300 # command -v pyenv &>/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
301 # command -v pyenv &>/dev/null && eval "$(pyenv init -)"
304 # output showed this example for pyenv-virtualenv, which i have no idea
305 # what it is, but leaving it as a comment in case I end up doing python
308 #eval "$(pyenv virtualenv-init -)"
309 ### end begin pyenv ###
315 if [[ -s $bashrc_dir/path-add-function
]]; then
316 source $bashrc_dir/path-add-function
317 if [[ $SSH_CLIENT ]]; then
322 # if someone exported $SOE (stop on error), catch errors.
324 # Note, on debian this results in the following warning when in ssh,
325 # hich I haven't figured out how to fix. It doesn't happen if we source
326 # after the shell has started
328 # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory
329 # bash: warning: cannot start debugger; debugging mode disabled
331 if [[ -e /a
/bin
/bash-bear-trap
/bash-bear
]]; then
332 source /a
/bin
/bash-bear-trap
/bash-bear
337 path-add
--ifexists /usr
/local
/go
/bin
345 if [[ -s $path ]]; then
346 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
348 elif [[ -s $bashrc_dir/$file ]]; then
349 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
350 source $bashrc_dir/$file
355 mysrc
/a
/bin
/small-misc-bash
/ll-function
356 mysrc
/a
/bin
/distro-functions
/src
/package-manager-abstractions
357 mysrc
/a
/bin
/fai
/fai
/config
/distro-install-common
/bash-misc-funcs
359 # things to remember:
360 # ALT-C - cd into the selected directory
361 # CTRL-T - Paste the selected file path into the command line
363 # good guide to some of its basic features is the readme file
364 # https://github.com/junegunn/fzf
366 # if [[ -s /usr/share/doc/fzf/examples/key-bindings.bash ]]; then
367 # source /usr/share/doc/fzf/examples/key-bindings.bash
373 # temporary functions
375 m
"${@//spring/fall}"
378 e
"${@//spring/fall}"
382 ### begin FSF section ###
384 # Comments before functions are meant to be good useful
385 # documentation. If they fail at that, please improve them or send Ian a
388 ## copy bash completion
390 # It copies how the bash completion works from one command to other
391 # commands. Generally just use within a .bashrc.
393 # Usage: ORIGINAL_COMMAND TARGET_COMMAND...
399 if ! c
=$
(complete
-p $src 2>/dev
/null
); then
400 _completion_loader
$src &>/dev
/null ||
:
401 c
=$
(complete
-p $src 2>/dev
/null
) ||
return 0
409 ## BEGIN functions to change directory better than cd ##
413 # c: acts like cd, but stores directory history: you could alias to cd if you wanted.
416 # cl: list recent directories and optionally choose one.
418 # Finer details you may want to skip:
420 # bl: print the list of back and forward directories.
422 # We keep 2 stacks of directories, forward and back. Unlike with a web
423 # browser, the forward stack is not erased when going somewhere new.
425 # Recent directories are stored in ~/.cdirs.
427 declare -a _dir_forward _dir_back
429 # normally, the top of _dir_back is our current dir. if it isn't,
430 # put it on there, except we don't want to do that when we
431 # just launched a shell
432 if [[ $OLDPWD ]]; then
433 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
438 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
441 echo "$PWD" >> ~
/.cdirs
448 if (( ${#_dir_back[@]} == 0 )); then
449 echo "nothing left to go back to" >&2
452 top_back
="${_dir_back[-1]}"
454 if [[ $top_back == "$PWD" ]] && (( ${#_dir_back[@]} == 1 )); then
455 echo "already on last back entry" >&2
460 if [[ $top_back == "$PWD" ]]; then
461 # add to dirf if not already there
462 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$top_back" ]]; then
463 _dir_forward
+=("$top_back")
465 unset "_dir_back[-1]"
466 command cd "${_dir_back[-1]}"
468 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$PWD" ]]; then
469 _dir_forward
+=("$PWD")
471 command cd "$top_back"
474 # Interesting feature, not sure I want it.
475 # give us a peek at what is next in the list
476 # if (( ${#_dir_back[@]} >= 2 )); then
477 # printf "%s\n" "${_dir_back[-2]}"
481 # c/b/f Implementation notes:
483 # The top of the back is $PWD
484 # as long as the last directory change was due to c,b,or cl.
486 # Example of stack changes:
512 if (( ${#_dir_forward[@]} == 0 )); then
513 echo "no forward dir left" >&2
516 top_forward
="${_dir_forward[-1]}"
517 unset "_dir_forward[-1]"
520 # give us a peek at what is next in the list
521 # if (( ${#_dir_forward[@]} )); then
522 # printf "%s\n" "${_dir_forward[-1]}"
527 local i line input start
528 local -A buttondirs alines
529 local -a buttons
dirs lines
530 buttons
=( {a..z
} {2.
.9} )
531 if [[ ! -s ~
/.cdirs
]]; then
532 echo nothing
in ~
/.cdirs
538 mapfile
-t lines
<~
/.cdirs
539 start
=$
(( ${#lines[@]} - 1 ))
541 # we have ~33 buttons as of this writing, so lets
542 # prune down the history every once in a while.
543 if (( start
> 500 )); then
544 tac ~
/.cdirs |
awk '!seen[$0]++' |
head -n 200 |
tac | sponge ~
/.cdirs ||
[[ $?
== 141 ]]
547 for (( j
=start
; j
>= 0; j--
)); do
549 if [[ ! $line ||
${alines[$line]} ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
553 buttondirs
[${buttons[i]}]="$line"
554 printf "%s %s\n" ${buttons[i]} "$line"
555 # the LINES bit is for when we have a short terminal, just dont print all
556 # the directories. alternative would be to do something like less the list.
557 if (( i
== ${#buttons[@]} - 1 )) ||
{ [[ $LINES ]] && (( i
== LINES
- 3 )); }; then
563 if (( i
== 0 )); then
564 echo "no dirs in ~/.cdirs"
568 if [[ $input != $
'\n' ]]; then
569 c
"${buttondirs[$input]}"
572 # bl = back list. lists the back and forward directories. i tend to
573 # forget this exists and use cl instead.
577 start
=$
(( ${#_dir_back[@]} - 1 ))
579 # cleanup possible repeating of pwd
580 if (( start
>= 0 )) && [[ ${_dir_back[$start]} == "$PWD" ]]; then
581 start
=$
(( start
- 1 ))
584 if (( start
>= 0 )); then
585 for (( i
=start
; i
>= 0 ; i--
)); do
586 printf "%s %s\n" $j ${_dir_back[i]}
588 if (( j
>= max
)); then
595 start
=$
(( ${#_dir_forward[@]} - 1 ))
597 # cleanup possible repeating of pwd
598 if (( start
>= 0 )) && [[ ${_dir_forward[$start]} == "$PWD" ]]; then
599 start
=$
(( start
- 1 ))
601 if (( start
< 0 )); then
606 for (( i
=start
; i
>= 0 ; i--
)); do
607 printf "%s %s\n" $j ${_dir_forward[i]}
609 if (( j
>= max
)); then
614 # like running cl <enter> a <enter>
617 mapfile
-t lines
<~
/.cdirs
618 start
=$
(( ${#lines[@]} - 1 ))
619 for (( j
=start
; j
>= 0; j--
)); do
621 if [[ ! $line ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
629 ## END functions to change directory better than cd ##
631 # pee do. run args as a command with output copied to syslog.
633 # Usage: pd [-t TAG] COMMAND...
635 # -t TAG Override the tag in the syslog. The default is COMMAND with
636 # any path part is removed, eg. for /bin/cat the tag is cat.
638 # You can view the log via "journalctl -t TAG"
644 -t) tag
="$2"; shift 2 ;;
646 echo "PWD=$PWD command: $*" | logger
-t $tag
647 "$@" |
& pee
cat "logger -t $tag" || ret
=$?
648 echo "exited with status=$ret" | pee
cat "logger -t $tag"
649 # this avoids any err-catch
650 (( ret
== 0 )) ||
return $ret
654 # jdo = journal do. Run command as transient systemd service, tailing
655 # its output in the journal until it completes.
657 # Usage: jdo COMMAND...
659 # Compared to pd: commands recognize this is a non-interactive shell.
660 # The service is unaffected if our ssh connection dies, no need to run
663 # Note: The last few lines of any existing entries for a unit by that
664 # name will be output first, and there will be a few second delay at the
665 # start of the command, and a second or so at the end.
667 # Note: Functions and aliases obviously won't work, we resolve the
670 # Note: requires running as root.
672 local cmd cmd_name jr_pid ret
676 if [[ $EUID != 0 ]]; then
677 echo "jdo: error: rerun as root"
681 if [[ $cmd != /* ]]; then
682 cmd
=$
(type -P "$cmd")
685 journalctl
-qn2 -f -u "$cmd_name" &
687 # Trial and error of time needed to avoid missing initial lines.
688 # .5 was not reliable. 1 was not reliable. 2 was not reliable
690 systemd-run
--unit "$cmd_name" --wait --collect "$cmd" "$@" || ret
=$?
691 # The sleep lets the journal output its last line
692 # before the prompt comes up.
694 kill $jr_pid &>/dev
/null ||
:
697 # this avoids any err-catch
698 (( ret
== 0 )) ||
return $ret
702 # standard date as used in logs
707 # date in log appropriate format
714 command ts
"%F %T" "$@"
717 # ts log. log command to log file.
718 # usage: tsl LOG_PATH_PREFIX COMMAND...
719 # example: tsl /root/command
720 # log file will be like /root/command-2024-02-10.log
722 local log_prefix log_path appending ret
723 if (( $# < 2 )); then
724 echo "tsl: error: expected >= 2 arguments, got $#" >&2
728 if [[ $log_prefix == */* && ! -d ${log_prefix%*/} ]]; then
729 echo "tsl: error: expected directory at ${log_prefix%*/}" >&2
732 log_path
=$log_prefix-$
(date +%Y-
%m-
%d
).log
734 if [[ -s $log_path ]]; then
738 printf "%s\n" "CWD: $PWD, log: $log_path, running $*" | ts
"%F %T" |
tee -a "$log_path"
740 "$@" |
& ts
"%F %T" |
tee -a "$log_path" || ret
=$?
741 printf "%s\n" "exit code $ret from command: $*" | ts
"%F %T" |
tee -a "$log_path"
743 printf "%s\n" "note: this log file contains logs before those of previous command" | ts
"%F %T" |
tee -a "$log_path"
749 mapfile
-t cmds
<<'EOF'
750 tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab
753 ls -la /dev/disk/by-id
756 for cmd
in "${cmds[@]}"; do
773 local ip port xoffset
774 read -r ip port xoffset
<<<"$@"
778 if [[ ! $port ]]; then
783 # By default, plugged in screen goes to the right side, so we need an
784 # offset that is the same as the laptop's x resolution. If we are in
785 # mirror mode, then we don't need an offset.
786 if [[ ! $xoffset ]]; then
788 laptop_x
=$
(xrandr |
awk '$1 == "LVDS-1" {print $4}' |
sed 's/x.*//') ||
{ sleep 1; continue; }
789 total_x
=$
(xdpyinfo|
awk '$1 == "dimensions:" {print $2}' |
sed 's/x.*//') ||
{ sleep 1; continue; }
790 screen2_res
=$
(xrandr |
awk '$2 == "connected" && $1 != "LVDS-1" { print $3 }' |
sed 's/+.*//')
791 if (( laptop_x
< total_x
)); then
796 m ffmpeg
-probesize 50M
-thread_queue_size 50 \
797 -video_size $screen2_res -f x11grab
-framerate 30 -i :0.0+$xoffset.0 \
798 -vcodec libx264
-g 1 -tune zerolatency
-preset ultrafast
-pix_fmt yuv420p
-x264-params repeat-headers
=1 \
799 -f rtp_mpegts rtp
://$ip:$port ||
:
807 if [[ ! $DISPLAY ]]; then
810 if [[ ! $XAUTHORITY ]]; then
811 export XAUTHORITY
=$HOME/.Xauthority
820 ....
() { c ..
/..
/..
; }
821 .....
() { c ..
/..
/..
/..
; }
822 ......
() { c ..
/..
/..
/..
/..
; }
827 path
=$
(readlink
-e "$f")
828 echo "cat >$path <<'EOF'"
835 # file cut copy and paste, like the text buffers :)
836 # I havnt tested these.
837 _fbufferinit
() { # internal use
838 ! [[ $my_f_tempdir ]] && my_f_tempdir
="$(mktemp -d)"
839 rm -rf "${my_f_tempdir:?}"/*
843 cp "$@" "$my_f_tempdir"/
847 mv "$@" "$my_f_tempdir"/
849 fpst
() { # file paste
850 [[ $2 ]] && { echo too many arguments
; return 1; }
852 cp "$my_f_tempdir"/* "$target"
856 local host ip port
file key tmp ssh_host
alias
861 # note ":graph:" is needed or else we get a trailing \r out of ssh,
862 # dunno why. web search says terminals add \r, so I tried adding -T
863 # to turn off psuedo terminal, but it didnt help.
864 } < <(timeout
-s 9 2 ssh -TN -oBatchMode=yes -oControlMaster=no
-oControlPath=/ -v $ssh_host |
&
865 sed -rn "s/debug1: Connecting to ([^ ]+) \[([^\]*)] port ([0-9]+).*/\1 \2 \3/p;
866 s/^debug1: using hostkeyalias: ([[:graph:]]*).*/\1/p" ||
: )
867 file=$
(readlink
-f ~
/.ssh
/known_hosts
)
869 echo "khfix: ssh failed"
874 if [[ $alias ]]; then
877 if [[ $port != 22 ]]; then
878 ip_entry
="[$ip]:$port"
879 if [[ ! $alias ]]; then
880 host_entry
="[$host]:$port"
883 if [[ $host_entry != "$ip_entry" ]]; then
885 ssh-keygen
-F "$host_entry" -f $file >$tmp ||
[[ $?
== 1 ]] # 1 when it doesnt exist in the file
886 if [[ -s $tmp ]]; then
887 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
889 echo "khfix WARNING: did not find host entry:$host_entry in known_hosts"
893 grep -Fv "$key" "$file" | sponge
"$file"
898 ssh-keygen
-F "$ip_entry" -f $file >$tmp ||
[[ $?
== 1 ]]
899 if [[ -s $tmp ]]; then
900 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
902 echo "khfix WARNING: did not find ip entry:$ip_entry in known_hosts"
906 grep -Fv "$key" "$file" | sponge
"$file"
909 khfix-r
() { # known hosts fix without syncing to root user
910 _khfix-common
"$@" ||
return 1
914 _khfix-common
"$@" ||
return 1
919 # copy path into clipboard
922 x
=$
(readlink
-nf "${1:-$PWD}")
923 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
924 # And, summarizing this:
925 # https://askubuntu.com/questions/705620/xclip-vs-xsel
926 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
930 # clipboard a string (into selection & clipboard buffer)
932 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
933 # And, summarizing this:
934 # https://askubuntu.com/questions/705620/xclip-vs-xsel
935 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
936 printf "%s" "$*" | xclip
-selection clipboard
937 printf "%s" "$*" | xclip
940 # a1 = awk {print $1}
941 for field
in {1.
.20}; do
942 eval a
$field"() { awk '{print \$$field}'; }"
945 for num
in {1.
.9}; do
946 eval h
$num"() { head -n$num || [[ \$? == 141 ]]; }"
951 # shellcheck disable=SC2046 disable=SC2001 disable=SC2183 # hacks, expected
952 printf '%d.%d.%d.%d\n' $
(echo $1 |
sed 's/../0x& /g')
956 local f out outdir
in fname origdir skip1
960 while [[ $1 == -* ]]; do
962 # if we got interrupted after 1st phase
974 # first pass only uses about 1 cpu, so run in parallel
978 if [[ $f == /* ]]; then
983 out
="$origdir/$outdir/$fname"
984 mkdir
-p /tmp
/vp
9/$fname
986 if ! $skip1 && [[ ! -s ffmpeg2pass-0.log
]]; then
987 # -nostdin or else wait causes ffmpeg to go into stopped state. dunno why, random stackoverflow answer.
988 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
990 if [[ -e $out ]]; then rm -f $out; fi
991 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
998 utcl
() { # utc 24 hour time to local hour 24 hour time
999 echo "print( ($1 $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
1007 # for running in a fai rescue. iank specific.
1009 d
=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
1010 for f
in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V
; do
1011 cryptsetup luksOpen
--key-file /p
/dev
/$f/root crypt-
$f-root
1012 cryptsetup luksOpen
--key-file /p
/dev
/$f/o crypt-
$f-o
1014 mount
-o subvol
=root_trisquelaramo
/dev
/mapper
/crypt-
$d-root /mnt
1015 mount
-o subvol
=a
/dev
/mapper
/crypt-
$d-root /mnt
/a
1016 mount
-o subvol
=o
/dev
/mapper
/crypt-
$d-o /mnt
/o
1017 mount
-o subvol
=boot_trisquelaramo
/dev
/sda2
/mnt
/boot
1025 c4
() { c
/var
/log
/exim4
; }
1027 caa
() { git commit
--amend --no-edit -a; }
1040 find -L "$@" -type f
-not \
( -name .svn
-prune -o -name .git
-prune \
1041 -o -name .hg
-prune -o -name .editor-backups
-prune \
1042 -o -name .undo-tree-history
-prune \
) -printf '%h\0%d\0%p\n' |
sort -t '\0' -n \
1043 |
awk -F '\0' '{print $3}' 2>/dev
/null |
while read -r file; do
1046 # if the file is nonempty and the last char is nonempty, it is not
1047 # newline terminated.
1048 if [[ -s "$file" && "$(tail -c 1 "$file")" ]]; then
1055 calc
() { echo "scale=3; $*" |
bc -l; }
1056 # no having to type quotes, but also no command history:
1060 echo "scale=3; $x" |
bc -l
1071 ccat
() { # config cat. see a config without extra lines.
1072 sed -r '/^[[:space:]]*([;#]|--|\/\/|$)/d' "$@"
1078 # dev/pts needed for pacman signature check
1079 for d
in dev proc sys dev
/pts
; do
1081 if ! mountpoint
$d &>/dev
/null
; then
1082 m s mount
-o bind /$d $d
1088 # dev/pts needed for pacman signature check
1089 for d
in dev
/pts dev proc sys
; do
1091 if mountpoint
$d &>/dev
/null
; then
1099 # join options which are continued to multiples lines onto one line
1101 while IFS
= read -r line
; do
1102 # remove leading spaces/tabs. assumes extglob
1103 if [[ $line == "[ ]*" ]]; then
1104 line
="${line##+( )}"
1109 elif [[ $line == *=* ]]; then
1110 echo "$pastline" >> "$2"
1113 pastline
="$pastline $line"
1115 done < <(grep -vE '^([ \t]*#|^[ \t]*$)' "$1")
1116 echo "$pastline" >> "$2"
1120 # diff config files,
1121 # setup for format of postfix, eg:
1124 local pastline unified f1 f2
1128 _cdiff-prep
"$1" "$f1"
1129 _cdiff-prep
"$2" "$f2"
1130 cat "$f1" "$f2" |
grep -Po '^[^=]+=' |
sort |
uniq > "$unified"
1131 while IFS
= read -r line
; do
1132 # the default bright red / blue doesnt work in emacs shell
1133 dwdiff
-cblue,red
-A best
-d " ," <(grep "^$line" "$f1" ||
echo ) <(grep "^$line" "$f2" ||
echo ) | colordiff
1139 local start
=$SECONDS
1141 # shellcheck disable=SC2030
1142 inotifywait
-m "$dir" -e create
-e moved_to | \
1143 while read -r filedir _
file; do
1146 calc $
((SECONDS
- start
)) / 60
1153 s chown
-R $USER:$USER "$@"
1156 # shellcheck disable=SC2032
1158 # makes it so chown -R symlink affects the symlink and its target.
1159 if [[ $1 == -R ]]; then
1161 command chown
-h "$@"
1162 command chown
-R "$@"
1173 d
() { builtin bg "$@"; }
1176 # f would be more natural, but i already am using it for something
1177 z
() { builtin fg "$@"; }
1180 x
() { builtin kill %%; }
1183 diff --strip-trailing-cr -w "$@" # diff content
1191 safe_rename
"$x" "$y"
1196 # usage: dfp MOUNTPOINT [SECOND_INTERVAL]
1197 # SECOND_INTERVAL defaults to 90
1200 local a b mp interval
1203 if [[ ! $mp ]]; then
1204 echo "dfp: error, missing 1st arg" >&2
1208 a
=$
(df
--output=used
$mp |
tail -n1)
1210 b
=$
(df
--output=used
$mp |
tail -n1)
1211 printf "used mib: %'d mib/min: %s\n" $
(( b
/1000 )) $
(( (b-a
) / (interval
* 1000 / 60 ) ))
1215 # get ipv4 ip from HOST. or if it is already a number, return that
1223 getent ahostsv4
"$host" |
awk '{ print $1 }' |
head -n1
1229 command dig +nostats
+nocmd
"$@"
1231 # Output with sections sorted, and removal of query id, so 2 dig outputs can be diffed.
1235 dig +nordflag
"$@" |
sed -r 's/^(;; ->>HEADER<<-.*), id: .*/\1/' |
while read -r l
; do
1236 if [[ $l == [^\
;]* ]]; then
1240 printf "%s" "$sec" |
sort
1248 # compare digs to the 2 servers
1249 # usage: digdiff @server1 @server2 DIG_ARGS
1250 # note: only the soa master nameserver will respond with
1251 # ra "recursive answer" flag. That difference is meaningless afaik.
1258 digsort
$s1 "$@" |
tee /tmp
/digdiff
1259 diff -u /tmp
/digdiff
<(digsort
$s2 "$@")
1262 # date in a format i like reading
1264 date "+%A, %B %d, %r" "$@"
1269 # date with all digits in a format i like
1273 ccomp
date dt dtr dtd
1275 dus
() { # du, sorted, default arg of
1276 du
-sh ${@:-*} |
sort -h
1281 e
() { printf "%s\n" "$*"; }
1289 printf "%qEOL\n" "${arg}"
1290 printf "%s" "${arg}" |
& hexdump -C
1294 # echo variables. print var including escapes, etc, like xxd for variable
1300 if [[ -v $arg ]]; then
1301 printf "%qEOL\n" "${!arg}"
1302 printf "%s" "${!arg}" |
& hexdump -C
1304 echo arg
$arg is
unset
1310 [[ ${#@} == 2 ]] ||
{ echo "error: ediff requires 2 arguments"; return 1; }
1311 emacs
--eval "(ediff-files \"$1\" \"$2\")"
1315 # shellcheck disable=SC2120 # we expect to pass arguments in use outside this file
1318 tail -F /var
/log
/exim
4/mainlog
/var
/log
/exim
4/*main
/var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
-n 200 "$@"
1322 tail -F /var
/log
/exim
4/mainlog
-n 200 "$@"
1325 tail -F /var
/log
/exim
4/nondmain
-n 200 "$@"
1327 # shortcut for tail -F
1331 ccomp
tail etail etail2 ta
1333 # ran into this online, trying it out
1335 ( "$@" &>/dev
/null
& disown )
1339 ssh "$@" cat .ssh
/authorized_keys
{,2}
1343 # print exim old pids
1345 local configtime pid piduptime now daemonpid
1346 printf -v now
'%(%s)T' -1
1347 configtime
=$
(stat
-c%Y
/var
/lib
/exim
4/config.autogenerated
)
1348 if [[ -s /run
/exim
4/exim.pid
]]; then
1349 daemonpid
=$
(cat /run
/exim
4/exim.pid
)
1351 for pid
in $
(pgrep
-f '^/usr/sbin/exim4( |$)'); do
1352 # the daemonpid gets reexeced on HUP (service reloads), keeping its same old timestamp
1353 if [[ $pid == "$daemonpid" ]]; then
1356 piduptime
=$
(awk -v ticks
="$(getconf CLK_TCK)" 'NR==1 { now=$1; next } END { printf "%9.0f\n", now - ($20/ticks) }' /proc
/uptime RS
=')' /proc
/$pid/stat
) ||
: # sometimes pids disappear pretty fast
1357 if (( configtime
> now
- piduptime
)); then
1363 # exim tail but only watch lines from new pids
1366 for pid
in $
(eoldpids
); do
1369 if [[ $oldpids ]]; then
1370 etail |
awk '$3 !~ /^\[('"${oldpids%|}"')\]$/'
1375 # exim watch as old pids go away
1377 local configtime pid piduptime now tmpstr
1383 mapfile
-t oldpids
<<<"$tmpstr"
1384 if (( ! ${#oldpids[@]} )); then
1387 # print the date every 20 iterations
1388 if (( ! count
% 20 )); then
1392 ps
-f -p "${oldpids[*]}"
1398 less /var
/log
/exim
4/mainlog
1402 exiqgrep
-ir.\
* -o 60 |
while read -r i
; do
1405 hlm exigrep
$i /var
/log
/exim
4/mainlog |
cat ||
:
1409 # other ways to get the list of message ids:
1410 # exim -bp | awk 'NF == 4 {print $3}'
1411 # # this is slower 160ms, vs 60.
1413 exiqgrep
-ir.\
* |
xargs exim
-Mrm
1418 mkdir
-p /tmp
/edev
/etc
1419 cp -ra /etc
/exim4
/tmp
/edev
/etc
1420 cp -ra /etc
/alias* /tmp
/edev
/etc
1421 find /tmp
/edev
/etc
/exim4
-type f
-execdir sed -i "s,/etc/,/tmp/edev/etc/,g" '{}' +
1425 update-exim4.conf
-d /tmp
/edev
/etc
/exim4
-o /tmp
/edev
/e.conf
1429 # show important information about incoming mail in the exim log
1431 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' <${1:-/var/log/exim4/mainlog}
1434 # 2nd line is message-id:
1436 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog}
1444 tail "${tail_arg[@]}" -F /var
/log
/exim
4/mainlog |
sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p'
1451 # find array. make an array of file names found by find into $x
1452 # argument: find arguments
1453 # return: find results in an array $x
1454 while read -rd ''; do
1456 done < <(find "$@" -print0);
1459 # shellcheck disable=SC2120
1460 faf
() { # find all files. use -L to follow symlinks
1461 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1462 -o -name .hg
-prune -o -name .editor-backups
-prune \
1463 -o -name .undo-tree-history
-prune \
) -type f
2>/dev
/null
1466 # usage ffconcat FILES_TO_CONCAT OUTPUT_FILE
1470 printf "file '%s'\n" "$1" >$tmpf
1471 while (( $# > 1 )); do
1473 printf "file '%s'\n" "$1" >>$tmpf
1475 # https://trac.ffmpeg.org/wiki/Concatenate
1476 ffmpeg
-f concat
-safe 0 -i $tmpf -c copy
"$1"
1481 if (( $# == 0 )); then
1482 echo ffremux error expected args
>&2
1487 tmpf
=$tmpd/"${f##*/}"
1488 ffmpeg
-i "$f" -c:v copy
-c:a copy
$tmpf
1496 # absolute path of file/dir without resolving symlinks.
1498 # Most of the time, I want this where I would normally use readlink.
1499 # This is what realpath -s does in most cases, but sometimes it
1500 # actually resolves symlinks, at least when they are in /.
1502 # Note, if run on a dir, if the final component is relative, it won't
1503 # resolve that. Use the below fpd for that.
1505 # note: we could make a variation of this which
1506 # assigns to a variable name using eval, so that we don't have to do
1507 # x=$(fp somepath), which might save subshell overhead and look nice,
1508 # but I'm not going to bother.
1510 local initial_oldpwd initial_pwd dir base
1511 initial_oldpwd
="$OLDPWD"
1513 if [[ $1 == */* ]]; then
1516 # CDPATH because having it set will cause cd to possibly print output
1518 printf "%s%s\n" "$PWD" "$base"
1519 CDPATH
='' cd "$initial_pwd"
1520 OLDPWD
="$initial_oldpwd"
1522 printf "%s/%s\n" "$PWD" "$1"
1525 # full path of directory without resolving symlinks
1527 local initial_oldpwd initial_pwd dir
1528 initial_oldpwd
="$OLDPWD"
1532 printf "%s%s\n" "$PWD" "$base"
1534 OLDPWD
="$initial_oldpwd"
1541 sudo mailq |gr frozen|
awk '{print $3}' |
while read -r id
; do
1547 echo -e '\n\n##############################\n'
1548 done |
tee -a /tmp
/frozen
1552 while read -r line
; do
1553 printf '%s\n' "$line"
1554 ids
+=("$(printf '%s\n' "$line" |gr frozen|awk '{print $3}')")
1556 echo "sleeping for 2 in case you change your mind"
1558 sudo exim
-Mrm "${ids[@]}"
1562 # like -e for functions. returns on error.
1563 # at the end of the function, disable with:
1565 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
1566 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
1572 local help="Usage: getdir [--help] PATH
1573 Output the directory of PATH, or just PATH if it is a directory."
1574 if [[ $1 == --help ]]; then
1578 if [[ $# -ne 1 ]]; then
1579 echo "getdir error: expected 1 argument, got $#"
1582 if [[ -d $1 ]]; then
1586 dir
="$(dirname "$1")"
1587 if [[ -d $dir ]]; then
1590 echo "getdir error: directory does not exist"
1596 git_empty_branch
() { # start an empty git branch. carefull, it deletes untracked files.
1597 [[ $# == 1 ]] ||
{ echo 'need a branch name!'; return 1;}
1599 root
=$
(gitroot
) ||
return 1 # function to set gitroot
1601 git symbolic-ref HEAD refs
/heads
/$1
1606 # shellcheck disable=SC2120
1608 local help="Usage: gitroot [--help]
1609 Print the full path to the root of the current git repo
1611 Handles being within a .git directory, unlike git rev-parse --show-toplevel,
1612 and works in older versions of git which did not have that."
1613 if [[ $1 == --help ]]; then
1618 p
=$
(git rev-parse
--git-dir) ||
{ echo "error: not in a git repo" ; return 1; }
1619 [[ $p != /* ]] && p
=$PWD
1624 # g pipe. like: cmd | emacs. save cmd output to tmp file, then edit.
1630 #like cmd &> tempfile; emacs tempfile
1632 # note: a useful workflow for doing mass replace on my files:
1634 ## remove any false positives, or manually edit them. rename files if needed.
1635 # sedi 's/REGEX/REPLACEMENT/' $(gr '^/' /a/tmp/gtmp)
1640 # g command substitution.
1642 # shellcheck disable=SC2046 # i want word splitting for this hackery
1646 # force terminal version
1652 # quit will prompt if the program crashes.
1653 gdb
-ex=r
-ex=quit
--args emacs
"$@"; r
;
1657 # kill the emacs daemon
1662 grep -iIP --color=auto
"$@" ||
return $?
1664 grr
() { # grep recursive
1665 # Don't return 1 on nonmatch because this is meant to be
1666 # interactive, not in a conditional.
1667 if [[ ${#@} == 1 ]]; then
1668 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -rniIP --color=auto
"$@" . ||
[[ $?
== 1 ]]
1670 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -rniIP --color=auto
"$@" ||
[[ $?
== 1 ]]
1678 # recursive everything. search for files/dirs and lines. rs = easy chars to press
1682 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1683 -o -name .hg
-prune -o -name .editor-backups
-prune \
1684 -o -name .undo-tree-history
-prune \
) 2>/dev
/null |
grep -iP --color=auto
"$query"
1688 # horizontal row. used to break up output
1690 local start end end_count arg
1691 # 180 is long enough. 5 for start.
1692 start
=█████ end
=█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
1693 end_count
=$
(( ${COLUMNS:-180} - 5 ))
1696 end_count
=$
(( end_count
- 2 - ${#arg} ))
1697 start
="$start $arg "
1699 if (( end_count
>= 1 )); then
1700 end
=${end:0:$end_count}
1704 printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)$start$end$(tput sgr0 2>/dev/null||:)"
1708 local col input_len
=0
1710 input_len
=$
((input_len
+ 1 + ${#arg}))
1712 col=$
((60 - input_len
))
1713 printf "\e[1;97;41m%s" "$*"
1714 if (( col > 0 )); then
1715 # shellcheck disable=SC2046 # needed to work as intended. a better way would be like hr above.
1716 printf "\e[1;97;41m \e[0m%.0s" $
(eval echo "{1..${col}}")
1720 hlm
() { hl
"$*"; "$@"; }
1722 hrcat
() { local f
; for f
; do [[ -f $f ]] ||
continue; hr
; echo "$f"; cat "$f"; done }
1725 # github-release-dl restic/restic restic_ _linux_amd64.bz2
1727 # https://github.com/restic/restic/releases/download/v0.16.3/restic_0.16.3_linux_amd64.bz2
1728 github-release-dl
() {
1729 local github_path file_prefix file_suffix latest_prefix version redir_path
1733 if (( $# != 3 )); then
1734 echo "$0: error, expected 3 arguments" >&2
1737 redir_path
="https://github.com/$github_path/releases/latest/download/"
1738 latest_prefix
=$
(curl
-s -I "$redir_path" |
awk 'tolower($1) == "location:" {print $2}')
1739 # it has a trailing /r at the end. just kill any whitespace.
1740 latest_prefix
="${latest_prefix//[$'\t\r\n ']}"
1741 if [[ ! $latest_prefix ]]; then
1742 echo "failed to find latest path. Tried to find case insensitive 'location:' in the curl output:"
1743 m curl
-s -I "$redir_path"
1746 version
="${latest_prefix##*/}"
1747 version
="${version#v}"
1748 m wget
-- "$latest_prefix/$file_prefix$version$file_suffix"
1752 # go-github-install restic/restic restic_ _linux_amd64.bz2
1753 # go-github-install restic/rest-server rest-server_ _linux_amd64.tar.gz
1755 # common pattern among go binaries on github
1756 go-github-install
() {
1757 local tmpd targetf tmp files src
1762 tmp
="${file_prefix##*[[:alnum:]]}"
1763 targetf
="${file_prefix%"$tmp"}"
1764 echo targetf
: $targetf
1765 github-release-dl
"$@"
1767 case $file_suffix in
1775 rm -f -- "${files[@]}"
1777 # Here we detect and handle 2 cases: either we extracted a single
1778 # binary which we have to rename or a folder with a binary named
1779 # $targetf in it which is all we care about.
1780 if (( ${#files[@]} == 1 )) && [[ -f ${files[0]} ]]; then
1782 mv -- .
/* /usr
/local
/bin
/$targetf
1784 files
=(.
/*/$targetf)
1785 if [[ -f $targetf ]]; then
1787 elif [[ -f ${files[0]} ]]; then
1791 mv -- "$src" /usr
/local
/bin
1797 ## 2024: I'm using gh instead of hub, but leaving this just in case.
1798 ## I tried the github cli tool (gh) and it seems easier than
1801 ## hub predated github's 2020 official cli tool gh.
1803 ## https://raw.githubusercontent.com/cli/cli/trunk/docs/gh-vs-hub.md
1804 # get latest hub and run it
1805 # main command to use:
1806 # hub pull-request --no-edit
1807 # --no-edit means to use the first commit\'s message as the pull request message.
1808 # If that fails, try doing
1809 # hub pull-request --no-edit -b UPSTREAM_OWNER:branch
1810 # where branch is usually master. it does the pr against your current branch.
1812 # On first use, you input username/pass and it gets an oath token so you dont have to repeat
1813 # it\'s at ~/.config/hub
1815 local up uptar updir p re
1816 # example https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz
1817 up
=$
(wget
-q -O- https
://api.github.com
/repos
/github
/hub
/releases
/latest | jq
-r .assets
[].browser_download_url |
grep linux-amd64
)
1819 if [[ ! $up ||
$up =~
$re ]]; then
1820 echo "failed to get good update url. got: $up"
1824 if [[ ! -e /a
/opt
/$updir ]]; then
1825 rm -rf /a
/opt
/hub-linux-amd64
*
1827 tar -C /a
/opt
-zxf /a
/opt
/$uptar
1830 if ! which hub
&>/dev
/null
; then
1831 sudo
/a
/opt
/$updir/install
1834 # save token across computers
1835 if [[ ! -L ~
/.config
/hub
]]; then
1836 if [[ -e ~
/.config
/hub
]]; then
1837 mv ~
/.config
/hub
/p
/c
/subdir_files
/.config
/
1839 if [[ -e /p
/c
/subdir_files
/.config
/hub
]]; then
1853 # cvs update -C FILE
1858 # potentially useful command translation
1859 # https://fling.seas.upenn.edu/~giesen/dynamic/wordpress/equivalent-commands-for-git-svn-and-cvs/
1861 # importing cvs repo into git using git-cvs package:
1862 # /f/www $ /usr/lib/git-core/git-cvsimport -C /f/www-git
1878 find -L "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1879 -o -name .hg
-prune -o -name .editor-backups
-prune \
1880 -o -name .undo-tree-history
-prune \
) -iname "*$glob*" 2>/dev
/null
1884 # insensitive find here. args are combined into the search string.
1885 # -L = follow symlinks
1886 find -L .
-not \
( -name .svn
-prune -o -name .git
-prune \
1887 -o -name .hg
-prune -o -name .editor-backups
-prune \
1888 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1892 # insensitive find directory
1893 find -L .
-type d
-not \
( -name .svn
-prune -o -name .git
-prune \
1894 -o -name .hg
-prune -o -name .editor-backups
-prune \
1895 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1900 sudo iptables
-A INPUT
-s $1 -j DROP
1905 grep -Il "" "$@" &>/dev
/null
1912 # journalctl with times in the format the --since= and --until= options accept
1913 jrt
() { journalctl
-e -n100000 -o short-full
"$@"; }
1914 jr
() { journalctl
-e -n100000 "$@" ; }
1915 jrf
() { journalctl
-n1000 -f "$@" ; }
1917 # the invocation id is "assigned each time the unit changes from an inactive
1918 # state into an activating or active state" man systemd.exec
1919 journalctl
-e --no-tail -u exim4 _SYSTEMD_INVOCATION_ID
="$(systemctl show -p InvocationID --value $1)"
1921 ccomp journalctl jr jrf jru
1926 if [[ $PWD == /[iap
] ]]; then
1927 command ls -A --color=auto
-I lost
+found
"$@"
1929 command ls -A --color=auto
"$@"
1933 lcn
() { locate -i "*$**"; }
1935 lg
() { LC_COLLATE
=C.UTF-8 ll
--group-directories-first "$@"; }
1937 lt
() { ll
-tr "$@"; }
1939 lld
() { ll
-d "$@"; }
1941 ccomp
ls l lg lt lld ll
1947 for dirs in false true
; do
1949 if [[ -d $f ]]; then
1951 # reverse the order to rename the nested dirs first.
1952 # note: 0 element is the dir itself
1953 for ((i
=${#all[@]}-1; i
>=1; i--
)); do
1955 if $dirs && [[ -d $a ]]; then
1956 # e dirs low "$a" # debug
1958 elif ! $dirs && [[ ! -d $a && -e $a ]]; then
1960 # e not dirs low "$a" # debug
1965 # just rename all the top level args on the second pass
1967 # e final dirs low "$f" # debug
1974 low
() { # make filenames lowercase, remove bad chars
1977 arg
="${arg%%+(/)}" # remove trailing slashes. assumes we have extglob on.
1979 if (( ${#dir} == ${#arg} )); then
1983 new
="${f,,}" # downcase
1984 # shellcheck disable=SC2031 # seems like a shellcheck bug
1985 new
="${new//[^a-zA-Z0-9._-]/_}" # sub bad chars
1986 new
="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
1987 new
="${new%"${new##*[[:alnum:]]}"}"
1988 # remove bad underscores, like __ and _._
1989 new
=$
(echo $new |
sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
1990 safe_rename
"$dir/$f" "$dir/$new" ||
return 1
1995 lower
() { # make first letter of filenames lowercase.
1998 if [[ ${x::1} == [A-Z
] ]]; then
1999 y
=$
(tr '[:upper:]' '[:lower:]' <<<"${x::1}")"${x:1}"
2000 safe_rename
"$x" "$y" ||
return 1
2006 k
() { # history search
2007 grep -iP --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
tail -n 80 ||
[[ $?
== 1 ]];
2009 ks
() { # history search with context
2010 # args are an extended regex used by sed
2011 history |
sed -nr "h;s/^\s*(\S+\s+){4}//;/$*/{g;p}" |
tail -n 80 ||
[[ $?
== 1 ]];
2013 ksu
() { # history search unique
2014 grep -P --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
uniq ||
[[ $?
== 1 ]];
2017 # remove lines from history matching $1
2019 # todo: id like to do maybe a daily or hourly cronjob to
2020 # check that my history file size is increasing. Ive had it
2021 # inexplicably truncated in the past.
2024 HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/'
2025 read -r -p "press anything but contrl-c to delete"
2026 for entry
in $
(HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/ { print a }' |
tac); do
2032 # history without the date
2034 history "$@" | cut
-d' ' -f 7-
2037 ccomp
grep k ks ksu histrm
2041 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
2042 make -qp |
awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
2054 # mkdir the last arg, cp the rest into it
2057 cp "${@:1:$#-1}" "${@: -1}"
2061 mv "${@:1:$#-1}" "${@: -1}"
2064 mkt
() { # mkdir and touch file
2066 mkdir
-p "$(dirname "$path")"
2070 # shellcheck disable=SC2032
2071 mkdir
() { command mkdir
-p "$@"; }
2074 # https://github.com/HenriWahl/Nagstamon/issues/357
2075 if ! pgrep
-f /usr
/bin
/dunst
>/dev
/null
; then
2078 /usr
/bin
/nagstamon
&
2083 screen
-L profanity a
2086 # i dont want to wait for konsole to exit...
2088 command prof
&>/dev
/null
&
2093 printf '\033[1A\033[K'; printf "%s\n" "$l"| ts
"%F %T" |
tee -a /p
/self-chat.log
2098 # cant use s because sudo -i doesnt work for passwordless sudo command
2101 sudo nmtui-connect
"$@"
2111 if shopt nullglob
>/dev
/null
; then
2125 # shellcheck disable=SC2024
2127 for f
in /var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
; do
2129 if [[ -s $f ]]; then
2130 echo ================== $f =============
2131 s
tee -a /var
/log
/exim
4/$base-archive <$f
2139 ping() { command ping -O "$@"; }
2140 p8
() { ping "$@" 8.8.8.8; }
2141 p6
() { ping6
"$@" 2001:4860:4860::8888; }
2143 pkx
() { # package extract
2144 local pkg cached tmp f
2147 # shellcheck disable=SC2012
2148 cached
=$
(ls -t /var
/cache
/apt
/archives
/${pkg}_
* 2>/dev
/null |
tail -n1 2>/dev
/null
) ||
:
2149 if [[ $cached ]]; then
2152 m aptitude download
$pkg ||
return 1
2154 tmp
=(*); f
=${tmp[0]} # only 1 expected
2163 tmpf
=$
(pgrep
-f "$*")
2164 mapfile
-t pids
<<<"$tmpf"
2167 # shellcheck disable=SC2128
2173 0) echo "no pid found" ;;
2182 help="Usage: psg [--help] GREP_ARGS
2183 grep ps and output in a nice format"
2184 if [[ $1 == --help ]]; then
2189 # final grep is because some commands tend to have a lot of trailing spaces
2190 y
=$
(echo "$x" |
sed -r 's,//[^[:space:]:@/]+:[^[:space:]:@/]+@,//REDACTED_URL_USER@PASS/,g' |
grep -iP "$@" |
grep -o '.*[^ ]') ||
:
2192 echo "$x" |
head -n 1 ||
[[ $?
== 141 ]]
2197 pubip
() { curl
-4s https
://icanhazip.com
; }
2198 pubip6
() { curl
-6s https
://icanhazip.com
; }
2199 whatismyip
() { pubip
; }
2202 q
() { # start / launch a program in the backround and redir output to null
2206 # shellcheck disable=SC2120
2208 if [[ $HISTFILE ]]; then
2209 history -a # save history
2211 trap ERR
# this avoids a segfault
2213 # i had this redir, not sure why
2214 # exit "$@" 2>/dev/null
2217 # scp is insecure and deprecated.
2219 rsync
-Pt --inplace "$@"
2224 # available high ports are 1024-65535,
2225 # but lets skip things that are more likely to be in use
2228 print(secrets.SystemRandom().randrange(10002,65500))
2234 # shellcheck disable=SC1090 # expected to not follow
2244 # rsync, root is required to keep permissions right.
2245 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
2246 # --no-times --delete
2247 # basically, make an exact copy, use checksums instead of file times to be more accurate
2248 rsync
-ahvic --delete "$@"
2251 # like rlu, but dont delete files on the target end which
2252 # do not exist on the original end.
2256 # rl without preserving modification time.
2257 rsync
-ahvic --delete --no-t "$@"
2259 # [RSYNC_OPTS] HOST PATH
2261 # eg. rsu -opts frodo /testpath
2262 # relative paths will expanded with readlink -f.
2263 opts
=("${@:1:$#-2}") # 1 to last -2
2264 path
="${*:$#}" # last
2265 host="${*:$#-1:1}" # last -1
2266 if [[ $path == .
* ]]; then
2267 path
=$
(readlink
-f $path)
2269 m rsync
-ahvi --relative --no-implied-dirs "${opts[@]}" "$path" "root@$host:/";
2271 ccomp rsync rsd rsa rst rsu
2273 # find programs listening on a port
2276 # to figure out these args, i had to look at the man page from git version, as of 2022-04.
2277 s ss
-lpn state listening sport
= $port
2282 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2287 hr
; s ss
-lpn sport
= 53
2288 if systemctl is-enabled dnsmasq
&>/dev
/null ||
[[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2289 # this will fail is dnsmasq is failed
2290 hr
; m ser status dnsmasq |
cat ||
:
2292 hr
; echo $f:; ccat
$f
2293 hr
; m grr
'^ *(servers-file|server) *=|^ *no-resolv *$' /etc
/dnsmasq.conf
/etc
/dnsmasq.d
2294 f
=/etc
/dnsmasq-servers.conf
2295 hr
; echo $f:; ccat
$f
2298 echo /etc
/nsswitch.conf
:
2299 grep '^ *hosts:' /etc
/nsswitch.conf
2300 if systemctl is-enabled systemd-resolved
&>/dev
/null ||
[[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2301 hr
; m ser status systemd-resolved |
cat ||
:
2302 hr
; m resolvectl status |
cat
2310 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2314 m sudo nscd
-i hosts
2316 if [[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2317 m sudo systemctl restart dnsmasq
2319 if [[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2320 m sudo systemctl restart systemd-resolved
2322 if type -P resolvectl
&>/dev
/null
; then
2323 resolvectl flush-caches
2327 # add annoyingly long argument which should be the default
2329 sed -i --follow-symlinks "$@"
2334 # todo: test variable assignment with newlines here.
2335 # https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
2337 # beware that it only works on the assumption that any special
2338 # characters in the input string are intended to be escaped, not to work
2339 # as special chacters.
2341 LC_ALL
=C
sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
2345 ssh fencepost
head -n 300 /gd
/gnuorg
/EventAndTravelInfo
/rms-current-trips.txt |
less
2354 command sudo
"$@" ||
return $?
2359 # I use a function because otherwise we cant use in a script,
2360 # cant assign to variable.
2362 # note: gksudo is recommended for X apps because it does not set the
2363 # home directory to the same, and thus apps writing to ~ fuck things up
2364 # with root owned files.
2366 if [[ $EUID != 0 ||
$1 == -* ]]; then
2367 # shellcheck disable=SC2034
2368 SUDOD
="$PWD" command sudo
-i "$@"
2374 sb
() { # sudo bash -c
2375 # use sb instead of s is for sudo redirections,
2376 # eg. sb 'echo "ok fine" > /etc/file'
2377 # shellcheck disable=SC2034
2379 sudo
-i bash
-c "$@"
2382 se
() { s urun
0077 "$@"; }
2385 safe_rename
() { # warn and dont rename if file exists.
2386 # mv -n exists, but it\'s silent
2387 if [[ $# != 2 ]]; then
2388 echo safe_rename error
: $# args
, need
2 >&2
2391 if [[ $1 != "$2" ]]; then # yes, we want to silently ignore this
2392 if [[ -e $2 ||
-L $2 ]]; then
2393 echo "Cannot rename $1 to $2 as it already exists."
2402 sudo
dd status
=none of
="$1"
2406 if type -p systemctl
&>/dev
/null
; then
2409 if (( $# >= 3 )); then
2410 echo iank
: ser expected
2 or
less arguments
2417 systemctl
-n 40 status
"$@"
2420 # assume last arg is a service and we want to tail its log.
2422 local service jr_pid ret
2425 journalctl
-qn2 -f -u "$service" &
2427 s systemctl
"$@" || ret
=$?
2430 (( ret
== 0 )) ||
return $ret
2433 seru
() { systemctl
--user "$@"; }
2434 # like restart, but do nothing if its not already started
2437 if [[ $
(s systemctl
--no-pager show
-p ActiveState
$service ) == ActiveState
=active
]]; then
2438 systemctl restart
$service
2442 setini
() { # set a value in a .ini style file
2443 key
="$1" value
="$2" section
="$3" file="$4"
2444 if [[ -s $file ]]; then
2445 sed -ri -f - "$file" <<EOF
2446 # remove existing keys
2447 / *\[$section\]/,/^ *\[[^]]+\]/{/^\s*${key}[[:space:]=]/d}
2449 /^\s*\[$section\]/a $key=$value
2450 # from section to eof, do nothing
2451 /^\s*\[$section\]/,\$b
2452 # on the last line, if we haven't found section yet, add section and key
2464 sgo
() { # service go
2466 ser restart
$service ||
return 1
2467 if type -p systemctl
&>/dev
/null
; then
2473 # ignore services that dont exist
2474 if systemctl
cat $service &>/dev
/null
; then
2476 ser disable
$service
2482 systemctl list-unit-files | rg
"$@"
2485 # check whether we generally want to do sk on the file
2487 [[ ! -L $f ]] && istext
"$1" && [[ $
(head -n1 "$1" 2>/dev
/null
) == '#!/bin/bash'* ]]
2491 # see https://savannah.gnu.org/maintenance/fsf/bash-style-guide/ for justifications
2492 local quotes others ret
2493 quotes
=2048,2068,2086,2206,2254
2494 others
=2029,2032,2033,2054,2164
2495 shellcheck
-x -W 999 -e $quotes,$others "$@" || ret
=$?
2496 if (( ret
>= 1 )); then
2497 echo "A template comment to disable is now in clipboard. eg: # shellcheck disable=SC2206 # reason"
2498 cbs
"# shellcheck disable=SC"
2503 # sk with quotes. For checking scripts that we expect to take untrusted
2504 # input in order to verify we quoted vars.
2507 others
=2029,2033,2054,2164
2508 shellcheck
-W 999 -x -e $others "$@" ||
return $?
2511 # sk on all modified & new files in current git repo. must git add for new files.
2514 for f
in $
(i s |
awk '$1 == "modified:" {print $2}; $1 == "new" {print $3}'); do
2522 # sk on all the files in current git repo
2524 local f toplevel orig_dir tmp
2525 local -a ls_files sk_files
2526 toplevel
=$
(git rev-parse
--show-toplevel)
2527 if [[ $PWD != "$toplevel" ]]; then
2531 # tracked & untracked files
2532 tmp
=$
(git ls-files
&& git ls-files
--others --exclude-standard)
2533 mapfile
-t ls_files
<<<"$tmp"
2534 for f
in "${ls_files[@]}"; do
2540 if [[ $orig_dir ]]; then
2546 # sl: ssh, but firsh rsync our bashrc and related files to a special
2547 # directory on the remote host if needed.
2549 # Some environment variables and files need to be setup for this to work
2550 # (mine are set at the beginning of this file)
2552 # SL_FILES_DIR: Environment variable. Path to folder which should at
2553 # least have a .bashrc file or symlink. This dir will be rsynced to ~ on
2554 # remote hosts (top level symlinks are resolved) unless the host already
2555 # has a $SL_FILES_DIR/.bashrc. In that case, we assume it is a host you
2556 # control and sync files to separately and already has the ~/.bashrc you
2557 # want. The remote bash will also take its .inputrc config from this
2558 # folder (default of not existing is fine). Mine looks like this:
2559 # https://iankelling.org/git/?p=distro-setup;a=tree;f=sl/.iank
2561 # SL_INFO_DIR: Environment variable. This folder stores info about what
2562 # we detected on the remote system and when we last synced. It will be created
2563 # if it does not exist. Sometimes you may want to forget about a
2564 # remote system, you can use sl --rsync, or the function for that slr
2567 # SL_TEST_CMD: Env var. Meant to be used to vary the files synced
2568 # depending on the remote host. Run this string on the remote host the
2569 # first time sl is run (or if we run slr). The result is passed to
2570 # SL_TEST_HOOK. For example,
2571 # export SL_TEST_CMD=". /etc/os-release ; echo \${VERSION//[^a-zA-Z0-9]/}"
2573 # SL_TEST_HOOK: Env var. It is run as $SL_TEST_HOOK. This can set
2574 # $SL_FILES_DIR to vary the files synced.
2576 # SL_RSYNC_ARGS: Env var. String of arguments passed to rsync. For
2577 # example to exclude files within a directory. Note, excluded
2578 # files wont be deleted on rsync, you can add --delete-excluded
2579 # to the rsync command if that is desired.
2581 # SL_SSH_ARGS: Env var. Default arguments passed to ssh.
2583 # For when ~/.bashrc is already customized on the remote server, you
2584 # might find it problematic that ~/.bashrc is sourced for ALL ssh
2585 # commands, even in scripts. This paragraph is all about that. bash
2586 # scripts dont source ~/.bashrc, but call ssh in scripts and you get
2587 # ~/.bashrc. You dont want this. .bashrc is meant for interactive shells
2588 # and if you customize it, probably has bugs from time to time. This is
2589 # bad. Here's how I fix it. I have a special condition to "return" in my
2590 # .bashrc for noninteractive ssh shells (copy that code). Then use this
2591 # function or similar that passes LC_USEBASHRC=t when sshing and I want
2592 # my bashrc. Also, I don't keep most of my bashrc in .bashrc, i source a
2593 # separate file because even if I return early on, the whole file gets
2594 # parsed which can fail if there is a syntax error.
2596 # Background on LC_USEBASHRC var (no need to read if you just want to
2597 # use this function): env variables sent across ssh are strictly
2598 # limited, but we get LC_* at least in debian based machines, so we
2599 # just make that * be something no normal program would use. Note, on
2600 # hosts that dont allow LC_* I start an inner shell with LC_USEBASHRC
2601 # set, and the inner shell also allows running a nondefault
2602 # .bashrc. This means the outer shell still ran the default .bashrc,
2603 # but that is the best we can do.
2605 local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2
type info_sec force_rsync \
2606 sync_dirname testcmd extra_info testbool files_sec sl_test_cmd sl_test_hook
2607 declare -a args tmpa
2611 # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
2612 # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address]
2613 # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option]
2614 # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname
2617 # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
2618 # [-D [bind_address:]port] [-E log_file] [-e escape_char]
2619 # [-F configfile] [-I pkcs11] [-i identity_file]
2620 # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]
2621 # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
2622 # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
2625 if [[ $1 == --rsync ]]; then
2629 # shellcheck disable=SC2153 # intentional
2630 sl_test_cmd
=$SL_TEST_CMD
2631 # shellcheck disable=SC2153 # intentional
2632 sl_test_hook
=$SL_TEST_HOOK
2633 # shellcheck disable=SC2153 # intentional
2634 sl_rsync_args
=$SL_RSYNC_ARGS
2661 # note we dont support things like -4oOption
2662 -[46AaCfGgKkMNnqsTtVvXxYy
]*)
2665 -[bcDEeFIiJLlmOopQRSWw
]*)
2666 # -oOption etc is valid
2667 if (( ${#1} >= 3 )); then
2670 args
+=("$1" "$2"); shift 2
2679 if [[ ! $remote ]]; then
2680 echo $0: error hostname required
>&2
2685 if [[ ! $SL_INFO_DIR ]]; then
2686 echo 'error: missing SL_INFO_DIR env var' >&2
2692 tmpa
=($SL_INFO_DIR/???????????
"$remote")
2694 if [[ -e $sshinfo ]]; then
2695 if $force_rsync; then
2702 tmp
=${sshinfo[0]##*/}
2705 extra_info
=$
(cat $sshinfo)
2707 # we test for string to know ssh succeeded
2708 testbool
="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
2709 testcmd
="if $testbool; then printf y; else printf n; fi"
2710 if ! tmp
=$
(LC_USEBASHRC
=y
command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
2711 echo failed sl
test. doing plain
ssh -v
2712 command ssh -v "${args[@]}" "$remote"
2714 if [[ $tmp == y
* ]]; then
2720 extra_info
="${tmp:1}"
2722 if [[ $sl_test_hook ]]; then
2723 RSYNC_RSH
="ssh ${args[*]}" $sl_test_hook "$extra_info" "$remote"
2726 if $haveinfo && [[ $type == b
]]; then
2728 read -r files_sec _
< <(find -L $SL_FILES_DIR -printf "%T@ %p\n" |
sort -nr ||
[[ $?
== 141 ||
${PIPESTATUS[0]} == 32 ]] )
2729 files_sec
=${files_sec%%.*}
2730 if (( files_sec
> info_sec
)); then
2736 sync_dirname
=${SL_FILES_DIR##*/}
2738 if [[ ! $SL_FILES_DIR ]]; then
2739 echo 'error: missing SL_FILES_DIR env var' >&2
2744 RSYNC_RSH
="ssh ${args[*]}" m rsync
-rptL --delete $sl_rsync_args $SL_FILES_DIR "$remote":
2746 if $dorsync ||
! $haveinfo; then
2747 sshinfo
=$SL_INFO_DIR/$EPOCHSECONDS$type"$remote"
2748 [[ -e $SL_INFO_DIR ]] || mkdir
-p $SL_INFO_DIR
2749 printf "%s\n" "$extra_info" >$sshinfo
2752 if [[ $type == b
]]; then
2753 if (( ${#@} )); then
2754 # Theres a couple ways to pass arguments, im not sure whats best,
2755 # but relying on bash 4.4+ escape quoting seems most reliable.
2756 command ssh "${args[@]}" "$remote" \
2757 LC_USEBASHRC
=t bash
-c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash
${@@Q}
2758 elif [[ ! -t 0 ]]; then
2759 # This case is when commands are being piped to ssh.
2760 # Normally, no bashrc gets sourced.
2761 # But, since we are doing all this, lets source it because we can.
2762 cat <(echo .
$sync_dirname/.bashrc
) - |
command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2764 command ssh -t "${args[@]}" "$remote" LC_USEBASHRC
=t INPUTRC
=$sync_dirname/.inputrc bash
--rcfile $sync_dirname/.bashrc
2768 LC_USEBASHRC
=t
command ssh "${args[@]}" "$remote" ${@@Q}
2770 command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2773 # this function inspired from https://github.com/Russell91/sshrc
2783 # WARNING: If you are trying to use -i, remember that keys added to
2784 # agent previously will still be tried. Use ssh-add -D to remove all
2785 # keys from the agent.
2787 ssh -oControlMaster=no
-oControlPath=/ "$@"
2789 # kill off old shared socket then ssh
2791 m
ssh -O exit "$@" ||
[[ $?
== 255 ]]
2794 ccomp
ssh sl slr sss ssk
2797 LC_USEBASHRC
=t
command ssh "$@"
2802 # log with script. timing is $1.t and script is $1.s
2803 # -l to save to ~/typescripts/
2804 # -t to add a timestamp to the filenames
2805 local logdir do_stamp arg_base
2806 (( $# >= 1 )) ||
{ echo "arguments wrong"; return 1; }
2809 while getopts "lt" option
2812 l
) arg_base
=$logdir ;;
2815 echo error
: bad option
2820 shift $
((OPTIND
- 1))
2822 [[ -e $logdir ]] || mkdir
-p $logdir
2823 $do_stamp && arg_base
+=$
(date +%F.
%T
%z
)
2824 script -t $arg_base.s
2> $arg_base.t
2826 splay
() { # script replay
2827 #logRoot="$HOME/typescripts/"
2828 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
2829 scriptreplay
"$1.t" "$1.s"
2833 # sudo redo. be aware, this command may not work right on strange distros or earlier software
2834 if [[ $# == 0 ]]; then
2835 sudo
-E bash
-c -l "$(history -p '!!')"
2837 echo this
command redos last
history item. no argument is accepted
2842 # with -ll, less secure but faster.
2843 command srm
-ll "$@"
2848 ssh $1 "/tmp/${2##*/}" "$(printf "%q
\n" "${@:2}")"
2860 tclock
() { # terminal clock
2865 # this goes to full width
2866 #len=${1:-$((COLUMNS -7))}
2869 if (( x
== len
)); then
2871 d
="$(date +%l:%_M) "
2874 d
=$
(date +%l
:%M
:%_S
)
2878 for ((i
=0; i
<x
; i
++)); do
2879 if (( i
% 6 )); then
2897 # test existence / exists
2900 [[ -e "$x" ||
-L "$x" ]] || ret
=1
2906 # normally, i would just execute these commands in the function.
2907 # however, DEBUG is not inherited, so we need to run it outside a function.
2908 # And we want to run set -x afterwards to avoid spam, so we cram everything
2909 # in here, and then it will run after this function is done.
2910 # shellcheck disable=SC2178 # intentional
2911 PROMPT_COMMAND
='trap DEBUG; unset PROMPT_COMMAND; PS1=" \w \$ "'
2915 # shellcheck disable=SC2178 # intentional
2916 PROMPT_COMMAND
='trap DEBUG; unset PROMPT_COMMAND'
2917 PS1
='\[\e]133;L\a\]\[\e]133;D;$?\]\[\e]133;A\a\]\w \$ \[\e]133;B\a\]' ;
2918 PS2
='\[\e]133;A\a\]'$PS2'\[\e]133;B\a\]' ;
2919 PS0
='\[\e]133;C\a\]'
2924 PROMPT_COMMAND
=(prompt-command
)
2925 if [[ $TERM == *(screen
*|xterm
*|rxvt
*) ]]; then
2926 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
2930 # prometheus node curl
2933 host=${1:-127.0.0.1}
2934 s curl
--cert-type PEM
--cert /etc
/prometheus
/ssl
/prometheus_cert.pem
--key /etc
/prometheus
/ssl
/prometheus_key.pem
--cacert /etc
/prometheus
/ssl
/prom_node_cert.pem
--resolve prom_node
:9100:$host -v https
://prom_node
:9100/metrics
2937 tx
() { # toggle set -x, and the prompt so it doesnt spam
2938 if [[ $
- == *x
* ]]; then
2947 # show all processes in the network namespace $1.
2948 # blank entries appear to be subprocesses/threads
2952 sudo
find -L /proc
/[1-9]*/task
/*/ns
/net
-samefile /run
/netns
/$netns | cut
-d/ -f5 | \
2954 x
=$
(ps
-w --no-headers -p $l);
2955 if [[ $x ]]; then echo "$x"; else echo $l; fi;
2959 if ! s ip netns list |
grep -Fx nonet
&>/dev
/null
; then
2960 s ip netns add nonet
2962 sudo
-E env
/sbin
/ip netns
exec nonet sudo
-E -u iank
/bin
/bash
2965 m
() { printf "%s\n" "$*"; "$@"; }
2966 m2
() { printf "%s\n" "$*" >&2; "$@"; }
2971 if type -p uprecords
&>/dev
/null
; then
2979 for x
in "$@"; do virsh destroy
"$x"; virsh undefine
"$x"; done
2987 sudo virsh dumpxml
$vm |
sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
2988 sed -r "s/listen='[^']+/listen='$ip/"> $t
2989 sudo virsh undefine
$vm
2990 sudo virsh define
$t
2995 vm-set-listen
$1 0.0.0.0
3000 vm-set-listen
$1 127.0.0.1
3005 interfaces
=$
(iw dev |
awk '$1 == "Interface" {print $2}')
3006 for i
in $interfaces; do
3007 echo "myiwscan: considering $i"
3008 # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines.
3009 # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h}
3010 sudo iw dev
$i scan |
sed -rn "
3011 s/^\Wcapability: (.*)/\1/;Ta;h;b
3012 :a;s/^\Wsignal: -([^.]+).*/\1/;Tb;H;b
3013 # padded to min width of 20
3014 :b;s/\WSSID: (.*)/\1 /;T;s/^(.{20}(.*[^ ])?) */\1/;H;g;s/(.*)\n(.*)\n(.*)/\2 \3 \1/gp;b
3019 # Run script by copying it to a temporary location first,
3020 # and changing directory, so we don't have any open
3021 # directories or files that could cause problems when
3038 # spark 1 5 22 13 53
3042 # Copyright (c) Zach Holman, https://zachholman.com
3043 # https://github.com/holman/spark
3045 # As of 2022-10-28, I reviewed github forks that had several newer
3046 # commits, none had anything interesting. I did a little refactoring
3047 # mostly to fix emacs indent bug.
3049 # Generates sparklines.
3052 if [ "X$1" = "X-n" ]; then
3066 # find min/max values
3067 local min
=0xffffffff max
=0
3071 # on Linux (or with bash4) we could use `printf %.0f $n` here to
3072 # round the number but that doesn't work on OS X (bash3) nor does
3073 # `awk '{printf "%.0f",$1}' <<< $n` work, so just cut it off
3075 (( n
< min
)) && min
=$n
3076 (( n
> max
)) && max
=$n
3077 numbers
=$numbers${numbers:+ }$n
3081 local ticks
=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █
)
3083 # use a high tick if data is constant
3084 (( min
== max
)) && ticks
=(▅ ▆
)
3087 f
=$
(( ( (max-min
) <<8)/( tc - 1) ))
3092 _spark_echo -n ${ticks[$(( ((n-min)<<8)/f ))]}
3097 pdfwc() { local f; for f; do echo "$f" "$(pdfinfo "$f" | awk '/^Pages:/ {print $2}')"; done }
3100 # nvm install script appended this to my .bashrc. I dont want to run it all the time,
3101 # so put it in a function.
3103 export NVM_DIR="$HOME/.nvm"
3104 # shellcheck disable=SC1091 # may not exist, & third party
3105 [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" # This loads nvm
3106 # shellcheck disable=SC1091 # may not exist, & third party
3107 [ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion" # This loads nvm bash_completion
3112 if date -d 'february 29' &>/dev/null; then
3122 if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
3129 # make vim work with my light colortheme terminal.
3131 if [[ -e ~/.vimrc ]]; then
3134 command vim -c ':colorscheme peachpuff' "$@"
3138 # ls count. usage: pass a directory, get the number of files.
3139 # https://unix.stackexchange.com/questions/90106/whats-the-most-resource-efficient-way-to-count-how-many-files-are-in-a-director
3141 # shellcheck disable=SC2790 disable=SC2012 # intentional
3145 # run then notify. close notification after the next prompt.
3148 dunstify -u critical -h string:x-dunst-stack-tag:profanity "$*"
3149 _psrun=(dunstctl close-all)
3152 dunstify -u critical -h string:x-dunst-stack-tag:profanity n
3153 _psrun=(dunstctl close-all)
3159 # shellcheck disable=SC2030
3160 inotifywait -m "$dir" -e create -e moved_to | while read -r _ _ file; do
3174 if ! type -p sponge &>/dev/null; then
3175 echo "$0: error: missing dependency: sudo apt install moreutils" >&2
3180 echo "adding header to $f"
3181 if [[ -s $f ]]; then
3186 cat - "${f_maybe[@]}" <<EOF | sponge "$f"
3187 The following is the GNU All-permissive License as recommended in
3188 <https://www.gnu.org/licenses/license-recommendations.en.html>
3190 Copyright (C) $(date +%Y) Free Software Foundation <sysadmin@fsf.org>
3192 Copying and distribution of this file, with or without modification,
3193 are permitted in any medium without royalty provided the copyright
3194 notice and this notice are preserved. This file is offered as-is,
3195 without any warranty.
3197 Contributions are welcome. See <https://savannah.gnu.org/maintenance/fsf/>.
3203 # note, there is also the tool gron which is meant for this, but
3204 # this is good enough to not bother installing another tool
3206 # https://stackoverflow.com/questions/59700329/how-to-print-path-and-key-values-of-json-file-using-jq
3207 jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' "$@"
3211 "$@" |& ts || return $?
3218 if $use_color && type -p tput &>/dev/null; then
3219 # this is nice for a dark background terminal:
3220 # https://github.com/trapd00r/LS_COLORS
3221 # I would like if there was something similar for light.
3223 # https://www.bigsoft.co.uk/blog/2008/04/11/configuring-ls_colors
3224 # change the hard to read turqouise.
3225 # defaults dircolors --print-database.
3227 # the default bold green is too light.
3228 # this explains the codes: https://gist.github.com/thomd/7667642
3229 export LS_COLORS="ex=1:ln=00;31"
3231 term_bold="$(tput bold)"
3232 term_red="$(tput setaf 1)"
3233 term_green="$(tput setaf 2)"
3234 # shellcheck disable=SC2034 # expected
3235 term_yellow="$(tput setaf 3)"
3236 term_purple="$(tput setaf 5)"
3237 term_nocolor="$(tput sgr0)" # no font attributes
3239 # unused so far. commented for shellcheck
3240 # term_underl="$(tput smul)"
3241 # term_blue="$(tput setaf 4)"
3242 # term_cyan="$(tput setaf 6)"
3244 # Try to keep environment pollution down, EPA loves us.
3245 unset safe_term match_lhs use_color
3250 if [[ $- == *i* ]]; then
3255 if [[ $EUID == 1000 ]]; then
3262 # this needs to come before next ps1 stuff
3263 # this stuff needs bash 4, feb 2009,
3264 # old enough to no longer condition on $BASH_VERSION anymore
3268 if [[ $- == *i* ]] && [[ ! $LC_INSIDE_EMACS ]]; then
3270 bind -m vi-command B:shell-backward-word
3271 bind -m vi-command W:shell-forward-word
3274 if [[ $SSH_CLIENT || $SUDO_USER ]]; then
3275 unset PROMPT_DIRTRIM
3279 # emacs terminal has problems if this runs slowly,
3280 # so I've thrown a bunch of things at the wall to speed it up.
3282 local return=$? # this MUST COME FIRST
3285 # all usable colors:
3287 # green nonzero exit (pri 1)
3290 # red pwd different owner & group & not writable (pri 2)
3291 # red bold pwd different owner & group & writable (pri 2)
3294 local ps_char ps_color
3297 if [[ $HISTFILE ]]; then
3298 history -a # save history
3299 if [[ -e $HOME/.iank-stream-on ]]; then
3300 if [[ $HISTFILE == $HOME/.bh ]]; then
3303 elif [[ $HISTFILE == /a/bin/data/stream_hist ]]; then
3308 ps_color="$term_purple"
3309 ps_char="$ps_char"'\$'
3310 if [[ ! -O . ]]; then # not owner
3311 if [[ -w . ]]; then # writable
3312 ps_color="$term_bold$term_red"
3314 ps_color="$term_red"
3318 if [[ $return != 0 ]]; then
3319 ps_color="$term_green"
3320 ps_char="$return \\$"
3323 # faster than sourceing the file im guessing
3324 if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then
3325 eval "$(< /dev/shm/iank-status)"
3327 if [[ $MAIL_HOST && $MAIL_HOST != "$HOSTNAME" ]]; then
3328 ps_char="@ $ps_char"
3331 if [[ $(jobs -p) ]]; then
3332 jobs_char="$(jobs -p)"'j\j '
3336 # allow a function to specify a command to run after we run the next
3337 # command. Use case: a function makes a persistent notification. If
3338 # we happen to be using that terminal, we can just keep working by
3339 # entering our next command, even a noop in order to dismiss the
3340 # notification, instead of having to explicitly dismiss it.
3341 if [[ ${_psrun[*]} ]]; then
3342 if (( _psrun_count >= 1 )); then
3348 _psrun_count=$(( _psrun_count + 1 ))
3354 # We could test if sudo is active with sudo -nv
3355 # but then we get an email and log of lots of failed sudo commands.
3356 # We could turn those off, but seems better not to.
3357 if [[ $EUID != 0 ]] && [[ $DID_SUDO ]]; then
3358 psudo="\[$term_bold$term_red\]s\[$term_nocolor\] "
3360 if [[ ! $HISTFILE ]]; then
3361 ps_char="NOHIST $ps_char"
3363 PS1="${PS1%"${PS1#*[wW]}"} $jobs_char$psudo\[$ps_color\]$ps_char\[$term_nocolor\] "
3367 # copy of what is automatically added by guix.
3368 # adds [env] to PS1 if GUIX_ENVIRONMENT is set and PS1 contains '$';
3369 if [ -n "$GUIX_ENVIRONMENT" ]; then
3370 if [[ $PS1 =~ (.*)"\\$" ]]; then
3371 PS1="${BASH_REMATCH[1]} [env]\\\$ "
3376 # set titlebar. instead, using more advanced
3378 #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007"
3380 # version 211203 does not have this feature.
3381 if [[ $KONSOLE_VERSION && $KONSOLE_VERSION == [3456789]* || $KONSOLE_VERSION == 2[2456789]* ]]; then
3382 # from konsole, output via ctrl-alt-]
3383 if [[ ! $PS1 =~ 133 ]] ; then
3384 PS1='\[\e]133;L\a\]\[\e]133;D;$?\]\[\e]133;A\a\]'$PS1'\[\e]133;B\a\]' ;
3385 PS2='\[\e]133;A\a\]'$PS2'\[\e]133;B\a\]' ;
3386 PS0='\[\e]133;C\a\]' ; fi
3390 PROMPT_COMMAND=(prompt-command)
3392 if [[ $TERM == screen* ]]; then
3393 _title_escape="\033]..2;"
3395 # somme sites recommend this, i dunno what the diff is.
3396 #_title_escape="\033]30;"
3397 _title_escape="\033]0;"
3400 # make the titlebar be the last command and the current directory.
3401 auto-window-title () {
3404 # These are some checks to help ensure we dont set the title at
3405 # times that the debug trap is running other than the case we
3406 # want. Some of them might not be needed.
3407 if (( ${#FUNCNAME[@]} != 1 || ${#BASH_ARGC[@]} != 2 || BASH_SUBSHELL != 0 )); then
3410 if [[ $1 == prompt-command ]]; then
3413 echo -ne "$_title_escape ${PWD/#$HOME/~} "
3418 # note, this wont work:
3419 # x=$(mktemp); cp a $x
3420 # I havnt figured out why, bigger fish to fry.
3423 # condition from the screen man page i think.
3424 # note: duplicated in tx()
3425 if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
3426 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
3437 rooms=(jupiter saturn)
3438 for ip in 209.51.188.25 live.fsf.org; do
3439 out=$(curl -sS --insecure https://$ip/)
3444 # shellcheck disable=SC2004 # false positive
3445 roomv[$i]=$(( ${roomv[$i]} + n ))
3446 done < <(printf "%s\n" "$out" | grep -Po "$room.*?current[^0-9]*[0-9]*" | grep -o '[0-9]*$' )
3449 printf "total: %s " $v
3452 printf "$room: %s " "${roomv[$i]}"
3458 local default_route_dev
3459 default_route_dev=$(ip r show default | sed 's/.*dev \([^ ]*\).*/\1/' | head -n1)
3460 m s ip n flush dev "$default_route_dev"
3467 # cat or bat with color if we have it
3469 if type -t batcat >/dev/null; then
3470 # note: another useful useful style is "header"
3471 batcat --color always --style plain --theme Coldark-Cold -P "$@"
3477 # Combine files $@ into a single file with comments between them which
3478 # allow splitting them back with fsplit.
3480 # Assumes file names do not have newlines in them.
3483 # generated with cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c8
3484 comment='# jvvuyUsq '
3488 echo "$comment$f" >>$out
3493 local f fin line fin_lines
3496 fin_lines=$(wc -l "$fin" | awk '{print $1}')
3497 comment='# jvvuyUsq '
3498 while (( line <= fin_lines )); do
3499 f=$(sed -n "${line}s/^$comment//p" "$fin")
3500 sed -n "$line,/^$comment/{/^$comment/d;p}" "$fin" >"$f"
3501 line=$(( line + 1 + $(wc -l "$f" | awk '{print $1}') ))
3505 # * stuff that makes sense to be at the end
3511 if [[ -s "$HOME/.rvm/scripts/rvm" ]]; then
3512 # shellcheck disable=SC1091
3513 source "$HOME/.rvm/scripts/rvm"
3516 # I had this idea to start a bash shell which would run an initial
3517 # command passed through this env variable, then continue on
3518 # interactively. But the use case I had in mind went away.
3520 # if [[ $MY_INIT_CMD ]]; then
3521 # "${MY_INIT_CMD[@]}"
3525 # ensure no bad programs appending to this file will have an affect