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 if [[ $KONSOLE_PROFILE_NAME ]]; then
251 if [[ $TERM == alacritty
&& ! -e /usr
/share
/terminfo
/a
/alacritty
]]; then
252 # todo: we should try installing the alacritty terminfo if it is not found
253 # https://github.com/alacritty/alacritty/issues/2838
257 # copying from the alacritty example above,
258 if [[ $TERM == xterm-kitty
]]; then
259 if [[ ! -e /usr
/share
/terminfo
/x
/xterm-kitty
]]; then
262 if [[ -e /a
/opt
/kitty
/shell-integration
/bash
/kitty.bash
]]; then
263 KITTY_SHELL_INTEGRATION
=t
264 source /a
/opt
/kitty
/shell-integration
/bash
/kitty.bash
269 # todo: not sure this works in sakura
271 #bind "\C-w": kill-region
272 # sakura == xterm-256color
274 if [[ $TERM != xterm-kitty
&& $TERM == xterm
* ]]; then
275 # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
276 bind '"\e[1;5C": shell-forward-word' 2>/dev
/null
277 bind '"\e[1;5D": shell-backward-word' 2>/dev
/null
279 # make ctrl-backspace work. for konsole, i fixed it through
280 # /home/iank/.local/share/konsole/default.keytab
282 bind '"\eOc": shell-forward-word'
283 bind '"\eOd": shell-backward-word'
285 # i cant remember why i did this, probably to free up some keys to bind
286 # to other things in bash.
287 # other than C-c and C-z, the rest defined by stty -a are, at least in
288 # gnome-terminal, overridden by bash, or disabled by the system
289 stty lnext undef stop undef start undef
295 # fixup broken backspace in chroots
296 xterm-kitty|alacritty
)
298 TERM
=xterm-256color
command chroot
"$@"
303 export BC_LINE_LENGTH
=0
306 export PROFILE_TASKS_TASK_OUTPUT_LIMIT
=100
308 # note, if I use a machine I dont want files readable by all users, set
309 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
311 # i for insensitive. the rest from
312 # X means dont remove the current screenworth of output upon exit
313 # R means to show colors n things
314 # a useful flag is -F aka --quit-if-one-screen
316 export SYSTEMD_LESS
=$LESS
318 export NNN_COLORS
=2136
320 export SL_FILES_DIR
=/b
/ds
/sl
/.iank
321 export SL_INFO_DIR
=/p
/sshinfo
326 # this is adapted from things printed to term after install
327 # pyenv. commented for now since I'm not actually using pyenv.
329 # export PYENV_ROOT="$HOME/.pyenv"
330 # command -v pyenv &>/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
331 # command -v pyenv &>/dev/null && eval "$(pyenv init -)"
334 # output showed this example for pyenv-virtualenv, which i have no idea
335 # what it is, but leaving it as a comment in case I end up doing python
338 #eval "$(pyenv virtualenv-init -)"
339 ### end begin pyenv ###
345 if [[ -s $bashrc_dir/path-add-function
]]; then
346 source $bashrc_dir/path-add-function
347 if [[ $SSH_CLIENT ]]; then
348 if grep -qF /home
/iank
/.iank
/e
/e
/etc
/exports
&>/dev
/null
; then
349 export EMACSDIR
=/home
/iank
/.iank
/e
/e
355 # if someone exported $SOE (stop on error), catch errors.
357 # Note, on debian this results in the following warning when in ssh,
358 # hich I haven't figured out how to fix. It doesn't happen if we source
359 # after the shell has started
361 # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory
362 # bash: warning: cannot start debugger; debugging mode disabled
364 if [[ -e /a
/bin
/bash-bear-trap
/bash-bear
]]; then
365 source /a
/bin
/bash-bear-trap
/bash-bear
370 path-add
--ifexists /usr
/local
/go
/bin
378 if [[ -s $path ]]; then
379 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
381 elif [[ -s $bashrc_dir/$file ]]; then
382 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
383 source $bashrc_dir/$file
388 mysrc
/a
/bin
/small-misc-bash
/ll-function
389 mysrc
/a
/bin
/distro-functions
/src
/package-manager-abstractions
391 # things to remember:
392 # ALT-C - cd into the selected directory
393 # CTRL-T - Paste the selected file path into the command line
395 # good guide to some of its basic features is the readme file
396 # https://github.com/junegunn/fzf
398 # if [[ -s /usr/share/doc/fzf/examples/key-bindings.bash ]]; then
399 # source /usr/share/doc/fzf/examples/key-bindings.bash
405 # temporary functions
407 m
"${@//spring/fall}"
410 e
"${@//spring/fall}"
414 ### begin FSF section ###
416 # Comments before functions are meant to be good useful
417 # documentation. If they fail at that, please improve them or send Ian a
420 ## copy bash completion
422 # It copies how the bash completion works from one command to other
423 # commands. Generally just use within a .bashrc.
425 # Usage: ORIGINAL_COMMAND TARGET_COMMAND...
431 if ! c
=$
(complete
-p $src 2>/dev
/null
); then
432 _completion_loader
$src &>/dev
/null ||
:
433 c
=$
(complete
-p $src 2>/dev
/null
) ||
return 0
441 ## BEGIN functions to change directory better than cd ##
445 # c: acts like cd, but stores directory history: you could alias to cd if you wanted.
448 # cl: list recent directories and optionally choose one.
450 # Finer details you may want to skip:
452 # bl: print the list of back and forward directories.
454 # We keep 2 stacks of directories, forward and back. Unlike with a web
455 # browser, the forward stack is not erased when going somewhere new.
457 # Recent directories are stored in ~/.cdirs.
459 declare -a _dir_forward _dir_back
461 # normally, the top of _dir_back is our current dir. if it isn't,
462 # put it on there, except we don't want to do that when we
463 # just launched a shell
464 if [[ $OLDPWD ]]; then
465 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
470 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
473 echo "$PWD" >> ~
/.cdirs
480 if (( ${#_dir_back[@]} == 0 )); then
481 echo "nothing left to go back to" >&2
484 top_back
="${_dir_back[-1]}"
486 if [[ $top_back == "$PWD" ]] && (( ${#_dir_back[@]} == 1 )); then
487 echo "already on last back entry" >&2
492 if [[ $top_back == "$PWD" ]]; then
493 # add to dirf if not already there
494 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$top_back" ]]; then
495 _dir_forward
+=("$top_back")
497 unset "_dir_back[-1]"
498 command cd "${_dir_back[-1]}"
500 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$PWD" ]]; then
501 _dir_forward
+=("$PWD")
503 command cd "$top_back"
506 # Interesting feature, not sure I want it.
507 # give us a peek at what is next in the list
508 # if (( ${#_dir_back[@]} >= 2 )); then
509 # printf "%s\n" "${_dir_back[-2]}"
513 # c/b/f Implementation notes:
515 # The top of the back is $PWD
516 # as long as the last directory change was due to c,b,or cl.
518 # Example of stack changes:
544 if (( ${#_dir_forward[@]} == 0 )); then
545 echo "no forward dir left" >&2
548 top_forward
="${_dir_forward[-1]}"
549 unset "_dir_forward[-1]"
552 # give us a peek at what is next in the list
553 # if (( ${#_dir_forward[@]} )); then
554 # printf "%s\n" "${_dir_forward[-1]}"
559 local i line input start
560 local -A buttondirs alines
561 local -a buttons
dirs lines
562 buttons
=( {a..z
} {2.
.9} )
563 if [[ ! -s ~
/.cdirs
]]; then
564 echo nothing
in ~
/.cdirs
570 mapfile
-t lines
<~
/.cdirs
571 start
=$
(( ${#lines[@]} - 1 ))
573 # we have ~33 buttons as of this writing, so lets
574 # prune down the history every once in a while.
575 if (( start
> 500 )); then
576 tac ~
/.cdirs |
awk '!seen[$0]++' |
head -n 200 |
tac | sponge ~
/.cdirs ||
[[ $?
== 141 ]]
579 for (( j
=start
; j
>= 0; j--
)); do
581 if [[ ! $line ||
${alines[$line]} ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
585 buttondirs
[${buttons[i]}]="$line"
586 printf "%s %s\n" ${buttons[i]} "$line"
587 # the LINES bit is for when we have a short terminal, just dont print all
588 # the directories. alternative would be to do something like less the list.
589 if (( i
== ${#buttons[@]} - 1 )) ||
{ [[ $LINES ]] && (( i
== LINES
- 3 )); }; then
595 if (( i
== 0 )); then
596 echo "no dirs in ~/.cdirs"
600 if [[ $input != $
'\n' ]]; then
601 c
"${buttondirs[$input]}"
604 # bl = back list. lists the back and forward directories. i tend to
605 # forget this exists and use cl instead.
609 start
=$
(( ${#_dir_back[@]} - 1 ))
611 # cleanup possible repeating of pwd
612 if (( start
>= 0 )) && [[ ${_dir_back[$start]} == "$PWD" ]]; then
613 start
=$
(( start
- 1 ))
616 if (( start
>= 0 )); then
617 for (( i
=start
; i
>= 0 ; i--
)); do
618 printf "%s %s\n" $j ${_dir_back[i]}
620 if (( j
>= max
)); then
627 start
=$
(( ${#_dir_forward[@]} - 1 ))
629 # cleanup possible repeating of pwd
630 if (( start
>= 0 )) && [[ ${_dir_forward[$start]} == "$PWD" ]]; then
631 start
=$
(( start
- 1 ))
633 if (( start
< 0 )); then
638 for (( i
=start
; i
>= 0 ; i--
)); do
639 printf "%s %s\n" $j ${_dir_forward[i]}
641 if (( j
>= max
)); then
646 # like running cl <enter> a <enter>
649 mapfile
-t lines
<~
/.cdirs
650 start
=$
(( ${#lines[@]} - 1 ))
651 for (( j
=start
; j
>= 0; j--
)); do
653 if [[ ! $line ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
661 ## END functions to change directory better than cd ##
663 # pee do. run args as a command with output copied to syslog.
665 # Usage: pd [-t TAG] COMMAND...
667 # -t TAG Override the tag in the syslog. The default is COMMAND with
668 # any path part is removed, eg. for /bin/cat the tag is cat.
670 # You can view the log via "journalctl -t TAG"
676 -t) tag
="$2"; shift 2 ;;
678 echo "PWD=$PWD command: $*" | logger
-t $tag
679 "$@" |
& pee
cat "logger -t $tag" || ret
=$?
680 echo "exited with status=$ret" | pee
cat "logger -t $tag"
681 # this avoids any err-catch
682 (( ret
== 0 )) ||
return $ret
686 # jdo = journal do. Run command as transient systemd service, tailing
687 # its output in the journal until it completes.
689 # Usage: jdo COMMAND...
691 # Compared to pd: commands recognize this is a non-interactive shell.
692 # The service is unaffected if our ssh connection dies, no need to run
695 # Note: The last few lines of any existing entries for a unit by that
696 # name will be output first, and there will be a few second delay at the
697 # start of the command, and a second or so at the end.
699 # Note: Functions and aliases obviously won't work, we resolve the
702 # Note: requires running as root.
704 local cmd cmd_name jr_pid ret
708 if [[ $EUID != 0 ]]; then
709 echo "jdo: error: rerun as root"
713 if [[ $cmd != /* ]]; then
714 cmd
=$
(type -P "$cmd")
717 journalctl
-qn2 -f -u "$cmd_name" &
718 # Trial and error of time needed to avoid missing initial lines.
719 # .5 was not reliable. 1 was not reliable. 2 was not reliable
722 systemd-run
--unit "$cmd_name" --wait --collect "$cmd" "$@" || ret
=$?
723 # The sleep lets the journal output its last line
724 # before the prompt comes up.
726 kill $jr_pid &>/dev
/null ||
:
729 # this avoids any err-catch
730 (( ret
== 0 )) ||
return $ret
734 # standard date as used in logs
739 # date in log appropriate format
746 command ts
"%F %T" "$@"
749 # ts log. log command to log file.
750 # usage: tsl LOG_PATH_PREFIX COMMAND...
751 # example: tsl /root/command
752 # log file will be like /root/command-2024-02-10.log
754 local log_prefix log_path appending ret
755 if (( $# < 2 )); then
756 echo "tsl: error: expected >= 2 arguments, got $#" >&2
760 if [[ $log_prefix == */* && ! -d ${log_prefix%*/} ]]; then
761 echo "tsl: error: expected directory at ${log_prefix%*/}" >&2
764 log_path
=$log_prefix-$
(date +%Y-
%m-
%d
).log
766 if [[ -s $log_path ]]; then
770 printf "%s\n" "CWD: $PWD, log: $log_path, running $*" | ts
"%F %T" |
tee -a "$log_path"
772 "$@" |
& ts
"%F %T" |
tee -a "$log_path" || ret
=$?
773 printf "%s\n" "exit code $ret from command: $*" | ts
"%F %T" |
tee -a "$log_path"
775 printf "%s\n" "note: this log file contains logs before those of previous command" | ts
"%F %T" |
tee -a "$log_path"
781 mapfile
-t cmds
<<'EOF'
782 tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab
785 ls -la /dev/disk/by-id
788 for cmd
in "${cmds[@]}"; do
808 ....
() { c ..
/..
/..
; }
809 .....
() { c ..
/..
/..
/..
; }
810 ......
() { c ..
/..
/..
/..
/..
; }
815 path
=$
(readlink
-e "$f")
816 echo "cat >$path <<'EOF'"
823 # file cut copy and paste, like the text buffers :)
824 # I havnt tested these.
825 _fbufferinit
() { # internal use
826 ! [[ $my_f_tempdir ]] && my_f_tempdir
="$(mktemp -d)"
827 rm -rf "${my_f_tempdir:?}"/*
831 cp "$@" "$my_f_tempdir"/
835 mv "$@" "$my_f_tempdir"/
837 fpst
() { # file paste
838 [[ $2 ]] && { echo too many arguments
; return 1; }
840 cp "$my_f_tempdir"/* "$target"
844 local host ip port
file key tmp
845 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" ||
: )
846 file=$
(readlink
-f ~
/.ssh
/known_hosts
)
848 echo "khfix: ssh failed"
851 if [[ $port != 22 ]]; then
852 ip_entry
="[$ip]:$port"
853 host_entry
="[$host]:$port"
858 if [[ $host != "$ip" ]]; then
860 ssh-keygen
-F "$host_entry" -f $file >$tmp ||
[[ $?
== 1 ]] # 1 when it doesnt exist in the file
861 if [[ -s $tmp ]]; then
862 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
866 grep -Fv "$key" "$file" | sponge
"$file"
871 ssh-keygen
-F "$ip_entry" -f $file >$tmp ||
[[ $?
== 1 ]]
872 if [[ -s $tmp ]]; then
873 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
877 grep -Fv "$key" "$file" | sponge
"$file"
879 ll ~
/.ssh
/known_hosts
881 khfix-r
() { # known hosts fix + root
882 _khfix-common
"$@" ||
return 1
887 _khfix-common
"$@" ||
return 1
891 # copy path into clipboard
894 x
=$
(readlink
-nf "${1:-$PWD}")
895 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
896 # And, summarizing this:
897 # https://askubuntu.com/questions/705620/xclip-vs-xsel
898 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
902 # clipboard a string (into selection & clipboard buffer)
904 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
905 # And, summarizing this:
906 # https://askubuntu.com/questions/705620/xclip-vs-xsel
907 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
908 printf "%s" "$*" | xclip
-selection clipboard
909 printf "%s" "$*" | xclip
912 # a1 = awk {print $1}
913 for field
in {1.
.20}; do
914 eval a
$field"() { awk '{print \$$field}'; }"
917 for num
in {1.
.9}; do
918 eval h
$num"() { head -n$num || [[ \$? == 141 ]]; }"
923 # shellcheck disable=SC2046 disable=SC2001 disable=SC2183 # hacks, expected
924 printf '%d.%d.%d.%d\n' $
(echo $1 |
sed 's/../0x& /g')
928 local f out outdir
in fname origdir skip1
932 while [[ $1 == -* ]]; do
934 # if we got interrupted after 1st phase
946 # first pass only uses about 1 cpu, so run in parallel
950 if [[ $f == /* ]]; then
955 out
="$origdir/$outdir/$fname"
956 mkdir
-p /tmp
/vp
9/$fname
958 if ! $skip1 && [[ ! -s ffmpeg2pass-0.log
]]; then
959 # -nostdin or else wait causes ffmpeg to go into stopped state. dunno why, random stackoverflow answer.
960 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
962 if [[ -e $out ]]; then rm -f $out; fi
963 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
970 utcl
() { # utc 24 hour time to local hour 24 hour time
971 echo "print( ($1 $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
979 # for running in a fai rescue. iank specific.
981 d
=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
982 for f
in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V
; do
983 cryptsetup luksOpen
--key-file /p
/dev
/$f/root crypt-
$f-root
984 cryptsetup luksOpen
--key-file /p
/dev
/$f/o crypt-
$f-o
986 mount
-o subvol
=root_trisquelaramo
/dev
/mapper
/crypt-
$d-root /mnt
987 mount
-o subvol
=a
/dev
/mapper
/crypt-
$d-root /mnt
/a
988 mount
-o subvol
=o
/dev
/mapper
/crypt-
$d-o /mnt
/o
989 mount
-o subvol
=boot_trisquelaramo
/dev
/sda2
/mnt
/boot
997 c4
() { c
/var
/log
/exim4
; }
999 caa
() { git commit
--amend --no-edit -a; }
1012 find -L "$@" -type f
-not \
( -name .svn
-prune -o -name .git
-prune \
1013 -o -name .hg
-prune -o -name .editor-backups
-prune \
1014 -o -name .undo-tree-history
-prune \
) -printf '%h\0%d\0%p\n' |
sort -t '\0' -n \
1015 |
awk -F '\0' '{print $3}' 2>/dev
/null |
while read -r file; do
1017 printf "%s\n" "$file"
1024 calc
() { echo "scale=3; $*" |
bc -l; }
1025 # no having to type quotes, but also no command history:
1029 echo "scale=3; $x" |
bc -l
1040 ccat
() { # config cat. see a config without extra lines.
1041 sed -r '/^[[:space:]]*([;#]|--|\/\/|$)/d' "$@"
1047 # dev/pts needed for pacman signature check
1048 for d
in dev proc sys dev
/pts
; do
1050 if ! mountpoint
$d &>/dev
/null
; then
1051 m s mount
-o bind /$d $d
1057 # dev/pts needed for pacman signature check
1058 for d
in dev
/pts dev proc sys
; do
1060 if mountpoint
$d &>/dev
/null
; then
1068 # join options which are continued to multiples lines onto one line
1070 while IFS
= read -r line
; do
1071 # remove leading spaces/tabs. assumes extglob
1072 if [[ $line == "[ ]*" ]]; then
1073 line
="${line##+( )}"
1078 elif [[ $line == *=* ]]; then
1079 echo "$pastline" >> "$2"
1082 pastline
="$pastline $line"
1084 done < <(grep -vE '^([ \t]*#|^[ \t]*$)' "$1")
1085 echo "$pastline" >> "$2"
1089 # diff config files,
1090 # setup for format of postfix, eg:
1093 local pastline unified f1 f2
1097 _cdiff-prep
"$1" "$f1"
1098 _cdiff-prep
"$2" "$f2"
1099 cat "$f1" "$f2" |
grep -Po '^[^=]+=' |
sort |
uniq > "$unified"
1100 while IFS
= read -r line
; do
1101 # the default bright red / blue doesnt work in emacs shell
1102 dwdiff
-cblue,red
-A best
-d " ," <(grep "^$line" "$f1" ||
echo ) <(grep "^$line" "$f2" ||
echo ) | colordiff
1108 local start
=$SECONDS
1110 # shellcheck disable=SC2030
1111 inotifywait
-m "$dir" -e create
-e moved_to | \
1112 while read -r filedir _
file; do
1115 calc $
((SECONDS
- start
)) / 60
1122 s chown
-R $USER:$USER "$@"
1125 # shellcheck disable=SC2032
1127 # makes it so chown -R symlink affects the symlink and its target.
1128 if [[ $1 == -R ]]; then
1130 command chown
-h "$@"
1131 command chown
-R "$@"
1142 d
() { builtin bg "$@"; }
1145 # f would be more natural, but i already am using it for something
1146 z
() { builtin fg "$@"; }
1149 x
() { builtin kill %%; }
1152 diff --strip-trailing-cr -w "$@" # diff content
1160 safe_rename
"$x" "$y"
1165 # usage: dfp MOUNTPOINT [SECOND_INTERVAL]
1166 # SECOND_INTERVAL defaults to 90
1169 local a b mp interval
1172 if [[ ! $mp ]]; then
1173 echo "dfp: error, missing 1st arg" >&2
1177 a
=$
(df
--output=used
$mp |
tail -n1)
1179 b
=$
(df
--output=used
$mp |
tail -n1)
1180 printf "used mib: %'d mib/min: %s\n" $
(( b
/1000 )) $
(( (b-a
) / (interval
* 1000 / 60 ) ))
1184 # get ipv4 ip from HOST. or if it is already a number, return that
1192 getent ahostsv4
"$host" |
awk '{ print $1 }' |
head -n1
1198 command dig +nostats
+nocmd
"$@"
1200 # Output with sections sorted, and removal of query id, so 2 dig outputs can be diffed.
1204 dig +nordflag
"$@" |
sed -r 's/^(;; ->>HEADER<<-.*), id: .*/\1/' |
while read -r l
; do
1205 if [[ $l == [^\
;]* ]]; then
1209 printf "%s" "$sec" |
sort
1217 # compare digs to the 2 servers
1218 # usage: digdiff @server1 @server2 DIG_ARGS
1219 # note: only the soa master nameserver will respond with
1220 # ra "recursive answer" flag. That difference is meaningless afaik.
1227 digsort
$s1 "$@" |
tee /tmp
/digdiff
1228 diff -u /tmp
/digdiff
<(digsort
$s2 "$@")
1231 # date in a format i like reading
1233 date "+%A, %B %d, %r" "$@"
1238 # date with all digits in a format i like
1242 ccomp
date dt dtr dtd
1244 dus
() { # du, sorted, default arg of
1245 du
-sh ${@:-*} |
sort -h
1250 e
() { printf "%s\n" "$*"; }
1258 printf "%qEOL\n" "${arg}"
1259 printf "%s" "${arg}" |
& hexdump -C
1263 # echo variables. print var including escapes, etc, like xxd for variable
1269 if [[ -v $arg ]]; then
1270 printf "%qEOL\n" "${!arg}"
1271 printf "%s" "${!arg}" |
& hexdump -C
1273 echo arg
$arg is
unset
1279 [[ ${#@} == 2 ]] ||
{ echo "error: ediff requires 2 arguments"; return 1; }
1280 emacs
--eval "(ediff-files \"$1\" \"$2\")"
1284 # shellcheck disable=SC2120 # we expect to pass arguments in use outside this file
1287 tail -F /var
/log
/exim
4/mainlog
/var
/log
/exim
4/*main
/var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
-n 200 "$@"
1291 tail -F /var
/log
/exim
4/mainlog
-n 200 "$@"
1294 tail -F /var
/log
/exim
4/mymain
-n 200 "$@"
1296 ccomp
tail etail etail2
1298 # ran into this online, trying it out
1300 ( "$@" &>/dev
/null
& disown )
1304 ssh "$@" cat .ssh
/authorized_keys
{,2}
1308 # print exim old pids
1310 local configtime pid piduptime now daemonpid
1311 printf -v now
'%(%s)T' -1
1312 configtime
=$
(stat
-c%Y
/var
/lib
/exim
4/config.autogenerated
)
1313 if [[ -s /run
/exim
4/exim.pid
]]; then
1314 daemonpid
=$
(cat /run
/exim
4/exim.pid
)
1316 for pid
in $
(pgrep
-f '^/usr/sbin/exim4( |$)'); do
1317 # the daemonpid gets reexeced on HUP (service reloads), keeping its same old timestamp
1318 if [[ $pid == "$daemonpid" ]]; then
1321 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
1322 if (( configtime
> now
- piduptime
)); then
1328 # exim tail but only watch lines from new pids
1331 for pid
in $
(eoldpids
); do
1334 if [[ $oldpids ]]; then
1335 etail |
awk '$3 !~ /^\[('"${oldpids%|}"')\]$/'
1340 # exim watch as old pids go away
1342 local configtime pid piduptime now tmpstr
1348 mapfile
-t oldpids
<<<"$tmpstr"
1349 if (( ! ${#oldpids[@]} )); then
1352 # print the date every 20 iterations
1353 if (( ! count
% 20 )); then
1357 ps
-f -p "${oldpids[*]}"
1363 less /var
/log
/exim
4/mainlog
1367 exiqgrep
-ir.\
* -o 60 |
while read -r i
; do
1370 hlm exigrep
$i /var
/log
/exim
4/mainlog |
cat ||
:
1374 # other ways to get the list of message ids:
1375 # exim -bp | awk 'NF == 4 {print $3}'
1376 # # this is slower 160ms, vs 60.
1378 exiqgrep
-ir.\
* |
xargs exim
-Mrm
1383 mkdir
-p /tmp
/edev
/etc
1384 cp -ra /etc
/exim4
/tmp
/edev
/etc
1385 cp -ra /etc
/alias* /tmp
/edev
/etc
1386 find /tmp
/edev
/etc
/exim4
-type f
-execdir sed -i "s,/etc/,/tmp/edev/etc/,g" '{}' +
1390 update-exim4.conf
-d /tmp
/edev
/etc
/exim4
-o /tmp
/edev
/e.conf
1394 # show important information about incoming mail in the exim log
1396 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' <${1:-/var/log/exim4/mainlog}
1399 # 2nd line is message-id:
1401 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog}
1404 tail -F /var
/log
/exim
4/mainlog |
sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p'
1411 # find array. make an array of file names found by find into $x
1412 # argument: find arguments
1413 # return: find results in an array $x
1414 while read -rd ''; do
1416 done < <(find "$@" -print0);
1419 faf
() { # find all files. use -L to follow symlinks
1420 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1421 -o -name .hg
-prune -o -name .editor-backups
-prune \
1422 -o -name .undo-tree-history
-prune \
) -type f
2>/dev
/null
1425 # usage ffconcat FILES_TO_CONCAT OUTPUT_FILE
1429 printf "file '%s'\n" "$1" >$tmpf
1430 while (( $# > 1 )); do
1432 printf "file '%s'\n" "$1" >>$tmpf
1434 # https://trac.ffmpeg.org/wiki/Concatenate
1435 ffmpeg
-f concat
-safe 0 -i $tmpf -c copy
"$1"
1440 if (( $# == 0 )); then
1441 echo ffremux error expected args
>&2
1446 tmpf
=$tmpd/"${f##*/}"
1447 ffmpeg
-i "$f" -c:v copy
-c:a copy
$tmpf
1455 # absolute path of file/dir without resolving symlinks.
1457 # Most of the time, I want this where I would normally use readlink.
1458 # This is what realpath -s does in most cases, but sometimes it
1459 # actually resolves symlinks, at least when they are in /.
1461 # Note, if run on a dir, if the final component is relative, it won't
1462 # resolve that. Use the below fpd for that.
1464 # note: we could make a variation of this which
1465 # assigns to a variable name using eval, so that we don't have to do
1466 # x=$(fp somepath), which might save subshell overhead and look nice,
1467 # but I'm not going to bother.
1469 local initial_oldpwd initial_pwd dir base
1470 initial_oldpwd
="$OLDPWD"
1472 if [[ $1 == */* ]]; then
1475 # CDPATH because having it set will cause cd to possibly print output
1477 printf "%s%s\n" "$PWD" "$base"
1478 CDPATH
='' cd "$initial_pwd"
1479 OLDPWD
="$initial_oldpwd"
1481 printf "%s/%s\n" "$PWD" "$1"
1484 # full path of directory without resolving symlinks
1486 local initial_oldpwd initial_pwd dir
1487 initial_oldpwd
="$OLDPWD"
1491 printf "%s%s\n" "$PWD" "$base"
1493 OLDPWD
="$initial_oldpwd"
1500 sudo mailq |gr frozen|
awk '{print $3}' |
while read -r id
; do
1506 echo -e '\n\n##############################\n'
1507 done |
tee -a /tmp
/frozen
1511 while read -r line
; do
1512 printf '%s\n' "$line"
1513 ids
+=("$(printf '%s\n' "$line" |gr frozen|awk '{print $3}')")
1515 echo "sleeping for 2 in case you change your mind"
1517 sudo exim
-Mrm "${ids[@]}"
1521 # like -e for functions. returns on error.
1522 # at the end of the function, disable with:
1524 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
1525 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
1531 local help="Usage: getdir [--help] PATH
1532 Output the directory of PATH, or just PATH if it is a directory."
1533 if [[ $1 == --help ]]; then
1537 if [[ $# -ne 1 ]]; then
1538 echo "getdir error: expected 1 argument, got $#"
1541 if [[ -d $1 ]]; then
1545 dir
="$(dirname "$1")"
1546 if [[ -d $dir ]]; then
1549 echo "getdir error: directory does not exist"
1555 git_empty_branch
() { # start an empty git branch. carefull, it deletes untracked files.
1556 [[ $# == 1 ]] ||
{ echo 'need a branch name!'; return 1;}
1558 root
=$
(gitroot
) ||
return 1 # function to set gitroot
1560 git symbolic-ref HEAD refs
/heads
/$1
1565 # shellcheck disable=SC2120
1567 local help="Usage: gitroot [--help]
1568 Print the full path to the root of the current git repo
1570 Handles being within a .git directory, unlike git rev-parse --show-toplevel,
1571 and works in older versions of git which did not have that."
1572 if [[ $1 == --help ]]; then
1577 p
=$
(git rev-parse
--git-dir) ||
{ echo "error: not in a git repo" ; return 1; }
1578 [[ $p != /* ]] && p
=$PWD
1584 local args gdb
=false
1586 if [[ $EMACSDIR ]]; then
1587 path-add
"$EMACSDIR/lib-src" "$EMACSDIR/src"
1590 if [[ $DISPLAY ]]; then
1594 if (( $# == 0 )); then
1597 # duplicate -c, but oh well
1598 if ! pgrep
-u $EUID emacsclient
; then
1599 if (( $# == 0 )) && type -p gdb
&>/dev
/null
; then
1605 if [[ $EMACSDIR ]]; then
1607 # todo: we don't have to alter HOME since emacs 29+, we can set
1608 # user-emacs-directory with the flag --init-directory
1610 # Alter the path here, otherwise the nfs mount gets triggered on the
1611 # first path lookup when emacs is not being used.
1612 # shellcheck disable=SC2098 disable=SC2097 # false positive
1613 PATH
="$EMACSDIR/lib-src:$EMACSDIR/src:$PATH" EHOME
=$HOME HOME
=$EMACSDIR m emacsclient
-a "" $args "$@"
1616 # due to a bug, we cant debug from the start unless we get a new gdb
1617 # https://sourceware.org/bugzilla/show_bug.cgi?id=24454
1618 # m gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacs --daemon
1619 m emacsclient
-a "" $args "$@"
1621 cd "/a/opt/emacs-$(distro-name)$(distro-num)"
1622 s gdb
-p "$(pgrep -f 'emacs --daemon')" -ex c
1625 m emacsclient
-a "" $args "$@"
1630 # g pipe. like: cmd | emacs. save cmd output to tmp file, then edit.
1636 #like cmd &> tempfile; emacs tempfile
1638 # note: a useful workflow for doing mass replace on my files:
1640 ## remove any false positives, or manually edit them. rename files if needed.
1641 # sedi 's/REGEX/REPLACEMENT/' $(gr '^/' /a/tmp/gtmp)
1646 # g command substitution.
1648 # shellcheck disable=SC2046 # i want word splitting for this hackery
1652 # force terminal version
1658 # quit will prompt if the program crashes.
1659 gdb
-ex=r
-ex=quit
--args emacs
"$@"; r
;
1663 # kill the emacs daemon
1668 grep -iIP --color=auto
"$@" ||
return $?
1670 grr
() { # grep recursive
1671 # Don't return 1 on nonmatch because this is meant to be
1672 # interactive, not in a conditional.
1673 if [[ ${#@} == 1 ]]; then
1674 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto
"$@" . ||
[[ $?
== 1 ]]
1676 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto
"$@" ||
[[ $?
== 1 ]]
1684 # recursive everything. search for files/dirs and lines. rs = easy chars to press
1688 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1689 -o -name .hg
-prune -o -name .editor-backups
-prune \
1690 -o -name .undo-tree-history
-prune \
) 2>/dev
/null |
grep -iP --color=auto
"$query"
1694 # horizontal row. used to break up output
1697 # 180 is long enough.
1698 blocks
=██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
1699 printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)${blocks:0:${COLUMNS:-180}}$(tput sgr0 2>/dev/null||:)"
1703 local col input_len
=0
1705 input_len
=$
((input_len
+ 1 + ${#arg}))
1707 col=$
((60 - input_len
))
1708 printf "\e[1;97;41m%s" "$*"
1709 if (( col > 0 )); then
1710 # shellcheck disable=SC2046 # needed to work as intended. a better way would be like hr above.
1711 printf "\e[1;97;41m \e[0m%.0s" $
(eval echo "{1..${col}}")
1715 hlm
() { hl
"$*"; "$@"; }
1717 hrcat
() { local f
; for f
; do [[ -f $f ]] ||
continue; hr
; echo "$f"; cat "$f"; done }
1720 # github-release-dl restic/restic restic_ _linux_amd64.bz2
1722 # https://github.com/restic/restic/releases/download/v0.16.3/restic_0.16.3_linux_amd64.bz2
1723 github-release-dl
() {
1724 local github_path file_prefix file_suffix latest_prefix version redir_path
1728 if (( $# != 3 )); then
1729 echo "$0: error, expected 3 arguments" >&2
1732 redir_path
="https://github.com/$github_path/releases/latest/download/"
1733 latest_prefix
=$
(curl
-s -I "$redir_path" |
awk 'tolower($1) == "location:" {print $2}')
1734 # it has a trailing /r at the end. just kill any whitespace.
1735 latest_prefix
="${latest_prefix//[$'\t\r\n ']}"
1736 if [[ ! $latest_prefix ]]; then
1737 echo "failed to find latest path. Tried to find case insensitive 'location:' in the curl output:"
1738 m curl
-s -I "$redir_path"
1741 version
="${latest_prefix##*/}"
1742 version
="${version#v}"
1743 m wget
-- "$latest_prefix/$file_prefix$version$file_suffix"
1747 # go-github-install restic/restic restic_ _linux_amd64.bz2
1748 # go-github-install restic/rest-server rest-server_ _linux_amd64.tar.gz
1750 # common pattern among go binaries on github
1751 go-github-install
() {
1752 local tmpd targetf tmp files src
1757 tmp
="${file_prefix##*[[:alnum:]]}"
1758 targetf
="${file_prefix%"$tmp"}"
1759 echo targetf
: $targetf
1760 github-release-dl
"$@"
1762 case $file_suffix in
1770 rm -f -- "${files[@]}"
1772 # Here we detect and handle 2 cases: either we extracted a single
1773 # binary which we have to rename or a folder with a binary named
1774 # $targetf in it which is all we care about.
1775 if (( ${#files[@]} == 1 )) && [[ -f ${files[0]} ]]; then
1777 mv -- .
/* /usr
/local
/bin
/$targetf
1779 files
=(.
/*/$targetf)
1780 if [[ -f $targetf ]]; then
1782 elif [[ -f ${files[0]} ]]; then
1786 mv -- "$src" /usr
/local
/bin
1792 ## 2024: I'm using gh instead of hub, but leaving this just in case.
1793 ## I tried the github cli tool (gh) and it seems easier than
1796 ## hub predated github's 2020 official cli tool gh.
1798 ## https://raw.githubusercontent.com/cli/cli/trunk/docs/gh-vs-hub.md
1799 # get latest hub and run it
1800 # main command to use:
1801 # hub pull-request --no-edit
1802 # --no-edit means to use the first commit\'s message as the pull request message.
1803 # If that fails, try doing
1804 # hub pull-request --no-edit -b UPSTREAM_OWNER:branch
1805 # where branch is usually master. it does the pr against your current branch.
1807 # On first use, you input username/pass and it gets an oath token so you dont have to repeat
1808 # it\'s at ~/.config/hub
1810 local up uptar updir p re
1811 # example https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz
1812 up
=$
(wget
-q -O- https
://api.github.com
/repos
/github
/hub
/releases
/latest | jq
-r .assets
[].browser_download_url |
grep linux-amd64
)
1814 if [[ ! $up ||
$up =~
$re ]]; then
1815 echo "failed to get good update url. got: $up"
1819 if [[ ! -e /a
/opt
/$updir ]]; then
1820 rm -rf /a
/opt
/hub-linux-amd64
*
1822 tar -C /a
/opt
-zxf /a
/opt
/$uptar
1825 if ! which hub
&>/dev
/null
; then
1826 sudo
/a
/opt
/$updir/install
1829 # save token across computers
1830 if [[ ! -L ~
/.config
/hub
]]; then
1831 if [[ -e ~
/.config
/hub
]]; then
1832 mv ~
/.config
/hub
/p
/c
/subdir_files
/.config
/
1834 if [[ -e /p
/c
/subdir_files
/.config
/hub
]]; then
1848 # cvs update -C FILE
1853 # potentially useful command translation
1854 # https://fling.seas.upenn.edu/~giesen/dynamic/wordpress/equivalent-commands-for-git-svn-and-cvs/
1856 # importing cvs repo into git using git-cvs package:
1857 # /f/www $ /usr/lib/git-core/git-cvsimport -C /f/www-git
1873 find -L "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1874 -o -name .hg
-prune -o -name .editor-backups
-prune \
1875 -o -name .undo-tree-history
-prune \
) -iname "*$glob*" 2>/dev
/null
1879 # insensitive find here. args are combined into the search string.
1880 # -L = follow symlinks
1881 find -L .
-not \
( -name .svn
-prune -o -name .git
-prune \
1882 -o -name .hg
-prune -o -name .editor-backups
-prune \
1883 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1887 # insensitive find directory
1888 find -L .
-type d
-not \
( -name .svn
-prune -o -name .git
-prune \
1889 -o -name .hg
-prune -o -name .editor-backups
-prune \
1890 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1895 sudo iptables
-A INPUT
-s $1 -j DROP
1900 grep -Il "" "$@" &>/dev
/null
1907 # journalctl with times in the format the --since= and --until= options accept
1908 jrt
() { journalctl
-e -n100000 -o short-full
"$@"; }
1909 jr
() { journalctl
-e -n100000 "$@" ; }
1910 jrf
() { journalctl
-n1000 -f "$@" ; }
1912 # the invocation id is "assigned each time the unit changes from an inactive
1913 # state into an activating or active state" man systemd.exec
1914 journalctl
-e --no-tail -u exim4 _SYSTEMD_INVOCATION_ID
="$(systemctl show -p InvocationID --value $1)"
1916 ccomp journalctl jr jrf jru
1921 if [[ $PWD == /[iap
] ]]; then
1922 command ls -A --color=auto
-I lost
+found
"$@"
1924 command ls -A --color=auto
"$@"
1928 lcn
() { locate -i "*$**"; }
1930 lg
() { LC_COLLATE
=C.UTF-8 ll
--group-directories-first "$@"; }
1932 lt
() { ll
-tr "$@"; }
1934 lld
() { ll
-d "$@"; }
1936 ccomp
ls l lg lt lld ll
1942 for dirs in false true
; do
1944 if [[ -d $f ]]; then
1946 # reverse the order to rename the nested dirs first.
1947 # note: 0 element is the dir itself
1948 for ((i
=${#all[@]}-1; i
>=1; i--
)); do
1950 if $dirs && [[ -d $a ]]; then
1951 # e dirs low "$a" # debug
1953 elif ! $dirs && [[ ! -d $a && -e $a ]]; then
1955 # e not dirs low "$a" # debug
1960 # just rename all the top level args on the second pass
1962 # e final dirs low "$f" # debug
1969 low
() { # make filenames lowercase, remove bad chars
1972 arg
="${arg%%+(/)}" # remove trailing slashes. assumes we have extglob on.
1974 if (( ${#dir} == ${#arg} )); then
1978 new
="${f,,}" # downcase
1979 # shellcheck disable=SC2031 # seems like a shellcheck bug
1980 new
="${new//[^a-zA-Z0-9._-]/_}" # sub bad chars
1981 new
="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
1982 new
="${new%"${new##*[[:alnum:]]}"}"
1983 # remove bad underscores, like __ and _._
1984 new
=$
(echo $new |
sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
1985 safe_rename
"$dir/$f" "$dir/$new" ||
return 1
1990 lower
() { # make first letter of filenames lowercase.
1993 if [[ ${x::1} == [A-Z
] ]]; then
1994 y
=$
(tr '[:upper:]' '[:lower:]' <<<"${x::1}")"${x:1}"
1995 safe_rename
"$x" "$y" ||
return 1
2001 k
() { # history search
2002 grep -iP --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
tail -n 80 ||
[[ $?
== 1 ]];
2004 ks
() { # history search with context
2005 # args are an extended regex used by sed
2006 history |
sed -nr "h;s/^\s*(\S+\s+){4}//;/$*/{g;p}" |
tail -n 80 ||
[[ $?
== 1 ]];
2008 ksu
() { # history search unique
2009 grep -P --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
uniq ||
[[ $?
== 1 ]];
2012 # todo: id like to do maybe a daily or hourly cronjob to
2013 # check that my history file size is increasing. Ive had it
2014 # inexplicably truncated in the past.
2017 HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/'
2018 read -r -p "press anything but contrl-c to delete"
2019 for entry
in $
(HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/ { print a }' |
tac); do
2025 # history without the date
2027 history "$@" | cut
-d' ' -f 7-
2030 ccomp
grep k ks ksu histrm
2034 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
2035 make -qp |
awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
2047 # mkdir the last arg, cp the rest into it
2050 cp "${@:1:$#-1}" "${@: -1}"
2054 mv "${@:1:$#-1}" "${@: -1}"
2057 mkt
() { # mkdir and touch file
2059 mkdir
-p "$(dirname "$path")"
2063 # shellcheck disable=SC2032
2064 mkdir
() { command mkdir
-p "$@"; }
2067 # https://github.com/HenriWahl/Nagstamon/issues/357
2068 if ! pgrep
-f /usr
/bin
/dunst
>/dev
/null
; then
2071 /usr
/bin
/nagstamon
&
2076 screen
-RD -S profanity
2079 # i dont want to wait for konsole to exit...
2081 command prof
&>/dev
/null
&
2086 printf '\033[1A\033[K'; printf "%s\n" "$l"| ts
"%F %T" |
tee -a /p
/self-chat.log
2091 # cant use s because sudo -i doesnt work for passwordless sudo command
2094 sudo nmtui-connect
"$@"
2104 if shopt nullglob
>/dev
/null
; then
2118 # shellcheck disable=SC2024
2120 for f
in /var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
; do
2122 if [[ -s $f ]]; then
2123 echo ================== $f =============
2124 s
tee -a /var
/log
/exim
4/$base-archive <$f
2132 ping() { command ping -O "$@"; }
2133 p8
() { ping "$@" 8.8.8.8; }
2134 p6
() { ping6
"$@" 2001:4860:4860::8888; }
2136 pkx
() { # package extract
2137 local pkg cached tmp f
2140 # shellcheck disable=SC2012
2141 cached
=$
(ls -t /var
/cache
/apt
/archives
/${pkg}_
* |
tail -n1 2>/dev
/null
) ||
:
2142 if [[ $cached ]]; then
2145 m aptitude download
$pkg ||
return 1
2147 tmp
=(*); f
=${tmp[0]} # only 1 expected
2156 tmpf
=$
(pgrep
-f "$*")
2157 mapfile
-t pids
<<<"$tmpf"
2160 # shellcheck disable=SC2128
2166 0) echo "no pid found" ;;
2175 help="Usage: psg [--help] GREP_ARGS
2176 grep ps and output in a nice format"
2177 if [[ $1 == --help ]]; then
2182 # final grep is because some commands tend to have a lot of trailing spaces
2183 y
=$
(echo "$x" |
grep -iP "$@" |
grep -o '.*[^ ]') ||
:
2185 echo "$x" |
head -n 1 ||
[[ $?
== 141 ]]
2190 pubip
() { curl
-4s https
://icanhazip.com
; }
2191 pubip6
() { curl
-6s https
://icanhazip.com
; }
2192 whatismyip
() { pubip
; }
2195 q
() { # start / launch a program in the backround and redir output to null
2199 # shellcheck disable=SC2120
2201 if [[ $HISTFILE ]]; then
2202 history -a # save history
2204 trap ERR
# this avoids a segfault
2206 # i had this redir, not sure why
2207 # exit "$@" 2>/dev/null
2210 # scp is insecure and deprecated.
2212 rsync
-Pt --inplace "$@"
2217 # available high ports are 1024-65535,
2218 # but lets skip things that are more likely to be in use
2221 print(secrets.SystemRandom().randrange(10002,65500))
2227 # shellcheck disable=SC1090 # expected to not follow
2237 # rsync, root is required to keep permissions right.
2238 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
2239 # --no-times --delete
2240 # basically, make an exact copy, use checksums instead of file times to be more accurate
2241 rsync
-ahvic --delete "$@"
2244 # like rlu, but dont delete files on the target end which
2245 # do not exist on the original end.
2249 # rl without preserving modification time.
2250 rsync
-ahvic --delete --no-t "$@"
2252 # [RSYNC_OPTS] HOST PATH
2254 # eg. rsu -opts frodo /testpath
2255 # relative paths will expanded with readlink -f.
2256 opts
=("${@:1:$#-2}") # 1 to last -2
2257 path
="${*:$#}" # last
2258 host="${*:$#-1:1}" # last -1
2259 if [[ $path == .
* ]]; then
2260 path
=$
(readlink
-f $path)
2262 m rsync
-ahvi --relative --no-implied-dirs "${opts[@]}" "$path" "root@$host:/";
2264 ccomp rsync rsd rsa rst rsu
2266 # find programs listening on a port
2269 # to figure out these args, i had to look at the man page from git version, as of 2022-04.
2270 s ss
-lpn state listening sport
= $port
2275 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2280 hr
; s ss
-lpn sport
= 53
2281 if systemctl is-enabled dnsmasq
&>/dev
/null ||
[[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2282 # this will fail is dnsmasq is failed
2283 hr
; m ser status dnsmasq |
cat ||
:
2285 hr
; echo $f:; ccat
$f
2286 hr
; m grr
'^ *(servers-file|server) *=|^ *no-resolv *$' /etc
/dnsmasq.conf
/etc
/dnsmasq.d
2287 f
=/etc
/dnsmasq-servers.conf
2288 hr
; echo $f:; ccat
$f
2291 echo /etc
/nsswitch.conf
:
2292 grep '^ *hosts:' /etc
/nsswitch.conf
2293 if systemctl is-enabled systemd-resolved
&>/dev
/null ||
[[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2294 hr
; m ser status systemd-resolved |
cat ||
:
2295 hr
; m resolvectl status |
cat
2303 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2307 m sudo nscd
-i hosts
2309 if [[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2310 m sudo systemctl restart dnsmasq
2312 if [[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2313 m sudo systemctl restart systemd-resolved
2315 if type -P resolvectl
&>/dev
/null
; then
2316 resolvectl flush-caches
2320 # add annoyingly long argument which should be the default
2322 sed -i --follow-symlinks "$@"
2327 # todo: test variable assignment with newlines here.
2328 # https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
2330 # beware that it only works on the assumption that any special
2331 # characters in the input string are intended to be escaped, not to work
2332 # as special chacters.
2334 LC_ALL
=C
sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
2338 ssh fencepost
head -n 300 /gd
/gnuorg
/EventAndTravelInfo
/rms-current-trips.txt |
less
2347 command sudo
"$@" ||
return $?
2352 # I use a function because otherwise we cant use in a script,
2353 # cant assign to variable.
2355 # note: gksudo is recommended for X apps because it does not set the
2356 # home directory to the same, and thus apps writing to ~ fuck things up
2357 # with root owned files.
2359 if [[ $EUID != 0 ||
$1 == -* ]]; then
2360 # shellcheck disable=SC2034
2361 SUDOD
="$PWD" command sudo
-i "$@"
2367 sb
() { # sudo bash -c
2368 # use sb instead of s is for sudo redirections,
2369 # eg. sb 'echo "ok fine" > /etc/file'
2370 # shellcheck disable=SC2034
2372 sudo
-i bash
-c "$@"
2375 se
() { s urun
0077 "$@"; }
2378 safe_rename
() { # warn and dont rename if file exists.
2379 # mv -n exists, but it\'s silent
2380 if [[ $# != 2 ]]; then
2381 echo safe_rename error
: $# args
, need
2 >&2
2384 if [[ $1 != "$2" ]]; then # yes, we want to silently ignore this
2385 if [[ -e $2 ||
-L $2 ]]; then
2386 echo "Cannot rename $1 to $2 as it already exists."
2395 sudo
dd status
=none of
="$1"
2399 if type -p systemctl
&>/dev
/null
; then
2402 if (( $# >= 3 )); then
2403 echo iank
: ser expected
2 or
less arguments
2410 systemctl
-n 40 status
"$@"
2413 seru
() { systemctl
--user "$@"; }
2414 # like restart, but do nothing if its not already started
2417 if [[ $
(s systemctl
--no-pager show
-p ActiveState
$service ) == ActiveState
=active
]]; then
2418 systemctl restart
$service
2422 setini
() { # set a value in a .ini style file
2423 key
="$1" value
="$2" section
="$3" file="$4"
2424 if [[ -s $file ]]; then
2425 sed -ri -f - "$file" <<EOF
2426 # remove existing keys
2427 / *\[$section\]/,/^ *\[[^]]+\]/{/^\s*${key}[[:space:]=]/d}
2429 /^\s*\[$section\]/a $key=$value
2430 # from section to eof, do nothing
2431 /^\s*\[$section\]/,\$b
2432 # on the last line, if we haven't found section yet, add section and key
2444 sgo
() { # service go
2446 ser restart
$service ||
return 1
2447 if type -p systemctl
&>/dev
/null
; then
2453 # ignore services that dont exist
2454 if systemctl
cat $service &>/dev
/null
; then
2456 ser disable
$service
2462 systemctl list-unit-files | rg
"$@"
2467 # see https://savannah.gnu.org/maintenance/fsf/bash-style-guide/ for justifications
2468 local quotes others ret
2469 quotes
=2048,2068,2086,2206,2254
2470 others
=2029,2032,2033,2054,2164,
2471 shellcheck
-W 999 -x -e $quotes,$others "$@" || ret
=$?
2472 if (( ret
>= 1 )); then
2473 echo "A template comment to disable is now in clipboard. eg: # shellcheck disable=SC2206 # reason"
2474 cbs
"# shellcheck disable=SC"
2478 # sk with quotes. For checking scripts that we expect to take untrusted
2479 # input in order to verify we quoted vars.
2482 others
=2029,2033,2054,2164
2483 shellcheck
-W 999 -x -e $others "$@" ||
return $?
2488 for f
in $
(i s |
awk '$1 == "modified:" {print $2}'); do
2489 if istext
"$f" && [[ $
(head -n1 "$f" 2>/dev
/null
) == '#!/bin/bash'* ]]; then
2495 # sl: ssh, but firsh rsync our bashrc and related files to a special
2496 # directory on the remote host if needed.
2498 # Some environment variables and files need to be setup for this to work
2499 # (mine are set at the beginning of this file)
2501 # SL_FILES_DIR: Environment variable. Path to folder which should at
2502 # least have a .bashrc file or symlink. This dir will be rsynced to ~ on
2503 # remote hosts (top level symlinks are resolved) unless the host already
2504 # has a $SL_FILES_DIR/.bashrc. In that case, we assume it is a host you
2505 # control and sync files to separately and already has the ~/.bashrc you
2506 # want. The remote bash will also take its .inputrc config from this
2507 # folder (default of not existing is fine). Mine looks like this:
2508 # https://iankelling.org/git/?p=distro-setup;a=tree;f=sl/.iank
2510 # SL_INFO_DIR: Environment variable. This folder stores info about what
2511 # we detected on the remote system and when we last synced. It will be created
2512 # if it does not exist. Sometimes you may want to forget about a
2513 # remote system, you can use sl --rsync, or the function for that slr
2516 # SL_TEST_CMD: Env var. Meant to be used to vary the files synced
2517 # depending on the remote host. Run this string on the remote host the
2518 # first time sl is run (or if we run slr). The result is passed to
2519 # SL_TEST_HOOK. For example,
2520 # export SL_TEST_CMD=". /etc/os-release ; echo \${VERSION//[^a-zA-Z0-9]/}"
2522 # SL_TEST_HOOK: Env var. It is run as $SL_TEST_HOOK. This can set
2523 # $SL_FILES_DIR to vary the files synced.
2525 # SL_RSYNC_ARGS: Env var. String of arguments passed to rsync. For
2526 # example to exclude files within a directory. Note, excluded
2527 # files wont be deleted on rsync, you can add --delete-excluded
2528 # to the rsync command if that is desired.
2530 # SL_SSH_ARGS: Env var. Default arguments passed to ssh.
2532 # For when ~/.bashrc is already customized on the remote server, you
2533 # might find it problematic that ~/.bashrc is sourced for ALL ssh
2534 # commands, even in scripts. This paragraph is all about that. bash
2535 # scripts dont source ~/.bashrc, but call ssh in scripts and you get
2536 # ~/.bashrc. You dont want this. .bashrc is meant for interactive shells
2537 # and if you customize it, probably has bugs from time to time. This is
2538 # bad. Here's how I fix it. I have a special condition to "return" in my
2539 # .bashrc for noninteractive ssh shells (copy that code). Then use this
2540 # function or similar that passes LC_USEBASHRC=t when sshing and I want
2541 # my bashrc. Also, I don't keep most of my bashrc in .bashrc, i source a
2542 # separate file because even if I return early on, the whole file gets
2543 # parsed which can fail if there is a syntax error.
2545 # Background on LC_USEBASHRC var (no need to read if you just want to
2546 # use this function): env variables sent across ssh are strictly
2547 # limited, but we get LC_* at least in debian based machines, so we
2548 # just make that * be something no normal program would use. Note, on
2549 # hosts that dont allow LC_* I start an inner shell with LC_USEBASHRC
2550 # set, and the inner shell also allows running a nondefault
2551 # .bashrc. This means the outer shell still ran the default .bashrc,
2552 # but that is the best we can do.
2554 local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2
type info_sec force_rsync \
2555 sync_dirname testcmd extra_info testbool files_sec sl_test_cmd sl_test_hook
2556 declare -a args tmpa
2560 # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
2561 # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address]
2562 # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option]
2563 # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname
2566 # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
2567 # [-D [bind_address:]port] [-E log_file] [-e escape_char]
2568 # [-F configfile] [-I pkcs11] [-i identity_file]
2569 # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]
2570 # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
2571 # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
2574 if [[ $1 == --rsync ]]; then
2578 # shellcheck disable=SC2153 # intentional
2579 sl_test_cmd
=$SL_TEST_CMD
2580 # shellcheck disable=SC2153 # intentional
2581 sl_test_hook
=$SL_TEST_HOOK
2582 # shellcheck disable=SC2153 # intentional
2583 sl_rsync_args
=$SL_RSYNC_ARGS
2610 # note we dont support things like -4oOption
2611 -[46AaCfGgKkMNnqsTtVvXxYy
]*)
2614 -[bcDEeFIiJLlmOopQRSWw
]*)
2615 # -oOption etc is valid
2616 if (( ${#1} >= 3 )); then
2619 args
+=("$1" "$2"); shift 2
2628 if [[ ! $remote ]]; then
2629 echo $0: error hostname required
>&2
2634 if [[ ! $SL_INFO_DIR ]]; then
2635 echo 'error: missing SL_INFO_DIR env var' >&2
2641 tmpa
=($SL_INFO_DIR/???????????
"$remote")
2643 if [[ -e $sshinfo ]]; then
2644 if $force_rsync; then
2651 tmp
=${sshinfo[0]##*/}
2654 extra_info
=$
(cat $sshinfo)
2656 # we test for string to know ssh succeeded
2657 testbool
="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
2658 testcmd
="if $testbool; then printf y; else printf n; fi"
2659 if ! tmp
=$
(LC_USEBASHRC
=y
command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
2660 echo failed sl
test. doing plain
ssh -v
2661 command ssh -v "${args[@]}" "$remote"
2663 if [[ $tmp == y
* ]]; then
2669 extra_info
="${tmp:1}"
2671 if [[ $sl_test_hook ]]; then
2672 RSYNC_RSH
="ssh ${args[*]}" $sl_test_hook "$extra_info" "$remote"
2675 if $haveinfo && [[ $type == b
]]; then
2677 read -r files_sec _
< <(find -L $SL_FILES_DIR -printf "%T@ %p\n" |
sort -nr ||
[[ $?
== 141 ||
${PIPESTATUS[0]} == 32 ]] )
2678 files_sec
=${files_sec%%.*}
2679 if (( files_sec
> info_sec
)); then
2685 sync_dirname
=${SL_FILES_DIR##*/}
2687 if [[ ! $SL_FILES_DIR ]]; then
2688 echo 'error: missing SL_FILES_DIR env var' >&2
2693 RSYNC_RSH
="ssh ${args[*]}" m rsync
-rptL --delete $sl_rsync_args $SL_FILES_DIR "$remote":
2695 if $dorsync ||
! $haveinfo; then
2696 sshinfo
=$SL_INFO_DIR/$EPOCHSECONDS$type"$remote"
2697 [[ -e $SL_INFO_DIR ]] || mkdir
-p $SL_INFO_DIR
2698 printf "%s\n" "$extra_info" >$sshinfo
2701 if [[ $type == b
]]; then
2702 if (( ${#@} )); then
2703 # Theres a couple ways to pass arguments, im not sure whats best,
2704 # but relying on bash 4.4+ escape quoting seems most reliable.
2705 command ssh "${args[@]}" "$remote" \
2706 LC_USEBASHRC
=t bash
-c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash
${@@Q}
2707 elif [[ ! -t 0 ]]; then
2708 # This case is when commands are being piped to ssh.
2709 # Normally, no bashrc gets sourced.
2710 # But, since we are doing all this, lets source it because we can.
2711 cat <(echo .
$sync_dirname/.bashrc
) - |
command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2713 command ssh -t "${args[@]}" "$remote" LC_USEBASHRC
=t INPUTRC
=$sync_dirname/.inputrc bash
--rcfile $sync_dirname/.bashrc
2717 LC_USEBASHRC
=t
command ssh "${args[@]}" "$remote" ${@@Q}
2719 command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2722 # this function inspired from https://github.com/Russell91/sshrc
2729 sl
-oControlMaster=no
-oControlPath=/ "$@"
2731 # kill off old shared socket then ssh
2733 m
ssh -O exit "$@" ||
[[ $?
== 255 ]]
2736 ccomp
ssh sl slr sss ssk
2739 if [[ $TERM == alacritty ||
$TERM == xterm-kitty
]]; then
2740 TERM
=xterm-256color LC_USEBASHRC
=t
command ssh "$@"
2742 LC_USEBASHRC
=t
command ssh "$@"
2748 # log with script. timing is $1.t and script is $1.s
2749 # -l to save to ~/typescripts/
2750 # -t to add a timestamp to the filenames
2751 local logdir do_stamp arg_base
2752 (( $# >= 1 )) ||
{ echo "arguments wrong"; return 1; }
2755 while getopts "lt" option
2758 l
) arg_base
=$logdir ;;
2761 echo error
: bad option
2766 shift $
((OPTIND
- 1))
2768 [[ -e $logdir ]] || mkdir
-p $logdir
2769 $do_stamp && arg_base
+=$
(date +%F.
%T
%z
)
2770 script -t $arg_base.s
2> $arg_base.t
2772 splay
() { # script replay
2773 #logRoot="$HOME/typescripts/"
2774 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
2775 scriptreplay
"$1.t" "$1.s"
2779 # sudo redo. be aware, this command may not work right on strange distros or earlier software
2780 if [[ $# == 0 ]]; then
2781 sudo
-E bash
-c -l "$(history -p '!!')"
2783 echo this
command redos last
history item. no argument is accepted
2788 # with -ll, less secure but faster.
2789 command srm
-ll "$@"
2794 ssh $1 "/tmp/${2##*/}" "$(printf "%q
\n" "${@:2}")"
2806 tclock
() { # terminal clock
2811 # this goes to full width
2812 #len=${1:-$((COLUMNS -7))}
2815 if (( x
== len
)); then
2817 d
="$(date +%l:%_M) "
2820 d
=$
(date +%l
:%M
:%_S
)
2824 for ((i
=0; i
<x
; i
++)); do
2825 if (( i
% 6 )); then
2843 # test existence / exists
2846 [[ -e "$x" ||
-L "$x" ]] || ret
=1
2852 # normally, i would just execute these commands in the function.
2853 # however, DEBUG is not inherited, so we need to run it outside a function.
2854 # And we want to run set -x afterwards to avoid spam, so we cram everything
2855 # in here, and then it will run after this function is done.
2856 # # set as array to satisfy shellcheck, but it is equivalent to setting it as non-array
2857 PROMPT_COMMAND
=('trap DEBUG; unset PROMPT_COMMAND; PS1="\w \$ "')
2860 PROMPT_COMMAND
=(prompt-command
)
2861 if [[ $TERM == *(screen
*|xterm
*|rxvt
*) ]]; then
2862 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
2866 # prometheus node curl
2869 host=${1:-127.0.0.1}
2870 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
2873 tx
() { # toggle set -x, and the prompt so it doesnt spam
2874 if [[ $
- == *x
* ]]; then
2883 # show all processes in the network namespace $1.
2884 # blank entries appear to be subprocesses/threads
2888 sudo
find -L /proc
/[1-9]*/task
/*/ns
/net
-samefile /run
/netns
/$netns | cut
-d/ -f5 | \
2890 x
=$
(ps
-w --no-headers -p $l);
2891 if [[ $x ]]; then echo "$x"; else echo $l; fi;
2895 if ! s ip netns list |
grep -Fx nonet
&>/dev
/null
; then
2896 s ip netns add nonet
2898 sudo
-E env
/sbin
/ip netns
exec nonet sudo
-E -u iank
/bin
/bash
2901 m
() { printf "%s\n" "$*"; "$@"; }
2903 # update file. note: duplicated in mail-setup.
2904 # updates $ur u result to true or false
2905 # updates $reload to true if file updated is in /etc/systemd/system
2907 local tmp tmpdir dest
="$1"
2908 local base
="${dest##*/}"
2909 local dir
="${dest%/*}"
2910 if [[ $dir != "$base" ]]; then
2911 # dest has a directory component
2914 # shellcheck disable=SC2034 # see comment at top of function
2916 tmpdir
="$(mktemp -d)"
2917 cat >$tmpdir/"$base"
2918 tmp
=$
(rsync
-ic $tmpdir/"$base" "$dest")
2920 printf "%s\n" "$tmp"
2921 # shellcheck disable=SC2034 # see comment at top of function
2923 if [[ $dest == /etc
/systemd
/system
/* ]]; then
2924 # shellcheck disable=SC2034 # see comment at top of function
2933 if type -p uprecords
&>/dev
/null
; then
2941 for x
in "$@"; do virsh destroy
"$x"; virsh undefine
"$x"; done
2949 sudo virsh dumpxml
$vm |
sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
2950 sed -r "s/listen='[^']+/listen='$ip/"> $t
2951 sudo virsh undefine
$vm
2952 sudo virsh define
$t
2957 vm-set-listen
$1 0.0.0.0
2962 vm-set-listen
$1 127.0.0.1
2967 interfaces
=$
(iw dev |
awk '$1 == "Interface" {print $2}')
2968 for i
in $interfaces; do
2969 echo "myiwscan: considering $i"
2970 # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines.
2971 # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h}
2972 sudo iw dev
$i scan |
sed -rn "
2973 s/^\Wcapability: (.*)/\1/;Ta;h;b
2974 :a;s/^\Wsignal: -([^.]+).*/\1/;Tb;H;b
2975 # padded to min width of 20
2976 :b;s/\WSSID: (.*)/\1 /;T;s/^(.{20}(.*[^ ])?) */\1/;H;g;s/(.*)\n(.*)\n(.*)/\2 \3 \1/gp;b
2981 # Run script by copying it to a temporary location first,
2982 # and changing directory, so we don't have any open
2983 # directories or files that could cause problems when
3000 # spark 1 5 22 13 53
3004 # Copyright (c) Zach Holman, https://zachholman.com
3005 # https://github.com/holman/spark
3007 # As of 2022-10-28, I reviewed github forks that had several newer
3008 # commits, none had anything interesting. I did a little refactoring
3009 # mostly to fix emacs indent bug.
3011 # Generates sparklines.
3014 if [ "X$1" = "X-n" ]; then
3028 # find min/max values
3029 local min
=0xffffffff max
=0
3033 # on Linux (or with bash4) we could use `printf %.0f $n` here to
3034 # round the number but that doesn't work on OS X (bash3) nor does
3035 # `awk '{printf "%.0f",$1}' <<< $n` work, so just cut it off
3037 (( n
< min
)) && min
=$n
3038 (( n
> max
)) && max
=$n
3039 numbers
=$numbers${numbers:+ }$n
3043 local ticks
=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █
)
3045 # use a high tick if data is constant
3046 (( min
== max
)) && ticks
=(▅ ▆
)
3049 f
=$
(( ( (max-min
) <<8)/( tc - 1) ))
3054 _spark_echo -n ${ticks[$(( (((n-min)<<8)/f) ))]}
3059 pdfwc() { local f; for f; do echo "$f" "$(pdfinfo "$f" | awk '/^Pages:/ {print $2}')"; done }
3062 # nvm install script appended this to my .bashrc. I dont want to run it all the time,
3063 # so put it in a function.
3065 export NVM_DIR="$HOME/.nvm"
3066 # shellcheck disable=SC1091 # may not exist, & third party
3067 [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" # This loads nvm
3068 # shellcheck disable=SC1091 # may not exist, & third party
3069 [ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion" # This loads nvm bash_completion
3074 if date -d 'february 29' &>/dev/null; then
3084 if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
3091 # make vim work with my light colortheme terminal.
3093 if [[ -e ~/.vimrc ]]; then
3096 command vim -c ':colorscheme peachpuff' "$@"
3100 # ls count. usage: pass a directory, get the number of files.
3101 # https://unix.stackexchange.com/questions/90106/whats-the-most-resource-efficient-way-to-count-how-many-files-are-in-a-director
3103 # shellcheck disable=SC2790 disable=SC2012 # intentional
3107 # run then notify. close notification after the next prompt.
3110 dunstify -u critical -h string:x-dunst-stack-tag:profanity "$*"
3111 _psrun=(dunstctl close-all)
3114 dunstify -u critical -h string:x-dunst-stack-tag:profanity n
3115 _psrun=(dunstctl close-all)
3121 inotifywait -m "$dir" -e create -e moved_to | while read -r _ _ file; do
3135 if ! type -p sponge &>/dev/null; then
3136 echo "$0: error: missing dependency: sudo apt install moreutils" >&2
3141 echo "adding header to $f"
3142 if [[ -s $f ]]; then
3147 cat - "${f_maybe[@]}" <<EOF | sponge "$f"
3148 The following is the GNU All-permissive License as recommended in
3149 <https://www.gnu.org/licenses/license-recommendations.en.html>
3151 Copyright (C) $(date +%Y) Free Software Foundation <sysadmin@fsf.org>
3153 Copying and distribution of this file, with or without modification,
3154 are permitted in any medium without royalty provided the copyright
3155 notice and this notice are preserved. This file is offered as-is,
3156 without any warranty.
3158 Contributions are welcome. See <https://savannah.gnu.org/maintenance/fsf/>.
3164 # note, there is also the tool gron which is meant for this, but
3165 # this is good enough to not bother installing another tool
3167 # https://stackoverflow.com/questions/59700329/how-to-print-path-and-key-values-of-json-file-using-jq
3168 jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' "$@"
3172 "$@" |& ts || return $?
3179 if $use_color && type -p tput &>/dev/null; then
3180 # this is nice for a dark background terminal:
3181 # https://github.com/trapd00r/LS_COLORS
3182 # I would like if there was something similar for light.
3184 # the default bold green is too light.
3185 # this explains the codes: https://gist.github.com/thomd/7667642
3186 export LS_COLORS=ex=1
3188 term_bold="$(tput bold)"
3189 term_red="$(tput setaf 1)"
3190 term_green="$(tput setaf 2)"
3191 # shellcheck disable=SC2034 # expected
3192 term_yellow="$(tput setaf 3)"
3193 term_purple="$(tput setaf 5)"
3194 term_nocolor="$(tput sgr0)" # no font attributes
3196 # unused so far. commented for shellcheck
3197 # term_underl="$(tput smul)"
3198 # term_blue="$(tput setaf 4)"
3199 # term_cyan="$(tput setaf 6)"
3201 # Try to keep environment pollution down, EPA loves us.
3202 unset safe_term match_lhs use_color
3207 if [[ $- == *i* ]]; then
3212 if [[ $EUID == 1000 ]]; then
3219 # this needs to come before next ps1 stuff
3220 # this stuff needs bash 4, feb 2009,
3221 # old enough to no longer condition on $BASH_VERSION anymore
3225 if [[ $- == *i* ]] && [[ ! $LC_INSIDE_EMACS ]]; then
3227 bind -m vi-command B:shell-backward-word
3228 bind -m vi-command W:shell-forward-word
3231 if [[ $SSH_CLIENT || $SUDO_USER ]]; then
3232 unset PROMPT_DIRTRIM
3236 # emacs terminal has problems if this runs slowly,
3237 # so I've thrown a bunch of things at the wall to speed it up.
3239 local return=$? # this MUST COME FIRST
3240 local ps_char ps_color
3243 if [[ $HISTFILE ]]; then
3244 history -a # save history
3248 0) ps_color="$term_purple"
3251 *) ps_color="$term_green"
3252 ps_char="$return \\$"
3255 if [[ ! -O . ]]; then # not owner
3256 if [[ -w . ]]; then # writable
3257 ps_color="$term_bold$term_red"
3259 ps_color="$term_bold$term_green"
3263 # faster than sourceing the file im guessing
3264 if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then
3265 eval "$(< /dev/shm/iank-status)"
3267 if [[ $MAIL_HOST && $MAIL_HOST != "$HOSTNAME" ]]; then
3268 ps_char="@ $ps_char"
3271 if [[ $(jobs -p) ]]; then
3276 # allow a function to specify a command to run after we run the next
3277 # command. Use case: a function makes a persistent notification. If
3278 # we happen to be using that terminal, we can just keep working by
3279 # entering our next command, even a noop in order to dismiss the
3280 # notification, instead of having to explicitly dismiss it.
3281 if [[ ${_psrun[*]} ]]; then
3282 if (( _psrun_count >= 1 )); then
3288 _psrun_count=$(( _psrun_count + 1 ))
3294 # We could test if sudo is active with sudo -nv
3295 # but then we get an email and log of lots of failed sudo commands.
3296 # We could turn those off, but seems better not to.
3297 if [[ $EUID != 0 ]] && [[ $DID_SUDO ]]; then
3298 psudo="\[$term_bold$term_red\]s\[$term_nocolor\] "
3300 if [[ ! $HISTFILE ]]; then
3301 ps_char="NOHIST $ps_char"
3303 PS1="${PS1%"${PS1#*[wW]}"} $jobs_char$psudo\[$ps_color\]$ps_char\[$term_nocolor\] "
3305 # copy of what is automatically added by guix.
3306 # adds [env] to PS1 if GUIX_ENVIRONMENT is set and PS1 contains '$';
3307 if [ -n "$GUIX_ENVIRONMENT" ]; then
3308 if [[ $PS1 =~ (.*)"\\$" ]]; then
3309 PS1="${BASH_REMATCH[1]} [env]\\\$ "
3314 # set titlebar. instead, using more advanced
3316 #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007"
3318 PROMPT_COMMAND=(prompt-command)
3320 if [[ $TERM == screen* ]]; then
3321 _title_escape="\033]..2;"
3323 # somme sites recommend this, i dunno what the diff is.
3324 #_title_escape="\033]30;"
3325 _title_escape="\033]0;"
3328 # make the titlebar be the last command and the current directory.
3329 auto-window-title () {
3332 # These are some checks to help ensure we dont set the title at
3333 # times that the debug trap is running other than the case we
3334 # want. Some of them might not be needed.
3335 if (( ${#FUNCNAME[@]} != 1 || ${#BASH_ARGC[@]} != 2 || BASH_SUBSHELL != 0 )); then
3338 if [[ $1 == prompt-command ]]; then
3341 echo -ne "$_title_escape ${PWD/#$HOME/~} "
3346 # note, this wont work:
3347 # x=$(mktemp); cp a $x
3348 # I havnt figured out why, bigger fish to fry.
3351 # condition from the screen man page i think.
3352 # note: duplicated in tx()
3353 if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
3354 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
3361 # * stuff that makes sense to be at the end
3367 if [[ -s "$HOME/.rvm/scripts/rvm" ]]; then
3368 # shellcheck disable=SC1091
3369 source "$HOME/.rvm/scripts/rvm"
3372 # I had this idea to start a bash shell which would run an initial
3373 # command passed through this env variable, then continue on
3374 # interactively. But the use case I had in mind went away.
3376 # if [[ $MY_INIT_CMD ]]; then
3377 # "${MY_INIT_CMD[@]}"
3381 # ensure no bad programs appending to this file will have an affect