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
319 export NNN_COLORS
=2136
321 export SL_FILES_DIR
=/b
/ds
/sl
/.iank
322 export SL_INFO_DIR
=/p
/sshinfo
327 # this is adapted from things printed to term after install
328 # pyenv. commented for now since I'm not actually using pyenv.
330 # export PYENV_ROOT="$HOME/.pyenv"
331 # command -v pyenv &>/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
332 # command -v pyenv &>/dev/null && eval "$(pyenv init -)"
335 # output showed this example for pyenv-virtualenv, which i have no idea
336 # what it is, but leaving it as a comment in case I end up doing python
339 #eval "$(pyenv virtualenv-init -)"
340 ### end begin pyenv ###
346 if [[ -s $bashrc_dir/path-add-function
]]; then
347 source $bashrc_dir/path-add-function
348 if [[ $SSH_CLIENT ]]; then
349 if grep -qF /home
/iank
/.iank
/e
/e
/etc
/exports
&>/dev
/null
; then
350 export EMACSDIR
=/home
/iank
/.iank
/e
/e
356 # if someone exported $SOE (stop on error), catch errors.
358 # Note, on debian this results in the following warning when in ssh,
359 # hich I haven't figured out how to fix. It doesn't happen if we source
360 # after the shell has started
362 # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory
363 # bash: warning: cannot start debugger; debugging mode disabled
365 if [[ -e /a
/bin
/bash-bear-trap
/bash-bear
]]; then
366 source /a
/bin
/bash-bear-trap
/bash-bear
371 path-add
--ifexists /usr
/local
/go
/bin
379 if [[ -s $path ]]; then
380 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
382 elif [[ -s $bashrc_dir/$file ]]; then
383 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
384 source $bashrc_dir/$file
389 mysrc
/a
/bin
/small-misc-bash
/ll-function
390 mysrc
/a
/bin
/distro-functions
/src
/package-manager-abstractions
392 # things to remember:
393 # ALT-C - cd into the selected directory
394 # CTRL-T - Paste the selected file path into the command line
396 # good guide to some of its basic features is the readme file
397 # https://github.com/junegunn/fzf
399 # if [[ -s /usr/share/doc/fzf/examples/key-bindings.bash ]]; then
400 # source /usr/share/doc/fzf/examples/key-bindings.bash
406 # temporary functions
408 m
"${@//spring/fall}"
411 e
"${@//spring/fall}"
415 ### begin FSF section ###
417 # Comments before functions are meant to be good useful
418 # documentation. If they fail at that, please improve them or send Ian a
421 ## copy bash completion
423 # It copies how the bash completion works from one command to other
424 # commands. Generally just use within a .bashrc.
426 # Usage: ORIGINAL_COMMAND TARGET_COMMAND...
432 if ! c
=$
(complete
-p $src 2>/dev
/null
); then
433 _completion_loader
$src &>/dev
/null ||
:
434 c
=$
(complete
-p $src 2>/dev
/null
) ||
return 0
442 ## BEGIN functions to change directory better than cd ##
446 # c: acts like cd, but stores directory history: you could alias to cd if you wanted.
449 # cl: list recent directories and optionally choose one.
451 # Finer details you may want to skip:
453 # bl: print the list of back and forward directories.
455 # We keep 2 stacks of directories, forward and back. Unlike with a web
456 # browser, the forward stack is not erased when going somewhere new.
458 # Recent directories are stored in ~/.cdirs.
460 declare -a _dir_forward _dir_back
462 # normally, the top of _dir_back is our current dir. if it isn't,
463 # put it on there, except we don't want to do that when we
464 # just launched a shell
465 if [[ $OLDPWD ]]; then
466 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
471 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
474 echo "$PWD" >> ~
/.cdirs
481 if (( ${#_dir_back[@]} == 0 )); then
482 echo "nothing left to go back to" >&2
485 top_back
="${_dir_back[-1]}"
487 if [[ $top_back == "$PWD" ]] && (( ${#_dir_back[@]} == 1 )); then
488 echo "already on last back entry" >&2
493 if [[ $top_back == "$PWD" ]]; then
494 # add to dirf if not already there
495 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$top_back" ]]; then
496 _dir_forward
+=("$top_back")
498 unset "_dir_back[-1]"
499 command cd "${_dir_back[-1]}"
501 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$PWD" ]]; then
502 _dir_forward
+=("$PWD")
504 command cd "$top_back"
507 # Interesting feature, not sure I want it.
508 # give us a peek at what is next in the list
509 # if (( ${#_dir_back[@]} >= 2 )); then
510 # printf "%s\n" "${_dir_back[-2]}"
514 # c/b/f Implementation notes:
516 # The top of the back is $PWD
517 # as long as the last directory change was due to c,b,or cl.
519 # Example of stack changes:
545 if (( ${#_dir_forward[@]} == 0 )); then
546 echo "no forward dir left" >&2
549 top_forward
="${_dir_forward[-1]}"
550 unset "_dir_forward[-1]"
553 # give us a peek at what is next in the list
554 # if (( ${#_dir_forward[@]} )); then
555 # printf "%s\n" "${_dir_forward[-1]}"
560 local i line input start
561 local -A buttondirs alines
562 local -a buttons
dirs lines
563 buttons
=( {a..z
} {2.
.9} )
564 if [[ ! -s ~
/.cdirs
]]; then
565 echo nothing
in ~
/.cdirs
571 mapfile
-t lines
<~
/.cdirs
572 start
=$
(( ${#lines[@]} - 1 ))
574 # we have ~33 buttons as of this writing, so lets
575 # prune down the history every once in a while.
576 if (( start
> 500 )); then
577 tac ~
/.cdirs |
awk '!seen[$0]++' |
head -n 200 |
tac | sponge ~
/.cdirs ||
[[ $?
== 141 ]]
580 for (( j
=start
; j
>= 0; j--
)); do
582 if [[ ! $line ||
${alines[$line]} ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
586 buttondirs
[${buttons[i]}]="$line"
587 printf "%s %s\n" ${buttons[i]} "$line"
588 # the LINES bit is for when we have a short terminal, just dont print all
589 # the directories. alternative would be to do something like less the list.
590 if (( i
== ${#buttons[@]} - 1 )) ||
{ [[ $LINES ]] && (( i
== LINES
- 3 )); }; then
596 if (( i
== 0 )); then
597 echo "no dirs in ~/.cdirs"
601 if [[ $input != $
'\n' ]]; then
602 c
"${buttondirs[$input]}"
605 # bl = back list. lists the back and forward directories. i tend to
606 # forget this exists and use cl instead.
610 start
=$
(( ${#_dir_back[@]} - 1 ))
612 # cleanup possible repeating of pwd
613 if (( start
>= 0 )) && [[ ${_dir_back[$start]} == "$PWD" ]]; then
614 start
=$
(( start
- 1 ))
617 if (( start
>= 0 )); then
618 for (( i
=start
; i
>= 0 ; i--
)); do
619 printf "%s %s\n" $j ${_dir_back[i]}
621 if (( j
>= max
)); then
628 start
=$
(( ${#_dir_forward[@]} - 1 ))
630 # cleanup possible repeating of pwd
631 if (( start
>= 0 )) && [[ ${_dir_forward[$start]} == "$PWD" ]]; then
632 start
=$
(( start
- 1 ))
634 if (( start
< 0 )); then
639 for (( i
=start
; i
>= 0 ; i--
)); do
640 printf "%s %s\n" $j ${_dir_forward[i]}
642 if (( j
>= max
)); then
647 # like running cl <enter> a <enter>
650 mapfile
-t lines
<~
/.cdirs
651 start
=$
(( ${#lines[@]} - 1 ))
652 for (( j
=start
; j
>= 0; j--
)); do
654 if [[ ! $line ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
662 ## END functions to change directory better than cd ##
664 # pee do. run args as a command with output copied to syslog.
666 # Usage: pd [-t TAG] COMMAND...
668 # -t TAG Override the tag in the syslog. The default is COMMAND with
669 # any path part is removed, eg. for /bin/cat the tag is cat.
671 # You can view the log via "journalctl -t TAG"
677 -t) tag
="$2"; shift 2 ;;
679 echo "PWD=$PWD command: $*" | logger
-t $tag
680 "$@" |
& pee
cat "logger -t $tag" || ret
=$?
681 echo "exited with status=$ret" | pee
cat "logger -t $tag"
682 # this avoids any err-catch
683 (( ret
== 0 )) ||
return $ret
687 # jdo = journal do. Run command as transient systemd service, tailing
688 # its output in the journal until it completes.
690 # Usage: jdo COMMAND...
692 # Compared to pd: commands recognize this is a non-interactive shell.
693 # The service is unaffected if our ssh connection dies, no need to run
696 # Note: The last few lines of any existing entries for a unit by that
697 # name will be output first, and there will be a few second delay at the
698 # start of the command, and a second or so at the end.
700 # Note: Functions and aliases obviously won't work, we resolve the
703 # Note: requires running as root.
705 local cmd cmd_name jr_pid ret
709 if [[ $EUID != 0 ]]; then
710 echo "jdo: error: rerun as root"
714 if [[ $cmd != /* ]]; then
715 cmd
=$
(type -P "$cmd")
718 journalctl
-qn2 -f -u "$cmd_name" &
719 # Trial and error of time needed to avoid missing initial lines.
720 # .5 was not reliable. 1 was not reliable. 2 was not reliable
723 systemd-run
--unit "$cmd_name" --wait --collect "$cmd" "$@" || ret
=$?
724 # The sleep lets the journal output its last line
725 # before the prompt comes up.
727 kill $jr_pid &>/dev
/null ||
:
730 # this avoids any err-catch
731 (( ret
== 0 )) ||
return $ret
735 # standard date as used in logs
740 # date in log appropriate format
747 command ts
"%F %T" "$@"
750 # ts log. log command to log file.
751 # usage: tsl LOG_PATH_PREFIX COMMAND...
752 # example: tsl /root/command
753 # log file will be like /root/command-2024-02-10.log
755 local log_prefix log_path appending ret
756 if (( $# < 2 )); then
757 echo "tsl: error: expected >= 2 arguments, got $#" >&2
761 if [[ $log_prefix == */* && ! -d ${log_prefix%*/} ]]; then
762 echo "tsl: error: expected directory at ${log_prefix%*/}" >&2
765 log_path
=$log_prefix-$
(date +%Y-
%m-
%d
).log
767 if [[ -s $log_path ]]; then
771 printf "%s\n" "CWD: $PWD, log: $log_path, running $*" | ts
"%F %T" |
tee -a "$log_path"
773 "$@" |
& ts
"%F %T" |
tee -a "$log_path" || ret
=$?
774 printf "%s\n" "exit code $ret from command: $*" | ts
"%F %T" |
tee -a "$log_path"
776 printf "%s\n" "note: this log file contains logs before those of previous command" | ts
"%F %T" |
tee -a "$log_path"
782 mapfile
-t cmds
<<'EOF'
783 tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab
786 ls -la /dev/disk/by-id
789 for cmd
in "${cmds[@]}"; do
809 ....
() { c ..
/..
/..
; }
810 .....
() { c ..
/..
/..
/..
; }
811 ......
() { c ..
/..
/..
/..
/..
; }
816 path
=$
(readlink
-e "$f")
817 echo "cat >$path <<'EOF'"
824 # file cut copy and paste, like the text buffers :)
825 # I havnt tested these.
826 _fbufferinit
() { # internal use
827 ! [[ $my_f_tempdir ]] && my_f_tempdir
="$(mktemp -d)"
828 rm -rf "${my_f_tempdir:?}"/*
832 cp "$@" "$my_f_tempdir"/
836 mv "$@" "$my_f_tempdir"/
838 fpst
() { # file paste
839 [[ $2 ]] && { echo too many arguments
; return 1; }
841 cp "$my_f_tempdir"/* "$target"
845 local host ip port
file key tmp
846 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" ||
: )
847 file=$
(readlink
-f ~
/.ssh
/known_hosts
)
849 echo "khfix: ssh failed"
852 if [[ $port != 22 ]]; then
853 ip_entry
="[$ip]:$port"
854 host_entry
="[$host]:$port"
859 if [[ $host != "$ip" ]]; then
861 ssh-keygen
-F "$host_entry" -f $file >$tmp ||
[[ $?
== 1 ]] # 1 when it doesnt exist in the file
862 if [[ -s $tmp ]]; then
863 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
867 grep -Fv "$key" "$file" | sponge
"$file"
872 ssh-keygen
-F "$ip_entry" -f $file >$tmp ||
[[ $?
== 1 ]]
873 if [[ -s $tmp ]]; then
874 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
878 grep -Fv "$key" "$file" | sponge
"$file"
880 ll ~
/.ssh
/known_hosts
882 khfix-r
() { # known hosts fix + root
883 _khfix-common
"$@" ||
return 1
888 _khfix-common
"$@" ||
return 1
892 # copy path into clipboard
895 x
=$
(readlink
-nf "${1:-$PWD}")
896 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
897 # And, summarizing this:
898 # https://askubuntu.com/questions/705620/xclip-vs-xsel
899 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
903 # clipboard a string (into selection & clipboard buffer)
905 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
906 # And, summarizing this:
907 # https://askubuntu.com/questions/705620/xclip-vs-xsel
908 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
909 printf "%s" "$*" | xclip
-selection clipboard
910 printf "%s" "$*" | xclip
913 # a1 = awk {print $1}
914 for field
in {1.
.20}; do
915 eval a
$field"() { awk '{print \$$field}'; }"
918 for num
in {1.
.9}; do
919 eval h
$num"() { head -n$num || [[ \$? == 141 ]]; }"
924 # shellcheck disable=SC2046 disable=SC2001 disable=SC2183 # hacks, expected
925 printf '%d.%d.%d.%d\n' $
(echo $1 |
sed 's/../0x& /g')
929 local f out outdir
in fname origdir skip1
933 while [[ $1 == -* ]]; do
935 # if we got interrupted after 1st phase
947 # first pass only uses about 1 cpu, so run in parallel
951 if [[ $f == /* ]]; then
956 out
="$origdir/$outdir/$fname"
957 mkdir
-p /tmp
/vp
9/$fname
959 if ! $skip1 && [[ ! -s ffmpeg2pass-0.log
]]; then
960 # -nostdin or else wait causes ffmpeg to go into stopped state. dunno why, random stackoverflow answer.
961 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
963 if [[ -e $out ]]; then rm -f $out; fi
964 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
971 utcl
() { # utc 24 hour time to local hour 24 hour time
972 echo "print( ($1 $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
980 # for running in a fai rescue. iank specific.
982 d
=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
983 for f
in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V
; do
984 cryptsetup luksOpen
--key-file /p
/dev
/$f/root crypt-
$f-root
985 cryptsetup luksOpen
--key-file /p
/dev
/$f/o crypt-
$f-o
987 mount
-o subvol
=root_trisquelaramo
/dev
/mapper
/crypt-
$d-root /mnt
988 mount
-o subvol
=a
/dev
/mapper
/crypt-
$d-root /mnt
/a
989 mount
-o subvol
=o
/dev
/mapper
/crypt-
$d-o /mnt
/o
990 mount
-o subvol
=boot_trisquelaramo
/dev
/sda2
/mnt
/boot
998 c4
() { c
/var
/log
/exim4
; }
1000 caa
() { git commit
--amend --no-edit -a; }
1013 find -L "$@" -type f
-not \
( -name .svn
-prune -o -name .git
-prune \
1014 -o -name .hg
-prune -o -name .editor-backups
-prune \
1015 -o -name .undo-tree-history
-prune \
) -printf '%h\0%d\0%p\n' |
sort -t '\0' -n \
1016 |
awk -F '\0' '{print $3}' 2>/dev
/null |
while read -r file; do
1018 printf "%s\n" "$file"
1025 calc
() { echo "scale=3; $*" |
bc -l; }
1026 # no having to type quotes, but also no command history:
1030 echo "scale=3; $x" |
bc -l
1041 ccat
() { # config cat. see a config without extra lines.
1042 sed -r '/^[[:space:]]*([;#]|--|\/\/|$)/d' "$@"
1048 # dev/pts needed for pacman signature check
1049 for d
in dev proc sys dev
/pts
; do
1051 if ! mountpoint
$d &>/dev
/null
; then
1052 m s mount
-o bind /$d $d
1058 # dev/pts needed for pacman signature check
1059 for d
in dev
/pts dev proc sys
; do
1061 if mountpoint
$d &>/dev
/null
; then
1069 # join options which are continued to multiples lines onto one line
1071 while IFS
= read -r line
; do
1072 # remove leading spaces/tabs. assumes extglob
1073 if [[ $line == "[ ]*" ]]; then
1074 line
="${line##+( )}"
1079 elif [[ $line == *=* ]]; then
1080 echo "$pastline" >> "$2"
1083 pastline
="$pastline $line"
1085 done < <(grep -vE '^([ \t]*#|^[ \t]*$)' "$1")
1086 echo "$pastline" >> "$2"
1090 # diff config files,
1091 # setup for format of postfix, eg:
1094 local pastline unified f1 f2
1098 _cdiff-prep
"$1" "$f1"
1099 _cdiff-prep
"$2" "$f2"
1100 cat "$f1" "$f2" |
grep -Po '^[^=]+=' |
sort |
uniq > "$unified"
1101 while IFS
= read -r line
; do
1102 # the default bright red / blue doesnt work in emacs shell
1103 dwdiff
-cblue,red
-A best
-d " ," <(grep "^$line" "$f1" ||
echo ) <(grep "^$line" "$f2" ||
echo ) | colordiff
1109 local start
=$SECONDS
1111 # shellcheck disable=SC2030
1112 inotifywait
-m "$dir" -e create
-e moved_to | \
1113 while read -r filedir _
file; do
1116 calc $
((SECONDS
- start
)) / 60
1123 s chown
-R $USER:$USER "$@"
1126 # shellcheck disable=SC2032
1128 # makes it so chown -R symlink affects the symlink and its target.
1129 if [[ $1 == -R ]]; then
1131 command chown
-h "$@"
1132 command chown
-R "$@"
1143 d
() { builtin bg "$@"; }
1146 # f would be more natural, but i already am using it for something
1147 z
() { builtin fg "$@"; }
1150 x
() { builtin kill %%; }
1153 diff --strip-trailing-cr -w "$@" # diff content
1161 safe_rename
"$x" "$y"
1166 # usage: dfp MOUNTPOINT [SECOND_INTERVAL]
1167 # SECOND_INTERVAL defaults to 90
1170 local a b mp interval
1173 if [[ ! $mp ]]; then
1174 echo "dfp: error, missing 1st arg" >&2
1178 a
=$
(df
--output=used
$mp |
tail -n1)
1180 b
=$
(df
--output=used
$mp |
tail -n1)
1181 printf "used mib: %'d mib/min: %s\n" $
(( b
/1000 )) $
(( (b-a
) / (interval
* 1000 / 60 ) ))
1185 # get ipv4 ip from HOST. or if it is already a number, return that
1193 getent ahostsv4
"$host" |
awk '{ print $1 }' |
head -n1
1199 command dig +nostats
+nocmd
"$@"
1201 # Output with sections sorted, and removal of query id, so 2 dig outputs can be diffed.
1205 dig +nordflag
"$@" |
sed -r 's/^(;; ->>HEADER<<-.*), id: .*/\1/' |
while read -r l
; do
1206 if [[ $l == [^\
;]* ]]; then
1210 printf "%s" "$sec" |
sort
1218 # compare digs to the 2 servers
1219 # usage: digdiff @server1 @server2 DIG_ARGS
1220 # note: only the soa master nameserver will respond with
1221 # ra "recursive answer" flag. That difference is meaningless afaik.
1228 digsort
$s1 "$@" |
tee /tmp
/digdiff
1229 diff -u /tmp
/digdiff
<(digsort
$s2 "$@")
1232 # date in a format i like reading
1234 date "+%A, %B %d, %r" "$@"
1239 # date with all digits in a format i like
1243 ccomp
date dt dtr dtd
1245 dus
() { # du, sorted, default arg of
1246 du
-sh ${@:-*} |
sort -h
1251 e
() { printf "%s\n" "$*"; }
1259 printf "%qEOL\n" "${arg}"
1260 printf "%s" "${arg}" |
& hexdump -C
1264 # echo variables. print var including escapes, etc, like xxd for variable
1270 if [[ -v $arg ]]; then
1271 printf "%qEOL\n" "${!arg}"
1272 printf "%s" "${!arg}" |
& hexdump -C
1274 echo arg
$arg is
unset
1280 [[ ${#@} == 2 ]] ||
{ echo "error: ediff requires 2 arguments"; return 1; }
1281 emacs
--eval "(ediff-files \"$1\" \"$2\")"
1285 # shellcheck disable=SC2120 # we expect to pass arguments in use outside this file
1288 tail -F /var
/log
/exim
4/mainlog
/var
/log
/exim
4/*main
/var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
-n 200 "$@"
1292 tail -F /var
/log
/exim
4/mainlog
-n 200 "$@"
1295 tail -F /var
/log
/exim
4/mymain
-n 200 "$@"
1297 ccomp
tail etail etail2
1299 # ran into this online, trying it out
1301 ( "$@" &>/dev
/null
& disown )
1305 ssh "$@" cat .ssh
/authorized_keys
{,2}
1309 # print exim old pids
1311 local configtime pid piduptime now daemonpid
1312 printf -v now
'%(%s)T' -1
1313 configtime
=$
(stat
-c%Y
/var
/lib
/exim
4/config.autogenerated
)
1314 if [[ -s /run
/exim
4/exim.pid
]]; then
1315 daemonpid
=$
(cat /run
/exim
4/exim.pid
)
1317 for pid
in $
(pgrep
-f '^/usr/sbin/exim4( |$)'); do
1318 # the daemonpid gets reexeced on HUP (service reloads), keeping its same old timestamp
1319 if [[ $pid == "$daemonpid" ]]; then
1322 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
1323 if (( configtime
> now
- piduptime
)); then
1329 # exim tail but only watch lines from new pids
1332 for pid
in $
(eoldpids
); do
1335 if [[ $oldpids ]]; then
1336 etail |
awk '$3 !~ /^\[('"${oldpids%|}"')\]$/'
1341 # exim watch as old pids go away
1343 local configtime pid piduptime now tmpstr
1349 mapfile
-t oldpids
<<<"$tmpstr"
1350 if (( ! ${#oldpids[@]} )); then
1353 # print the date every 20 iterations
1354 if (( ! count
% 20 )); then
1358 ps
-f -p "${oldpids[*]}"
1364 less /var
/log
/exim
4/mainlog
1368 exiqgrep
-ir.\
* -o 60 |
while read -r i
; do
1371 hlm exigrep
$i /var
/log
/exim
4/mainlog |
cat ||
:
1375 # other ways to get the list of message ids:
1376 # exim -bp | awk 'NF == 4 {print $3}'
1377 # # this is slower 160ms, vs 60.
1379 exiqgrep
-ir.\
* |
xargs exim
-Mrm
1384 mkdir
-p /tmp
/edev
/etc
1385 cp -ra /etc
/exim4
/tmp
/edev
/etc
1386 cp -ra /etc
/alias* /tmp
/edev
/etc
1387 find /tmp
/edev
/etc
/exim4
-type f
-execdir sed -i "s,/etc/,/tmp/edev/etc/,g" '{}' +
1391 update-exim4.conf
-d /tmp
/edev
/etc
/exim4
-o /tmp
/edev
/e.conf
1395 # show important information about incoming mail in the exim log
1397 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' <${1:-/var/log/exim4/mainlog}
1400 # 2nd line is message-id:
1402 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog}
1405 tail -F /var
/log
/exim
4/mainlog |
sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p'
1412 # find array. make an array of file names found by find into $x
1413 # argument: find arguments
1414 # return: find results in an array $x
1415 while read -rd ''; do
1417 done < <(find "$@" -print0);
1420 faf
() { # find all files. use -L to follow symlinks
1421 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1422 -o -name .hg
-prune -o -name .editor-backups
-prune \
1423 -o -name .undo-tree-history
-prune \
) -type f
2>/dev
/null
1426 # usage ffconcat FILES_TO_CONCAT OUTPUT_FILE
1430 printf "file '%s'\n" "$1" >$tmpf
1431 while (( $# > 1 )); do
1433 printf "file '%s'\n" "$1" >>$tmpf
1435 # https://trac.ffmpeg.org/wiki/Concatenate
1436 ffmpeg
-f concat
-safe 0 -i $tmpf -c copy
"$1"
1441 if (( $# == 0 )); then
1442 echo ffremux error expected args
>&2
1447 tmpf
=$tmpd/"${f##*/}"
1448 ffmpeg
-i "$f" -c:v copy
-c:a copy
$tmpf
1456 # absolute path of file/dir without resolving symlinks.
1458 # Most of the time, I want this where I would normally use readlink.
1459 # This is what realpath -s does in most cases, but sometimes it
1460 # actually resolves symlinks, at least when they are in /.
1462 # Note, if run on a dir, if the final component is relative, it won't
1463 # resolve that. Use the below fpd for that.
1465 # note: we could make a variation of this which
1466 # assigns to a variable name using eval, so that we don't have to do
1467 # x=$(fp somepath), which might save subshell overhead and look nice,
1468 # but I'm not going to bother.
1470 local initial_oldpwd initial_pwd dir base
1471 initial_oldpwd
="$OLDPWD"
1473 if [[ $1 == */* ]]; then
1476 # CDPATH because having it set will cause cd to possibly print output
1478 printf "%s%s\n" "$PWD" "$base"
1479 CDPATH
='' cd "$initial_pwd"
1480 OLDPWD
="$initial_oldpwd"
1482 printf "%s/%s\n" "$PWD" "$1"
1485 # full path of directory without resolving symlinks
1487 local initial_oldpwd initial_pwd dir
1488 initial_oldpwd
="$OLDPWD"
1492 printf "%s%s\n" "$PWD" "$base"
1494 OLDPWD
="$initial_oldpwd"
1501 sudo mailq |gr frozen|
awk '{print $3}' |
while read -r id
; do
1507 echo -e '\n\n##############################\n'
1508 done |
tee -a /tmp
/frozen
1512 while read -r line
; do
1513 printf '%s\n' "$line"
1514 ids
+=("$(printf '%s\n' "$line" |gr frozen|awk '{print $3}')")
1516 echo "sleeping for 2 in case you change your mind"
1518 sudo exim
-Mrm "${ids[@]}"
1522 # like -e for functions. returns on error.
1523 # at the end of the function, disable with:
1525 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
1526 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
1532 local help="Usage: getdir [--help] PATH
1533 Output the directory of PATH, or just PATH if it is a directory."
1534 if [[ $1 == --help ]]; then
1538 if [[ $# -ne 1 ]]; then
1539 echo "getdir error: expected 1 argument, got $#"
1542 if [[ -d $1 ]]; then
1546 dir
="$(dirname "$1")"
1547 if [[ -d $dir ]]; then
1550 echo "getdir error: directory does not exist"
1556 git_empty_branch
() { # start an empty git branch. carefull, it deletes untracked files.
1557 [[ $# == 1 ]] ||
{ echo 'need a branch name!'; return 1;}
1559 root
=$
(gitroot
) ||
return 1 # function to set gitroot
1561 git symbolic-ref HEAD refs
/heads
/$1
1566 # shellcheck disable=SC2120
1568 local help="Usage: gitroot [--help]
1569 Print the full path to the root of the current git repo
1571 Handles being within a .git directory, unlike git rev-parse --show-toplevel,
1572 and works in older versions of git which did not have that."
1573 if [[ $1 == --help ]]; then
1578 p
=$
(git rev-parse
--git-dir) ||
{ echo "error: not in a git repo" ; return 1; }
1579 [[ $p != /* ]] && p
=$PWD
1585 local args gdb
=false
1587 if [[ $EMACSDIR ]]; then
1588 path-add
"$EMACSDIR/lib-src" "$EMACSDIR/src"
1591 if [[ $DISPLAY ]]; then
1595 if (( $# == 0 )); then
1598 # duplicate -c, but oh well
1599 if ! pgrep
-u $EUID emacsclient
; then
1600 if (( $# == 0 )) && type -p gdb
&>/dev
/null
; then
1606 if [[ $EMACSDIR ]]; then
1608 # todo: we don't have to alter HOME since emacs 29+, we can set
1609 # user-emacs-directory with the flag --init-directory
1611 # Alter the path here, otherwise the nfs mount gets triggered on the
1612 # first path lookup when emacs is not being used.
1613 # shellcheck disable=SC2098 disable=SC2097 # false positive
1614 PATH
="$EMACSDIR/lib-src:$EMACSDIR/src:$PATH" EHOME
=$HOME HOME
=$EMACSDIR m emacsclient
-a "" $args "$@"
1617 # due to a bug, we cant debug from the start unless we get a new gdb
1618 # https://sourceware.org/bugzilla/show_bug.cgi?id=24454
1619 # m gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacs --daemon
1620 m emacsclient
-a "" $args "$@"
1622 cd "/a/opt/emacs-$(distro-name)$(distro-num)"
1623 s gdb
-p "$(pgrep -f 'emacs --daemon')" -ex c
1626 m emacsclient
-a "" $args "$@"
1631 # g pipe. like: cmd | emacs. save cmd output to tmp file, then edit.
1637 #like cmd &> tempfile; emacs tempfile
1639 # note: a useful workflow for doing mass replace on my files:
1641 ## remove any false positives, or manually edit them. rename files if needed.
1642 # sedi 's/REGEX/REPLACEMENT/' $(gr '^/' /a/tmp/gtmp)
1647 # g command substitution.
1649 # shellcheck disable=SC2046 # i want word splitting for this hackery
1653 # force terminal version
1659 # quit will prompt if the program crashes.
1660 gdb
-ex=r
-ex=quit
--args emacs
"$@"; r
;
1664 # kill the emacs daemon
1669 grep -iIP --color=auto
"$@" ||
return $?
1671 grr
() { # grep recursive
1672 # Don't return 1 on nonmatch because this is meant to be
1673 # interactive, not in a conditional.
1674 if [[ ${#@} == 1 ]]; then
1675 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto
"$@" . ||
[[ $?
== 1 ]]
1677 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto
"$@" ||
[[ $?
== 1 ]]
1685 # recursive everything. search for files/dirs and lines. rs = easy chars to press
1689 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1690 -o -name .hg
-prune -o -name .editor-backups
-prune \
1691 -o -name .undo-tree-history
-prune \
) 2>/dev
/null |
grep -iP --color=auto
"$query"
1695 # horizontal row. used to break up output
1698 # 180 is long enough.
1699 blocks
=██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
1700 printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)${blocks:0:${COLUMNS:-180}}$(tput sgr0 2>/dev/null||:)"
1704 local col input_len
=0
1706 input_len
=$
((input_len
+ 1 + ${#arg}))
1708 col=$
((60 - input_len
))
1709 printf "\e[1;97;41m%s" "$*"
1710 if (( col > 0 )); then
1711 # shellcheck disable=SC2046 # needed to work as intended. a better way would be like hr above.
1712 printf "\e[1;97;41m \e[0m%.0s" $
(eval echo "{1..${col}}")
1716 hlm
() { hl
"$*"; "$@"; }
1718 hrcat
() { local f
; for f
; do [[ -f $f ]] ||
continue; hr
; echo "$f"; cat "$f"; done }
1721 # github-release-dl restic/restic restic_ _linux_amd64.bz2
1723 # https://github.com/restic/restic/releases/download/v0.16.3/restic_0.16.3_linux_amd64.bz2
1724 github-release-dl
() {
1725 local github_path file_prefix file_suffix latest_prefix version redir_path
1729 if (( $# != 3 )); then
1730 echo "$0: error, expected 3 arguments" >&2
1733 redir_path
="https://github.com/$github_path/releases/latest/download/"
1734 latest_prefix
=$
(curl
-s -I "$redir_path" |
awk 'tolower($1) == "location:" {print $2}')
1735 # it has a trailing /r at the end. just kill any whitespace.
1736 latest_prefix
="${latest_prefix//[$'\t\r\n ']}"
1737 if [[ ! $latest_prefix ]]; then
1738 echo "failed to find latest path. Tried to find case insensitive 'location:' in the curl output:"
1739 m curl
-s -I "$redir_path"
1742 version
="${latest_prefix##*/}"
1743 version
="${version#v}"
1744 m wget
-- "$latest_prefix/$file_prefix$version$file_suffix"
1748 # go-github-install restic/restic restic_ _linux_amd64.bz2
1749 # go-github-install restic/rest-server rest-server_ _linux_amd64.tar.gz
1751 # common pattern among go binaries on github
1752 go-github-install
() {
1753 local tmpd targetf tmp files src
1758 tmp
="${file_prefix##*[[:alnum:]]}"
1759 targetf
="${file_prefix%"$tmp"}"
1760 echo targetf
: $targetf
1761 github-release-dl
"$@"
1763 case $file_suffix in
1771 rm -f -- "${files[@]}"
1773 # Here we detect and handle 2 cases: either we extracted a single
1774 # binary which we have to rename or a folder with a binary named
1775 # $targetf in it which is all we care about.
1776 if (( ${#files[@]} == 1 )) && [[ -f ${files[0]} ]]; then
1778 mv -- .
/* /usr
/local
/bin
/$targetf
1780 files
=(.
/*/$targetf)
1781 if [[ -f $targetf ]]; then
1783 elif [[ -f ${files[0]} ]]; then
1787 mv -- "$src" /usr
/local
/bin
1793 ## 2024: I'm using gh instead of hub, but leaving this just in case.
1794 ## I tried the github cli tool (gh) and it seems easier than
1797 ## hub predated github's 2020 official cli tool gh.
1799 ## https://raw.githubusercontent.com/cli/cli/trunk/docs/gh-vs-hub.md
1800 # get latest hub and run it
1801 # main command to use:
1802 # hub pull-request --no-edit
1803 # --no-edit means to use the first commit\'s message as the pull request message.
1804 # If that fails, try doing
1805 # hub pull-request --no-edit -b UPSTREAM_OWNER:branch
1806 # where branch is usually master. it does the pr against your current branch.
1808 # On first use, you input username/pass and it gets an oath token so you dont have to repeat
1809 # it\'s at ~/.config/hub
1811 local up uptar updir p re
1812 # example https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz
1813 up
=$
(wget
-q -O- https
://api.github.com
/repos
/github
/hub
/releases
/latest | jq
-r .assets
[].browser_download_url |
grep linux-amd64
)
1815 if [[ ! $up ||
$up =~
$re ]]; then
1816 echo "failed to get good update url. got: $up"
1820 if [[ ! -e /a
/opt
/$updir ]]; then
1821 rm -rf /a
/opt
/hub-linux-amd64
*
1823 tar -C /a
/opt
-zxf /a
/opt
/$uptar
1826 if ! which hub
&>/dev
/null
; then
1827 sudo
/a
/opt
/$updir/install
1830 # save token across computers
1831 if [[ ! -L ~
/.config
/hub
]]; then
1832 if [[ -e ~
/.config
/hub
]]; then
1833 mv ~
/.config
/hub
/p
/c
/subdir_files
/.config
/
1835 if [[ -e /p
/c
/subdir_files
/.config
/hub
]]; then
1849 # cvs update -C FILE
1854 # potentially useful command translation
1855 # https://fling.seas.upenn.edu/~giesen/dynamic/wordpress/equivalent-commands-for-git-svn-and-cvs/
1857 # importing cvs repo into git using git-cvs package:
1858 # /f/www $ /usr/lib/git-core/git-cvsimport -C /f/www-git
1874 find -L "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1875 -o -name .hg
-prune -o -name .editor-backups
-prune \
1876 -o -name .undo-tree-history
-prune \
) -iname "*$glob*" 2>/dev
/null
1880 # insensitive find here. args are combined into the search string.
1881 # -L = follow symlinks
1882 find -L .
-not \
( -name .svn
-prune -o -name .git
-prune \
1883 -o -name .hg
-prune -o -name .editor-backups
-prune \
1884 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1888 # insensitive find directory
1889 find -L .
-type d
-not \
( -name .svn
-prune -o -name .git
-prune \
1890 -o -name .hg
-prune -o -name .editor-backups
-prune \
1891 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1896 sudo iptables
-A INPUT
-s $1 -j DROP
1901 grep -Il "" "$@" &>/dev
/null
1908 # journalctl with times in the format the --since= and --until= options accept
1909 jrt
() { journalctl
-e -n100000 -o short-full
"$@"; }
1910 jr
() { journalctl
-e -n100000 "$@" ; }
1911 jrf
() { journalctl
-n1000 -f "$@" ; }
1913 # the invocation id is "assigned each time the unit changes from an inactive
1914 # state into an activating or active state" man systemd.exec
1915 journalctl
-e --no-tail -u exim4 _SYSTEMD_INVOCATION_ID
="$(systemctl show -p InvocationID --value $1)"
1917 ccomp journalctl jr jrf jru
1922 if [[ $PWD == /[iap
] ]]; then
1923 command ls -A --color=auto
-I lost
+found
"$@"
1925 command ls -A --color=auto
"$@"
1929 lcn
() { locate -i "*$**"; }
1931 lg
() { LC_COLLATE
=C.UTF-8 ll
--group-directories-first "$@"; }
1933 lt
() { ll
-tr "$@"; }
1935 lld
() { ll
-d "$@"; }
1937 ccomp
ls l lg lt lld ll
1943 for dirs in false true
; do
1945 if [[ -d $f ]]; then
1947 # reverse the order to rename the nested dirs first.
1948 # note: 0 element is the dir itself
1949 for ((i
=${#all[@]}-1; i
>=1; i--
)); do
1951 if $dirs && [[ -d $a ]]; then
1952 # e dirs low "$a" # debug
1954 elif ! $dirs && [[ ! -d $a && -e $a ]]; then
1956 # e not dirs low "$a" # debug
1961 # just rename all the top level args on the second pass
1963 # e final dirs low "$f" # debug
1970 low
() { # make filenames lowercase, remove bad chars
1973 arg
="${arg%%+(/)}" # remove trailing slashes. assumes we have extglob on.
1975 if (( ${#dir} == ${#arg} )); then
1979 new
="${f,,}" # downcase
1980 # shellcheck disable=SC2031 # seems like a shellcheck bug
1981 new
="${new//[^a-zA-Z0-9._-]/_}" # sub bad chars
1982 new
="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
1983 new
="${new%"${new##*[[:alnum:]]}"}"
1984 # remove bad underscores, like __ and _._
1985 new
=$
(echo $new |
sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
1986 safe_rename
"$dir/$f" "$dir/$new" ||
return 1
1991 lower
() { # make first letter of filenames lowercase.
1994 if [[ ${x::1} == [A-Z
] ]]; then
1995 y
=$
(tr '[:upper:]' '[:lower:]' <<<"${x::1}")"${x:1}"
1996 safe_rename
"$x" "$y" ||
return 1
2002 k
() { # history search
2003 grep -iP --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
tail -n 80 ||
[[ $?
== 1 ]];
2005 ks
() { # history search with context
2006 # args are an extended regex used by sed
2007 history |
sed -nr "h;s/^\s*(\S+\s+){4}//;/$*/{g;p}" |
tail -n 80 ||
[[ $?
== 1 ]];
2009 ksu
() { # history search unique
2010 grep -P --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
uniq ||
[[ $?
== 1 ]];
2013 # todo: id like to do maybe a daily or hourly cronjob to
2014 # check that my history file size is increasing. Ive had it
2015 # inexplicably truncated in the past.
2018 HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/'
2019 read -r -p "press anything but contrl-c to delete"
2020 for entry
in $
(HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/ { print a }' |
tac); do
2026 # history without the date
2028 history "$@" | cut
-d' ' -f 7-
2031 ccomp
grep k ks ksu histrm
2035 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
2036 make -qp |
awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
2048 # mkdir the last arg, cp the rest into it
2051 cp "${@:1:$#-1}" "${@: -1}"
2055 mv "${@:1:$#-1}" "${@: -1}"
2058 mkt
() { # mkdir and touch file
2060 mkdir
-p "$(dirname "$path")"
2064 # shellcheck disable=SC2032
2065 mkdir
() { command mkdir
-p "$@"; }
2068 # https://github.com/HenriWahl/Nagstamon/issues/357
2069 if ! pgrep
-f /usr
/bin
/dunst
>/dev
/null
; then
2072 /usr
/bin
/nagstamon
&
2077 screen
-RD -S profanity
2080 # i dont want to wait for konsole to exit...
2082 command prof
&>/dev
/null
&
2087 printf '\033[1A\033[K'; printf "%s\n" "$l"| ts
"%F %T" |
tee -a /p
/self-chat.log
2092 # cant use s because sudo -i doesnt work for passwordless sudo command
2095 sudo nmtui-connect
"$@"
2105 if shopt nullglob
>/dev
/null
; then
2119 # shellcheck disable=SC2024
2121 for f
in /var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
; do
2123 if [[ -s $f ]]; then
2124 echo ================== $f =============
2125 s
tee -a /var
/log
/exim
4/$base-archive <$f
2133 ping() { command ping -O "$@"; }
2134 p8
() { ping "$@" 8.8.8.8; }
2135 p6
() { ping6
"$@" 2001:4860:4860::8888; }
2137 pkx
() { # package extract
2138 local pkg cached tmp f
2141 # shellcheck disable=SC2012
2142 cached
=$
(ls -t /var
/cache
/apt
/archives
/${pkg}_
* |
tail -n1 2>/dev
/null
) ||
:
2143 if [[ $cached ]]; then
2146 m aptitude download
$pkg ||
return 1
2148 tmp
=(*); f
=${tmp[0]} # only 1 expected
2157 tmpf
=$
(pgrep
-f "$*")
2158 mapfile
-t pids
<<<"$tmpf"
2161 # shellcheck disable=SC2128
2167 0) echo "no pid found" ;;
2176 help="Usage: psg [--help] GREP_ARGS
2177 grep ps and output in a nice format"
2178 if [[ $1 == --help ]]; then
2183 # final grep is because some commands tend to have a lot of trailing spaces
2184 y
=$
(echo "$x" |
grep -iP "$@" |
grep -o '.*[^ ]') ||
:
2186 echo "$x" |
head -n 1 ||
[[ $?
== 141 ]]
2191 pubip
() { curl
-4s https
://icanhazip.com
; }
2192 pubip6
() { curl
-6s https
://icanhazip.com
; }
2193 whatismyip
() { pubip
; }
2196 q
() { # start / launch a program in the backround and redir output to null
2200 # shellcheck disable=SC2120
2202 if [[ $HISTFILE ]]; then
2203 history -a # save history
2205 trap ERR
# this avoids a segfault
2207 # i had this redir, not sure why
2208 # exit "$@" 2>/dev/null
2211 # scp is insecure and deprecated.
2213 rsync
-Pt --inplace "$@"
2218 # available high ports are 1024-65535,
2219 # but lets skip things that are more likely to be in use
2222 print(secrets.SystemRandom().randrange(10002,65500))
2228 # shellcheck disable=SC1090 # expected to not follow
2238 # rsync, root is required to keep permissions right.
2239 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
2240 # --no-times --delete
2241 # basically, make an exact copy, use checksums instead of file times to be more accurate
2242 rsync
-ahvic --delete "$@"
2245 # like rlu, but dont delete files on the target end which
2246 # do not exist on the original end.
2250 # rl without preserving modification time.
2251 rsync
-ahvic --delete --no-t "$@"
2253 # [RSYNC_OPTS] HOST PATH
2255 # eg. rsu -opts frodo /testpath
2256 # relative paths will expanded with readlink -f.
2257 opts
=("${@:1:$#-2}") # 1 to last -2
2258 path
="${*:$#}" # last
2259 host="${*:$#-1:1}" # last -1
2260 if [[ $path == .
* ]]; then
2261 path
=$
(readlink
-f $path)
2263 m rsync
-ahvi --relative --no-implied-dirs "${opts[@]}" "$path" "root@$host:/";
2265 ccomp rsync rsd rsa rst rsu
2267 # find programs listening on a port
2270 # to figure out these args, i had to look at the man page from git version, as of 2022-04.
2271 s ss
-lpn state listening sport
= $port
2276 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2281 hr
; s ss
-lpn sport
= 53
2282 if systemctl is-enabled dnsmasq
&>/dev
/null ||
[[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2283 # this will fail is dnsmasq is failed
2284 hr
; m ser status dnsmasq |
cat ||
:
2286 hr
; echo $f:; ccat
$f
2287 hr
; m grr
'^ *(servers-file|server) *=|^ *no-resolv *$' /etc
/dnsmasq.conf
/etc
/dnsmasq.d
2288 f
=/etc
/dnsmasq-servers.conf
2289 hr
; echo $f:; ccat
$f
2292 echo /etc
/nsswitch.conf
:
2293 grep '^ *hosts:' /etc
/nsswitch.conf
2294 if systemctl is-enabled systemd-resolved
&>/dev
/null ||
[[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2295 hr
; m ser status systemd-resolved |
cat ||
:
2296 hr
; m resolvectl status |
cat
2304 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2308 m sudo nscd
-i hosts
2310 if [[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2311 m sudo systemctl restart dnsmasq
2313 if [[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2314 m sudo systemctl restart systemd-resolved
2316 if type -P resolvectl
&>/dev
/null
; then
2317 resolvectl flush-caches
2321 # add annoyingly long argument which should be the default
2323 sed -i --follow-symlinks "$@"
2328 # todo: test variable assignment with newlines here.
2329 # https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
2331 # beware that it only works on the assumption that any special
2332 # characters in the input string are intended to be escaped, not to work
2333 # as special chacters.
2335 LC_ALL
=C
sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
2339 ssh fencepost
head -n 300 /gd
/gnuorg
/EventAndTravelInfo
/rms-current-trips.txt |
less
2348 command sudo
"$@" ||
return $?
2353 # I use a function because otherwise we cant use in a script,
2354 # cant assign to variable.
2356 # note: gksudo is recommended for X apps because it does not set the
2357 # home directory to the same, and thus apps writing to ~ fuck things up
2358 # with root owned files.
2360 if [[ $EUID != 0 ||
$1 == -* ]]; then
2361 # shellcheck disable=SC2034
2362 SUDOD
="$PWD" command sudo
-i "$@"
2368 sb
() { # sudo bash -c
2369 # use sb instead of s is for sudo redirections,
2370 # eg. sb 'echo "ok fine" > /etc/file'
2371 # shellcheck disable=SC2034
2373 sudo
-i bash
-c "$@"
2376 se
() { s urun
0077 "$@"; }
2379 safe_rename
() { # warn and dont rename if file exists.
2380 # mv -n exists, but it\'s silent
2381 if [[ $# != 2 ]]; then
2382 echo safe_rename error
: $# args
, need
2 >&2
2385 if [[ $1 != "$2" ]]; then # yes, we want to silently ignore this
2386 if [[ -e $2 ||
-L $2 ]]; then
2387 echo "Cannot rename $1 to $2 as it already exists."
2396 sudo
dd status
=none of
="$1"
2400 if type -p systemctl
&>/dev
/null
; then
2403 if (( $# >= 3 )); then
2404 echo iank
: ser expected
2 or
less arguments
2411 systemctl
-n 40 status
"$@"
2414 seru
() { systemctl
--user "$@"; }
2415 # like restart, but do nothing if its not already started
2418 if [[ $
(s systemctl
--no-pager show
-p ActiveState
$service ) == ActiveState
=active
]]; then
2419 systemctl restart
$service
2423 setini
() { # set a value in a .ini style file
2424 key
="$1" value
="$2" section
="$3" file="$4"
2425 if [[ -s $file ]]; then
2426 sed -ri -f - "$file" <<EOF
2427 # remove existing keys
2428 / *\[$section\]/,/^ *\[[^]]+\]/{/^\s*${key}[[:space:]=]/d}
2430 /^\s*\[$section\]/a $key=$value
2431 # from section to eof, do nothing
2432 /^\s*\[$section\]/,\$b
2433 # on the last line, if we haven't found section yet, add section and key
2445 sgo
() { # service go
2447 ser restart
$service ||
return 1
2448 if type -p systemctl
&>/dev
/null
; then
2454 # ignore services that dont exist
2455 if systemctl
cat $service &>/dev
/null
; then
2457 ser disable
$service
2463 systemctl list-unit-files | rg
"$@"
2468 # see https://savannah.gnu.org/maintenance/fsf/bash-style-guide/ for justifications
2469 local quotes others ret
2470 quotes
=2048,2068,2086,2206,2254
2471 others
=2029,2032,2033,2054,2164,
2472 shellcheck
-W 999 -x -e $quotes,$others "$@" || ret
=$?
2473 if (( ret
>= 1 )); then
2474 echo "A template comment to disable is now in clipboard. eg: # shellcheck disable=SC2206 # reason"
2475 cbs
"# shellcheck disable=SC"
2479 # sk with quotes. For checking scripts that we expect to take untrusted
2480 # input in order to verify we quoted vars.
2483 others
=2029,2033,2054,2164
2484 shellcheck
-W 999 -x -e $others "$@" ||
return $?
2489 for f
in $
(i s |
awk '$1 == "modified:" {print $2}'); do
2490 if istext
"$f" && [[ $
(head -n1 "$f" 2>/dev
/null
) == '#!/bin/bash'* ]]; then
2496 # sl: ssh, but firsh rsync our bashrc and related files to a special
2497 # directory on the remote host if needed.
2499 # Some environment variables and files need to be setup for this to work
2500 # (mine are set at the beginning of this file)
2502 # SL_FILES_DIR: Environment variable. Path to folder which should at
2503 # least have a .bashrc file or symlink. This dir will be rsynced to ~ on
2504 # remote hosts (top level symlinks are resolved) unless the host already
2505 # has a $SL_FILES_DIR/.bashrc. In that case, we assume it is a host you
2506 # control and sync files to separately and already has the ~/.bashrc you
2507 # want. The remote bash will also take its .inputrc config from this
2508 # folder (default of not existing is fine). Mine looks like this:
2509 # https://iankelling.org/git/?p=distro-setup;a=tree;f=sl/.iank
2511 # SL_INFO_DIR: Environment variable. This folder stores info about what
2512 # we detected on the remote system and when we last synced. It will be created
2513 # if it does not exist. Sometimes you may want to forget about a
2514 # remote system, you can use sl --rsync, or the function for that slr
2517 # SL_TEST_CMD: Env var. Meant to be used to vary the files synced
2518 # depending on the remote host. Run this string on the remote host the
2519 # first time sl is run (or if we run slr). The result is passed to
2520 # SL_TEST_HOOK. For example,
2521 # export SL_TEST_CMD=". /etc/os-release ; echo \${VERSION//[^a-zA-Z0-9]/}"
2523 # SL_TEST_HOOK: Env var. It is run as $SL_TEST_HOOK. This can set
2524 # $SL_FILES_DIR to vary the files synced.
2526 # SL_RSYNC_ARGS: Env var. String of arguments passed to rsync. For
2527 # example to exclude files within a directory. Note, excluded
2528 # files wont be deleted on rsync, you can add --delete-excluded
2529 # to the rsync command if that is desired.
2531 # SL_SSH_ARGS: Env var. Default arguments passed to ssh.
2533 # For when ~/.bashrc is already customized on the remote server, you
2534 # might find it problematic that ~/.bashrc is sourced for ALL ssh
2535 # commands, even in scripts. This paragraph is all about that. bash
2536 # scripts dont source ~/.bashrc, but call ssh in scripts and you get
2537 # ~/.bashrc. You dont want this. .bashrc is meant for interactive shells
2538 # and if you customize it, probably has bugs from time to time. This is
2539 # bad. Here's how I fix it. I have a special condition to "return" in my
2540 # .bashrc for noninteractive ssh shells (copy that code). Then use this
2541 # function or similar that passes LC_USEBASHRC=t when sshing and I want
2542 # my bashrc. Also, I don't keep most of my bashrc in .bashrc, i source a
2543 # separate file because even if I return early on, the whole file gets
2544 # parsed which can fail if there is a syntax error.
2546 # Background on LC_USEBASHRC var (no need to read if you just want to
2547 # use this function): env variables sent across ssh are strictly
2548 # limited, but we get LC_* at least in debian based machines, so we
2549 # just make that * be something no normal program would use. Note, on
2550 # hosts that dont allow LC_* I start an inner shell with LC_USEBASHRC
2551 # set, and the inner shell also allows running a nondefault
2552 # .bashrc. This means the outer shell still ran the default .bashrc,
2553 # but that is the best we can do.
2555 local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2
type info_sec force_rsync \
2556 sync_dirname testcmd extra_info testbool files_sec sl_test_cmd sl_test_hook
2557 declare -a args tmpa
2561 # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
2562 # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address]
2563 # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option]
2564 # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname
2567 # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
2568 # [-D [bind_address:]port] [-E log_file] [-e escape_char]
2569 # [-F configfile] [-I pkcs11] [-i identity_file]
2570 # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]
2571 # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
2572 # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
2575 if [[ $1 == --rsync ]]; then
2579 # shellcheck disable=SC2153 # intentional
2580 sl_test_cmd
=$SL_TEST_CMD
2581 # shellcheck disable=SC2153 # intentional
2582 sl_test_hook
=$SL_TEST_HOOK
2583 # shellcheck disable=SC2153 # intentional
2584 sl_rsync_args
=$SL_RSYNC_ARGS
2611 # note we dont support things like -4oOption
2612 -[46AaCfGgKkMNnqsTtVvXxYy
]*)
2615 -[bcDEeFIiJLlmOopQRSWw
]*)
2616 # -oOption etc is valid
2617 if (( ${#1} >= 3 )); then
2620 args
+=("$1" "$2"); shift 2
2629 if [[ ! $remote ]]; then
2630 echo $0: error hostname required
>&2
2635 if [[ ! $SL_INFO_DIR ]]; then
2636 echo 'error: missing SL_INFO_DIR env var' >&2
2642 tmpa
=($SL_INFO_DIR/???????????
"$remote")
2644 if [[ -e $sshinfo ]]; then
2645 if $force_rsync; then
2652 tmp
=${sshinfo[0]##*/}
2655 extra_info
=$
(cat $sshinfo)
2657 # we test for string to know ssh succeeded
2658 testbool
="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
2659 testcmd
="if $testbool; then printf y; else printf n; fi"
2660 if ! tmp
=$
(LC_USEBASHRC
=y
command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
2661 echo failed sl
test. doing plain
ssh -v
2662 command ssh -v "${args[@]}" "$remote"
2664 if [[ $tmp == y
* ]]; then
2670 extra_info
="${tmp:1}"
2672 if [[ $sl_test_hook ]]; then
2673 RSYNC_RSH
="ssh ${args[*]}" $sl_test_hook "$extra_info" "$remote"
2676 if $haveinfo && [[ $type == b
]]; then
2678 read -r files_sec _
< <(find -L $SL_FILES_DIR -printf "%T@ %p\n" |
sort -nr ||
[[ $?
== 141 ||
${PIPESTATUS[0]} == 32 ]] )
2679 files_sec
=${files_sec%%.*}
2680 if (( files_sec
> info_sec
)); then
2686 sync_dirname
=${SL_FILES_DIR##*/}
2688 if [[ ! $SL_FILES_DIR ]]; then
2689 echo 'error: missing SL_FILES_DIR env var' >&2
2694 RSYNC_RSH
="ssh ${args[*]}" m rsync
-rptL --delete $sl_rsync_args $SL_FILES_DIR "$remote":
2696 if $dorsync ||
! $haveinfo; then
2697 sshinfo
=$SL_INFO_DIR/$EPOCHSECONDS$type"$remote"
2698 [[ -e $SL_INFO_DIR ]] || mkdir
-p $SL_INFO_DIR
2699 printf "%s\n" "$extra_info" >$sshinfo
2702 if [[ $type == b
]]; then
2703 if (( ${#@} )); then
2704 # Theres a couple ways to pass arguments, im not sure whats best,
2705 # but relying on bash 4.4+ escape quoting seems most reliable.
2706 command ssh "${args[@]}" "$remote" \
2707 LC_USEBASHRC
=t bash
-c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash
${@@Q}
2708 elif [[ ! -t 0 ]]; then
2709 # This case is when commands are being piped to ssh.
2710 # Normally, no bashrc gets sourced.
2711 # But, since we are doing all this, lets source it because we can.
2712 cat <(echo .
$sync_dirname/.bashrc
) - |
command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2714 command ssh -t "${args[@]}" "$remote" LC_USEBASHRC
=t INPUTRC
=$sync_dirname/.inputrc bash
--rcfile $sync_dirname/.bashrc
2718 LC_USEBASHRC
=t
command ssh "${args[@]}" "$remote" ${@@Q}
2720 command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2723 # this function inspired from https://github.com/Russell91/sshrc
2730 sl
-oControlMaster=no
-oControlPath=/ "$@"
2732 # kill off old shared socket then ssh
2734 m
ssh -O exit "$@" ||
[[ $?
== 255 ]]
2737 ccomp
ssh sl slr sss ssk
2740 if [[ $TERM == alacritty ||
$TERM == xterm-kitty
]]; then
2741 TERM
=xterm-256color LC_USEBASHRC
=t
command ssh "$@"
2743 LC_USEBASHRC
=t
command ssh "$@"
2749 # log with script. timing is $1.t and script is $1.s
2750 # -l to save to ~/typescripts/
2751 # -t to add a timestamp to the filenames
2752 local logdir do_stamp arg_base
2753 (( $# >= 1 )) ||
{ echo "arguments wrong"; return 1; }
2756 while getopts "lt" option
2759 l
) arg_base
=$logdir ;;
2762 echo error
: bad option
2767 shift $
((OPTIND
- 1))
2769 [[ -e $logdir ]] || mkdir
-p $logdir
2770 $do_stamp && arg_base
+=$
(date +%F.
%T
%z
)
2771 script -t $arg_base.s
2> $arg_base.t
2773 splay
() { # script replay
2774 #logRoot="$HOME/typescripts/"
2775 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
2776 scriptreplay
"$1.t" "$1.s"
2780 # sudo redo. be aware, this command may not work right on strange distros or earlier software
2781 if [[ $# == 0 ]]; then
2782 sudo
-E bash
-c -l "$(history -p '!!')"
2784 echo this
command redos last
history item. no argument is accepted
2789 # with -ll, less secure but faster.
2790 command srm
-ll "$@"
2795 ssh $1 "/tmp/${2##*/}" "$(printf "%q
\n" "${@:2}")"
2807 tclock
() { # terminal clock
2812 # this goes to full width
2813 #len=${1:-$((COLUMNS -7))}
2816 if (( x
== len
)); then
2818 d
="$(date +%l:%_M) "
2821 d
=$
(date +%l
:%M
:%_S
)
2825 for ((i
=0; i
<x
; i
++)); do
2826 if (( i
% 6 )); then
2844 # test existence / exists
2847 [[ -e "$x" ||
-L "$x" ]] || ret
=1
2853 # normally, i would just execute these commands in the function.
2854 # however, DEBUG is not inherited, so we need to run it outside a function.
2855 # And we want to run set -x afterwards to avoid spam, so we cram everything
2856 # in here, and then it will run after this function is done.
2857 # # set as array to satisfy shellcheck, but it is equivalent to setting it as non-array
2858 PROMPT_COMMAND
=('trap DEBUG; unset PROMPT_COMMAND; PS1="\w \$ "')
2861 PROMPT_COMMAND
=(prompt-command
)
2862 if [[ $TERM == *(screen
*|xterm
*|rxvt
*) ]]; then
2863 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
2867 # prometheus node curl
2870 host=${1:-127.0.0.1}
2871 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
2874 tx
() { # toggle set -x, and the prompt so it doesnt spam
2875 if [[ $
- == *x
* ]]; then
2884 # show all processes in the network namespace $1.
2885 # blank entries appear to be subprocesses/threads
2889 sudo
find -L /proc
/[1-9]*/task
/*/ns
/net
-samefile /run
/netns
/$netns | cut
-d/ -f5 | \
2891 x
=$
(ps
-w --no-headers -p $l);
2892 if [[ $x ]]; then echo "$x"; else echo $l; fi;
2896 if ! s ip netns list |
grep -Fx nonet
&>/dev
/null
; then
2897 s ip netns add nonet
2899 sudo
-E env
/sbin
/ip netns
exec nonet sudo
-E -u iank
/bin
/bash
2902 m
() { printf "%s\n" "$*"; "$@"; }
2904 # update file. note: duplicated in mail-setup.
2905 # updates $ur u result to true or false
2906 # updates $reload to true if file updated is in /etc/systemd/system
2908 local tmp tmpdir dest
="$1"
2909 local base
="${dest##*/}"
2910 local dir
="${dest%/*}"
2911 if [[ $dir != "$base" ]]; then
2912 # dest has a directory component
2915 # shellcheck disable=SC2034 # see comment at top of function
2917 tmpdir
="$(mktemp -d)"
2918 cat >$tmpdir/"$base"
2919 tmp
=$
(rsync
-ic $tmpdir/"$base" "$dest")
2921 printf "%s\n" "$tmp"
2922 # shellcheck disable=SC2034 # see comment at top of function
2924 if [[ $dest == /etc
/systemd
/system
/* ]]; then
2925 # shellcheck disable=SC2034 # see comment at top of function
2934 if type -p uprecords
&>/dev
/null
; then
2942 for x
in "$@"; do virsh destroy
"$x"; virsh undefine
"$x"; done
2950 sudo virsh dumpxml
$vm |
sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
2951 sed -r "s/listen='[^']+/listen='$ip/"> $t
2952 sudo virsh undefine
$vm
2953 sudo virsh define
$t
2958 vm-set-listen
$1 0.0.0.0
2963 vm-set-listen
$1 127.0.0.1
2968 interfaces
=$
(iw dev |
awk '$1 == "Interface" {print $2}')
2969 for i
in $interfaces; do
2970 echo "myiwscan: considering $i"
2971 # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines.
2972 # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h}
2973 sudo iw dev
$i scan |
sed -rn "
2974 s/^\Wcapability: (.*)/\1/;Ta;h;b
2975 :a;s/^\Wsignal: -([^.]+).*/\1/;Tb;H;b
2976 # padded to min width of 20
2977 :b;s/\WSSID: (.*)/\1 /;T;s/^(.{20}(.*[^ ])?) */\1/;H;g;s/(.*)\n(.*)\n(.*)/\2 \3 \1/gp;b
2982 # Run script by copying it to a temporary location first,
2983 # and changing directory, so we don't have any open
2984 # directories or files that could cause problems when
3001 # spark 1 5 22 13 53
3005 # Copyright (c) Zach Holman, https://zachholman.com
3006 # https://github.com/holman/spark
3008 # As of 2022-10-28, I reviewed github forks that had several newer
3009 # commits, none had anything interesting. I did a little refactoring
3010 # mostly to fix emacs indent bug.
3012 # Generates sparklines.
3015 if [ "X$1" = "X-n" ]; then
3029 # find min/max values
3030 local min
=0xffffffff max
=0
3034 # on Linux (or with bash4) we could use `printf %.0f $n` here to
3035 # round the number but that doesn't work on OS X (bash3) nor does
3036 # `awk '{printf "%.0f",$1}' <<< $n` work, so just cut it off
3038 (( n
< min
)) && min
=$n
3039 (( n
> max
)) && max
=$n
3040 numbers
=$numbers${numbers:+ }$n
3044 local ticks
=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █
)
3046 # use a high tick if data is constant
3047 (( min
== max
)) && ticks
=(▅ ▆
)
3050 f
=$
(( ( (max-min
) <<8)/( tc - 1) ))
3055 _spark_echo -n ${ticks[$(( (((n-min)<<8)/f) ))]}
3060 pdfwc() { local f; for f; do echo "$f" "$(pdfinfo "$f" | awk '/^Pages:/ {print $2}')"; done }
3063 # nvm install script appended this to my .bashrc. I dont want to run it all the time,
3064 # so put it in a function.
3066 export NVM_DIR="$HOME/.nvm"
3067 # shellcheck disable=SC1091 # may not exist, & third party
3068 [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" # This loads nvm
3069 # shellcheck disable=SC1091 # may not exist, & third party
3070 [ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion" # This loads nvm bash_completion
3075 if date -d 'february 29' &>/dev/null; then
3085 if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
3092 # make vim work with my light colortheme terminal.
3094 if [[ -e ~/.vimrc ]]; then
3097 command vim -c ':colorscheme peachpuff' "$@"
3101 # ls count. usage: pass a directory, get the number of files.
3102 # https://unix.stackexchange.com/questions/90106/whats-the-most-resource-efficient-way-to-count-how-many-files-are-in-a-director
3104 # shellcheck disable=SC2790 disable=SC2012 # intentional
3108 # run then notify. close notification after the next prompt.
3111 dunstify -u critical -h string:x-dunst-stack-tag:profanity "$*"
3112 _psrun=(dunstctl close-all)
3115 dunstify -u critical -h string:x-dunst-stack-tag:profanity n
3116 _psrun=(dunstctl close-all)
3122 inotifywait -m "$dir" -e create -e moved_to | while read -r _ _ file; do
3136 if ! type -p sponge &>/dev/null; then
3137 echo "$0: error: missing dependency: sudo apt install moreutils" >&2
3142 echo "adding header to $f"
3143 if [[ -s $f ]]; then
3148 cat - "${f_maybe[@]}" <<EOF | sponge "$f"
3149 The following is the GNU All-permissive License as recommended in
3150 <https://www.gnu.org/licenses/license-recommendations.en.html>
3152 Copyright (C) $(date +%Y) Free Software Foundation <sysadmin@fsf.org>
3154 Copying and distribution of this file, with or without modification,
3155 are permitted in any medium without royalty provided the copyright
3156 notice and this notice are preserved. This file is offered as-is,
3157 without any warranty.
3159 Contributions are welcome. See <https://savannah.gnu.org/maintenance/fsf/>.
3165 # note, there is also the tool gron which is meant for this, but
3166 # this is good enough to not bother installing another tool
3168 # https://stackoverflow.com/questions/59700329/how-to-print-path-and-key-values-of-json-file-using-jq
3169 jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' "$@"
3173 "$@" |& ts || return $?
3180 if $use_color && type -p tput &>/dev/null; then
3181 # this is nice for a dark background terminal:
3182 # https://github.com/trapd00r/LS_COLORS
3183 # I would like if there was something similar for light.
3185 # https://www.bigsoft.co.uk/blog/2008/04/11/configuring-ls_colors
3186 # change the hard to read turqouise.
3187 # defaults dircolors --print-database.
3189 # the default bold green is too light.
3190 # this explains the codes: https://gist.github.com/thomd/7667642
3191 export LS_COLORS="ex=1:ln=00;31"
3193 term_bold="$(tput bold)"
3194 term_red="$(tput setaf 1)"
3195 term_green="$(tput setaf 2)"
3196 # shellcheck disable=SC2034 # expected
3197 term_yellow="$(tput setaf 3)"
3198 term_purple="$(tput setaf 5)"
3199 term_nocolor="$(tput sgr0)" # no font attributes
3201 # unused so far. commented for shellcheck
3202 # term_underl="$(tput smul)"
3203 # term_blue="$(tput setaf 4)"
3204 # term_cyan="$(tput setaf 6)"
3206 # Try to keep environment pollution down, EPA loves us.
3207 unset safe_term match_lhs use_color
3212 if [[ $- == *i* ]]; then
3217 if [[ $EUID == 1000 ]]; then
3224 # this needs to come before next ps1 stuff
3225 # this stuff needs bash 4, feb 2009,
3226 # old enough to no longer condition on $BASH_VERSION anymore
3230 if [[ $- == *i* ]] && [[ ! $LC_INSIDE_EMACS ]]; then
3232 bind -m vi-command B:shell-backward-word
3233 bind -m vi-command W:shell-forward-word
3236 if [[ $SSH_CLIENT || $SUDO_USER ]]; then
3237 unset PROMPT_DIRTRIM
3241 # emacs terminal has problems if this runs slowly,
3242 # so I've thrown a bunch of things at the wall to speed it up.
3244 local return=$? # this MUST COME FIRST
3245 local ps_char ps_color
3248 if [[ $HISTFILE ]]; then
3249 history -a # save history
3253 0) ps_color="$term_purple"
3256 *) ps_color="$term_green"
3257 ps_char="$return \\$"
3260 if [[ ! -O . ]]; then # not owner
3261 if [[ -w . ]]; then # writable
3262 ps_color="$term_bold$term_red"
3264 ps_color="$term_bold$term_green"
3268 # faster than sourceing the file im guessing
3269 if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then
3270 eval "$(< /dev/shm/iank-status)"
3272 if [[ $MAIL_HOST && $MAIL_HOST != "$HOSTNAME" ]]; then
3273 ps_char="@ $ps_char"
3276 if [[ $(jobs -p) ]]; then
3281 # allow a function to specify a command to run after we run the next
3282 # command. Use case: a function makes a persistent notification. If
3283 # we happen to be using that terminal, we can just keep working by
3284 # entering our next command, even a noop in order to dismiss the
3285 # notification, instead of having to explicitly dismiss it.
3286 if [[ ${_psrun[*]} ]]; then
3287 if (( _psrun_count >= 1 )); then
3293 _psrun_count=$(( _psrun_count + 1 ))
3299 # We could test if sudo is active with sudo -nv
3300 # but then we get an email and log of lots of failed sudo commands.
3301 # We could turn those off, but seems better not to.
3302 if [[ $EUID != 0 ]] && [[ $DID_SUDO ]]; then
3303 psudo="\[$term_bold$term_red\]s\[$term_nocolor\] "
3305 if [[ ! $HISTFILE ]]; then
3306 ps_char="NOHIST $ps_char"
3308 PS1="${PS1%"${PS1#*[wW]}"} $jobs_char$psudo\[$ps_color\]$ps_char\[$term_nocolor\] "
3310 # copy of what is automatically added by guix.
3311 # adds [env] to PS1 if GUIX_ENVIRONMENT is set and PS1 contains '$';
3312 if [ -n "$GUIX_ENVIRONMENT" ]; then
3313 if [[ $PS1 =~ (.*)"\\$" ]]; then
3314 PS1="${BASH_REMATCH[1]} [env]\\\$ "
3319 # set titlebar. instead, using more advanced
3321 #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007"
3323 PROMPT_COMMAND=(prompt-command)
3325 if [[ $TERM == screen* ]]; then
3326 _title_escape="\033]..2;"
3328 # somme sites recommend this, i dunno what the diff is.
3329 #_title_escape="\033]30;"
3330 _title_escape="\033]0;"
3333 # make the titlebar be the last command and the current directory.
3334 auto-window-title () {
3337 # These are some checks to help ensure we dont set the title at
3338 # times that the debug trap is running other than the case we
3339 # want. Some of them might not be needed.
3340 if (( ${#FUNCNAME[@]} != 1 || ${#BASH_ARGC[@]} != 2 || BASH_SUBSHELL != 0 )); then
3343 if [[ $1 == prompt-command ]]; then
3346 echo -ne "$_title_escape ${PWD/#$HOME/~} "
3351 # note, this wont work:
3352 # x=$(mktemp); cp a $x
3353 # I havnt figured out why, bigger fish to fry.
3356 # condition from the screen man page i think.
3357 # note: duplicated in tx()
3358 if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
3359 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
3366 # * stuff that makes sense to be at the end
3372 if [[ -s "$HOME/.rvm/scripts/rvm" ]]; then
3373 # shellcheck disable=SC1091
3374 source "$HOME/.rvm/scripts/rvm"
3377 # I had this idea to start a bash shell which would run an initial
3378 # command passed through this env variable, then continue on
3379 # interactively. But the use case I had in mind went away.
3381 # if [[ $MY_INIT_CMD ]]; then
3382 # "${MY_INIT_CMD[@]}"
3386 # ensure no bad programs appending to this file will have an affect