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 # todo: not sure this works in sakura
253 #bind "\C-w": kill-region
254 # sakura == xterm-256color
256 if [[ $TERM == xterm
* ]]; then
257 # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
258 bind '"\e[1;5C": shell-forward-word' 2>/dev
/null
259 bind '"\e[1;5D": shell-backward-word' 2>/dev
/null
261 # make ctrl-backspace work. for konsole, i fixed it through
262 # /home/iank/.local/share/konsole/default.keytab
264 bind '"\eOc": shell-forward-word'
265 bind '"\eOd": shell-backward-word'
267 # i cant remember why i did this, probably to free up some keys to bind
268 # to other things in bash.
269 # other than C-c and C-z, the rest defined by stty -a are, at least in
270 # gnome-terminal, overridden by bash, or disabled by the system
271 stty lnext undef stop undef start undef
276 export BC_LINE_LENGTH
=0
279 export PROFILE_TASKS_TASK_OUTPUT_LIMIT
=100
281 # note, if I use a machine I dont want files readable by all users, set
282 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
284 # i for insensitive. the rest from
285 # X means dont remove the current screenworth of output upon exit
286 # R means to show colors n things
287 # a useful flag is -F aka --quit-if-one-screen
289 export SYSTEMD_LESS
=$LESS
292 export NNN_COLORS
=2136
294 export SL_FILES_DIR
=/b
/ds
/sl
/.iank
295 export SL_INFO_DIR
=/p
/sshinfo
300 # this is adapted from things printed to term after install
301 # pyenv. commented for now since I'm not actually using pyenv.
303 # export PYENV_ROOT="$HOME/.pyenv"
304 # command -v pyenv &>/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
305 # command -v pyenv &>/dev/null && eval "$(pyenv init -)"
308 # output showed this example for pyenv-virtualenv, which i have no idea
309 # what it is, but leaving it as a comment in case I end up doing python
312 #eval "$(pyenv virtualenv-init -)"
313 ### end begin pyenv ###
319 if [[ -s $bashrc_dir/path-add-function
]]; then
320 source $bashrc_dir/path-add-function
321 if [[ $SSH_CLIENT ]]; then
322 if grep -qF /home
/iank
/.iank
/e
/e
/etc
/exports
&>/dev
/null
; then
323 export EMACSDIR
=/home
/iank
/.iank
/e
/e
329 # if someone exported $SOE (stop on error), catch errors.
331 # Note, on debian this results in the following warning when in ssh,
332 # hich I haven't figured out how to fix. It doesn't happen if we source
333 # after the shell has started
335 # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory
336 # bash: warning: cannot start debugger; debugging mode disabled
338 if [[ -e /a
/bin
/bash-bear-trap
/bash-bear
]]; then
339 source /a
/bin
/bash-bear-trap
/bash-bear
344 path-add
--ifexists /usr
/local
/go
/bin
352 if [[ -s $path ]]; then
353 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
355 elif [[ -s $bashrc_dir/$file ]]; then
356 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
357 source $bashrc_dir/$file
362 mysrc
/a
/bin
/small-misc-bash
/ll-function
363 mysrc
/a
/bin
/distro-functions
/src
/package-manager-abstractions
365 # things to remember:
366 # ALT-C - cd into the selected directory
367 # CTRL-T - Paste the selected file path into the command line
369 # good guide to some of its basic features is the readme file
370 # https://github.com/junegunn/fzf
372 # if [[ -s /usr/share/doc/fzf/examples/key-bindings.bash ]]; then
373 # source /usr/share/doc/fzf/examples/key-bindings.bash
379 # temporary functions
381 m
"${@//spring/fall}"
384 e
"${@//spring/fall}"
388 ### begin FSF section ###
390 # Comments before functions are meant to be good useful
391 # documentation. If they fail at that, please improve them or send Ian a
394 ## copy bash completion
396 # It copies how the bash completion works from one command to other
397 # commands. Generally just use within a .bashrc.
399 # Usage: ORIGINAL_COMMAND TARGET_COMMAND...
405 if ! c
=$
(complete
-p $src 2>/dev
/null
); then
406 _completion_loader
$src &>/dev
/null ||
:
407 c
=$
(complete
-p $src 2>/dev
/null
) ||
return 0
415 ## BEGIN functions to change directory better than cd ##
419 # c: acts like cd, but stores directory history: you could alias to cd if you wanted.
422 # cl: list recent directories and optionally choose one.
424 # Finer details you may want to skip:
426 # bl: print the list of back and forward directories.
428 # We keep 2 stacks of directories, forward and back. Unlike with a web
429 # browser, the forward stack is not erased when going somewhere new.
431 # Recent directories are stored in ~/.cdirs.
433 declare -a _dir_forward _dir_back
435 # normally, the top of _dir_back is our current dir. if it isn't,
436 # put it on there, except we don't want to do that when we
437 # just launched a shell
438 if [[ $OLDPWD ]]; then
439 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
444 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
447 echo "$PWD" >> ~
/.cdirs
454 if (( ${#_dir_back[@]} == 0 )); then
455 echo "nothing left to go back to" >&2
458 top_back
="${_dir_back[-1]}"
460 if [[ $top_back == "$PWD" ]] && (( ${#_dir_back[@]} == 1 )); then
461 echo "already on last back entry" >&2
466 if [[ $top_back == "$PWD" ]]; then
467 # add to dirf if not already there
468 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$top_back" ]]; then
469 _dir_forward
+=("$top_back")
471 unset "_dir_back[-1]"
472 command cd "${_dir_back[-1]}"
474 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$PWD" ]]; then
475 _dir_forward
+=("$PWD")
477 command cd "$top_back"
480 # Interesting feature, not sure I want it.
481 # give us a peek at what is next in the list
482 # if (( ${#_dir_back[@]} >= 2 )); then
483 # printf "%s\n" "${_dir_back[-2]}"
487 # c/b/f Implementation notes:
489 # The top of the back is $PWD
490 # as long as the last directory change was due to c,b,or cl.
492 # Example of stack changes:
518 if (( ${#_dir_forward[@]} == 0 )); then
519 echo "no forward dir left" >&2
522 top_forward
="${_dir_forward[-1]}"
523 unset "_dir_forward[-1]"
526 # give us a peek at what is next in the list
527 # if (( ${#_dir_forward[@]} )); then
528 # printf "%s\n" "${_dir_forward[-1]}"
533 local i line input start
534 local -A buttondirs alines
535 local -a buttons
dirs lines
536 buttons
=( {a..z
} {2.
.9} )
537 if [[ ! -s ~
/.cdirs
]]; then
538 echo nothing
in ~
/.cdirs
544 mapfile
-t lines
<~
/.cdirs
545 start
=$
(( ${#lines[@]} - 1 ))
547 # we have ~33 buttons as of this writing, so lets
548 # prune down the history every once in a while.
549 if (( start
> 500 )); then
550 tac ~
/.cdirs |
awk '!seen[$0]++' |
head -n 200 |
tac | sponge ~
/.cdirs ||
[[ $?
== 141 ]]
553 for (( j
=start
; j
>= 0; j--
)); do
555 if [[ ! $line ||
${alines[$line]} ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
559 buttondirs
[${buttons[i]}]="$line"
560 printf "%s %s\n" ${buttons[i]} "$line"
561 # the LINES bit is for when we have a short terminal, just dont print all
562 # the directories. alternative would be to do something like less the list.
563 if (( i
== ${#buttons[@]} - 1 )) ||
{ [[ $LINES ]] && (( i
== LINES
- 3 )); }; then
569 if (( i
== 0 )); then
570 echo "no dirs in ~/.cdirs"
574 if [[ $input != $
'\n' ]]; then
575 c
"${buttondirs[$input]}"
578 # bl = back list. lists the back and forward directories. i tend to
579 # forget this exists and use cl instead.
583 start
=$
(( ${#_dir_back[@]} - 1 ))
585 # cleanup possible repeating of pwd
586 if (( start
>= 0 )) && [[ ${_dir_back[$start]} == "$PWD" ]]; then
587 start
=$
(( start
- 1 ))
590 if (( start
>= 0 )); then
591 for (( i
=start
; i
>= 0 ; i--
)); do
592 printf "%s %s\n" $j ${_dir_back[i]}
594 if (( j
>= max
)); then
601 start
=$
(( ${#_dir_forward[@]} - 1 ))
603 # cleanup possible repeating of pwd
604 if (( start
>= 0 )) && [[ ${_dir_forward[$start]} == "$PWD" ]]; then
605 start
=$
(( start
- 1 ))
607 if (( start
< 0 )); then
612 for (( i
=start
; i
>= 0 ; i--
)); do
613 printf "%s %s\n" $j ${_dir_forward[i]}
615 if (( j
>= max
)); then
620 # like running cl <enter> a <enter>
623 mapfile
-t lines
<~
/.cdirs
624 start
=$
(( ${#lines[@]} - 1 ))
625 for (( j
=start
; j
>= 0; j--
)); do
627 if [[ ! $line ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
635 ## END functions to change directory better than cd ##
637 # pee do. run args as a command with output copied to syslog.
639 # Usage: pd [-t TAG] COMMAND...
641 # -t TAG Override the tag in the syslog. The default is COMMAND with
642 # any path part is removed, eg. for /bin/cat the tag is cat.
644 # You can view the log via "journalctl -t TAG"
650 -t) tag
="$2"; shift 2 ;;
652 echo "PWD=$PWD command: $*" | logger
-t $tag
653 "$@" |
& pee
cat "logger -t $tag" || ret
=$?
654 echo "exited with status=$ret" | pee
cat "logger -t $tag"
655 # this avoids any err-catch
656 (( ret
== 0 )) ||
return $ret
660 # jdo = journal do. Run command as transient systemd service, tailing
661 # its output in the journal until it completes.
663 # Usage: jdo COMMAND...
665 # Compared to pd: commands recognize this is a non-interactive shell.
666 # The service is unaffected if our ssh connection dies, no need to run
669 # Note: The last few lines of any existing entries for a unit by that
670 # name will be output first, and there will be a few second delay at the
671 # start of the command, and a second or so at the end.
673 # Note: Functions and aliases obviously won't work, we resolve the
676 # Note: requires running as root.
678 local cmd cmd_name jr_pid ret
682 if [[ $EUID != 0 ]]; then
683 echo "jdo: error: rerun as root"
687 if [[ $cmd != /* ]]; then
688 cmd
=$
(type -P "$cmd")
691 journalctl
-qn2 -f -u "$cmd_name" &
692 # Trial and error of time needed to avoid missing initial lines.
693 # .5 was not reliable. 1 was not reliable. 2 was not reliable
696 systemd-run
--unit "$cmd_name" --wait --collect "$cmd" "$@" || ret
=$?
697 # The sleep lets the journal output its last line
698 # before the prompt comes up.
700 kill $jr_pid &>/dev
/null ||
:
703 # this avoids any err-catch
704 (( ret
== 0 )) ||
return $ret
708 # standard date as used in logs
713 # date in log appropriate format
720 command ts
"%F %T" "$@"
723 # ts log. log command to log file.
724 # usage: tsl LOG_PATH_PREFIX COMMAND...
725 # example: tsl /root/command
726 # log file will be like /root/command-2024-02-10.log
728 local log_prefix log_path appending ret
729 if (( $# < 2 )); then
730 echo "tsl: error: expected >= 2 arguments, got $#" >&2
734 if [[ $log_prefix == */* && ! -d ${log_prefix%*/} ]]; then
735 echo "tsl: error: expected directory at ${log_prefix%*/}" >&2
738 log_path
=$log_prefix-$
(date +%Y-
%m-
%d
).log
740 if [[ -s $log_path ]]; then
744 printf "%s\n" "CWD: $PWD, log: $log_path, running $*" | ts
"%F %T" |
tee -a "$log_path"
746 "$@" |
& ts
"%F %T" |
tee -a "$log_path" || ret
=$?
747 printf "%s\n" "exit code $ret from command: $*" | ts
"%F %T" |
tee -a "$log_path"
749 printf "%s\n" "note: this log file contains logs before those of previous command" | ts
"%F %T" |
tee -a "$log_path"
755 mapfile
-t cmds
<<'EOF'
756 tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab
759 ls -la /dev/disk/by-id
762 for cmd
in "${cmds[@]}"; do
782 ....
() { c ..
/..
/..
; }
783 .....
() { c ..
/..
/..
/..
; }
784 ......
() { c ..
/..
/..
/..
/..
; }
789 path
=$
(readlink
-e "$f")
790 echo "cat >$path <<'EOF'"
797 # file cut copy and paste, like the text buffers :)
798 # I havnt tested these.
799 _fbufferinit
() { # internal use
800 ! [[ $my_f_tempdir ]] && my_f_tempdir
="$(mktemp -d)"
801 rm -rf "${my_f_tempdir:?}"/*
805 cp "$@" "$my_f_tempdir"/
809 mv "$@" "$my_f_tempdir"/
811 fpst
() { # file paste
812 [[ $2 ]] && { echo too many arguments
; return 1; }
814 cp "$my_f_tempdir"/* "$target"
818 local host ip port
file key tmp
819 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" ||
: )
820 file=$
(readlink
-f ~
/.ssh
/known_hosts
)
822 echo "khfix: ssh failed"
825 if [[ $port != 22 ]]; then
826 ip_entry
="[$ip]:$port"
827 host_entry
="[$host]:$port"
832 if [[ $host != "$ip" ]]; then
834 ssh-keygen
-F "$host_entry" -f $file >$tmp ||
[[ $?
== 1 ]] # 1 when it doesnt exist in the file
835 if [[ -s $tmp ]]; then
836 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
840 grep -Fv "$key" "$file" | sponge
"$file"
845 ssh-keygen
-F "$ip_entry" -f $file >$tmp ||
[[ $?
== 1 ]]
846 if [[ -s $tmp ]]; then
847 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
851 grep -Fv "$key" "$file" | sponge
"$file"
853 ll ~
/.ssh
/known_hosts
855 khfix-r
() { # known hosts fix without syncing to root user
856 _khfix-common
"$@" ||
return 1
860 _khfix-common
"$@" ||
return 1
865 # copy path into clipboard
868 x
=$
(readlink
-nf "${1:-$PWD}")
869 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
870 # And, summarizing this:
871 # https://askubuntu.com/questions/705620/xclip-vs-xsel
872 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
876 # clipboard a string (into selection & clipboard buffer)
878 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
879 # And, summarizing this:
880 # https://askubuntu.com/questions/705620/xclip-vs-xsel
881 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
882 printf "%s" "$*" | xclip
-selection clipboard
883 printf "%s" "$*" | xclip
886 # a1 = awk {print $1}
887 for field
in {1.
.20}; do
888 eval a
$field"() { awk '{print \$$field}'; }"
891 for num
in {1.
.9}; do
892 eval h
$num"() { head -n$num || [[ \$? == 141 ]]; }"
897 # shellcheck disable=SC2046 disable=SC2001 disable=SC2183 # hacks, expected
898 printf '%d.%d.%d.%d\n' $
(echo $1 |
sed 's/../0x& /g')
902 local f out outdir
in fname origdir skip1
906 while [[ $1 == -* ]]; do
908 # if we got interrupted after 1st phase
920 # first pass only uses about 1 cpu, so run in parallel
924 if [[ $f == /* ]]; then
929 out
="$origdir/$outdir/$fname"
930 mkdir
-p /tmp
/vp
9/$fname
932 if ! $skip1 && [[ ! -s ffmpeg2pass-0.log
]]; then
933 # -nostdin or else wait causes ffmpeg to go into stopped state. dunno why, random stackoverflow answer.
934 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
936 if [[ -e $out ]]; then rm -f $out; fi
937 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
944 utcl
() { # utc 24 hour time to local hour 24 hour time
945 echo "print( ($1 $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
953 # for running in a fai rescue. iank specific.
955 d
=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
956 for f
in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V
; do
957 cryptsetup luksOpen
--key-file /p
/dev
/$f/root crypt-
$f-root
958 cryptsetup luksOpen
--key-file /p
/dev
/$f/o crypt-
$f-o
960 mount
-o subvol
=root_trisquelaramo
/dev
/mapper
/crypt-
$d-root /mnt
961 mount
-o subvol
=a
/dev
/mapper
/crypt-
$d-root /mnt
/a
962 mount
-o subvol
=o
/dev
/mapper
/crypt-
$d-o /mnt
/o
963 mount
-o subvol
=boot_trisquelaramo
/dev
/sda2
/mnt
/boot
971 c4
() { c
/var
/log
/exim4
; }
973 caa
() { git commit
--amend --no-edit -a; }
986 find -L "$@" -type f
-not \
( -name .svn
-prune -o -name .git
-prune \
987 -o -name .hg
-prune -o -name .editor-backups
-prune \
988 -o -name .undo-tree-history
-prune \
) -printf '%h\0%d\0%p\n' |
sort -t '\0' -n \
989 |
awk -F '\0' '{print $3}' 2>/dev
/null |
while read -r file; do
991 printf "%s\n" "$file"
998 calc
() { echo "scale=3; $*" |
bc -l; }
999 # no having to type quotes, but also no command history:
1003 echo "scale=3; $x" |
bc -l
1014 ccat
() { # config cat. see a config without extra lines.
1015 sed -r '/^[[:space:]]*([;#]|--|\/\/|$)/d' "$@"
1021 # dev/pts needed for pacman signature check
1022 for d
in dev proc sys dev
/pts
; do
1024 if ! mountpoint
$d &>/dev
/null
; then
1025 m s mount
-o bind /$d $d
1031 # dev/pts needed for pacman signature check
1032 for d
in dev
/pts dev proc sys
; do
1034 if mountpoint
$d &>/dev
/null
; then
1042 # join options which are continued to multiples lines onto one line
1044 while IFS
= read -r line
; do
1045 # remove leading spaces/tabs. assumes extglob
1046 if [[ $line == "[ ]*" ]]; then
1047 line
="${line##+( )}"
1052 elif [[ $line == *=* ]]; then
1053 echo "$pastline" >> "$2"
1056 pastline
="$pastline $line"
1058 done < <(grep -vE '^([ \t]*#|^[ \t]*$)' "$1")
1059 echo "$pastline" >> "$2"
1063 # diff config files,
1064 # setup for format of postfix, eg:
1067 local pastline unified f1 f2
1071 _cdiff-prep
"$1" "$f1"
1072 _cdiff-prep
"$2" "$f2"
1073 cat "$f1" "$f2" |
grep -Po '^[^=]+=' |
sort |
uniq > "$unified"
1074 while IFS
= read -r line
; do
1075 # the default bright red / blue doesnt work in emacs shell
1076 dwdiff
-cblue,red
-A best
-d " ," <(grep "^$line" "$f1" ||
echo ) <(grep "^$line" "$f2" ||
echo ) | colordiff
1082 local start
=$SECONDS
1084 # shellcheck disable=SC2030
1085 inotifywait
-m "$dir" -e create
-e moved_to | \
1086 while read -r filedir _
file; do
1089 calc $
((SECONDS
- start
)) / 60
1096 s chown
-R $USER:$USER "$@"
1099 # shellcheck disable=SC2032
1101 # makes it so chown -R symlink affects the symlink and its target.
1102 if [[ $1 == -R ]]; then
1104 command chown
-h "$@"
1105 command chown
-R "$@"
1116 d
() { builtin bg "$@"; }
1119 # f would be more natural, but i already am using it for something
1120 z
() { builtin fg "$@"; }
1123 x
() { builtin kill %%; }
1126 diff --strip-trailing-cr -w "$@" # diff content
1134 safe_rename
"$x" "$y"
1139 # usage: dfp MOUNTPOINT [SECOND_INTERVAL]
1140 # SECOND_INTERVAL defaults to 90
1143 local a b mp interval
1146 if [[ ! $mp ]]; then
1147 echo "dfp: error, missing 1st arg" >&2
1151 a
=$
(df
--output=used
$mp |
tail -n1)
1153 b
=$
(df
--output=used
$mp |
tail -n1)
1154 printf "used mib: %'d mib/min: %s\n" $
(( b
/1000 )) $
(( (b-a
) / (interval
* 1000 / 60 ) ))
1158 # get ipv4 ip from HOST. or if it is already a number, return that
1166 getent ahostsv4
"$host" |
awk '{ print $1 }' |
head -n1
1172 command dig +nostats
+nocmd
"$@"
1174 # Output with sections sorted, and removal of query id, so 2 dig outputs can be diffed.
1178 dig +nordflag
"$@" |
sed -r 's/^(;; ->>HEADER<<-.*), id: .*/\1/' |
while read -r l
; do
1179 if [[ $l == [^\
;]* ]]; then
1183 printf "%s" "$sec" |
sort
1191 # compare digs to the 2 servers
1192 # usage: digdiff @server1 @server2 DIG_ARGS
1193 # note: only the soa master nameserver will respond with
1194 # ra "recursive answer" flag. That difference is meaningless afaik.
1201 digsort
$s1 "$@" |
tee /tmp
/digdiff
1202 diff -u /tmp
/digdiff
<(digsort
$s2 "$@")
1205 # date in a format i like reading
1207 date "+%A, %B %d, %r" "$@"
1212 # date with all digits in a format i like
1216 ccomp
date dt dtr dtd
1218 dus
() { # du, sorted, default arg of
1219 du
-sh ${@:-*} |
sort -h
1224 e
() { printf "%s\n" "$*"; }
1232 printf "%qEOL\n" "${arg}"
1233 printf "%s" "${arg}" |
& hexdump -C
1237 # echo variables. print var including escapes, etc, like xxd for variable
1243 if [[ -v $arg ]]; then
1244 printf "%qEOL\n" "${!arg}"
1245 printf "%s" "${!arg}" |
& hexdump -C
1247 echo arg
$arg is
unset
1253 [[ ${#@} == 2 ]] ||
{ echo "error: ediff requires 2 arguments"; return 1; }
1254 emacs
--eval "(ediff-files \"$1\" \"$2\")"
1258 # shellcheck disable=SC2120 # we expect to pass arguments in use outside this file
1261 tail -F /var
/log
/exim
4/mainlog
/var
/log
/exim
4/*main
/var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
-n 200 "$@"
1265 tail -F /var
/log
/exim
4/mainlog
-n 200 "$@"
1268 tail -F /var
/log
/exim
4/mymain
-n 200 "$@"
1270 ccomp
tail etail etail2
1272 # ran into this online, trying it out
1274 ( "$@" &>/dev
/null
& disown )
1278 ssh "$@" cat .ssh
/authorized_keys
{,2}
1282 # print exim old pids
1284 local configtime pid piduptime now daemonpid
1285 printf -v now
'%(%s)T' -1
1286 configtime
=$
(stat
-c%Y
/var
/lib
/exim
4/config.autogenerated
)
1287 if [[ -s /run
/exim
4/exim.pid
]]; then
1288 daemonpid
=$
(cat /run
/exim
4/exim.pid
)
1290 for pid
in $
(pgrep
-f '^/usr/sbin/exim4( |$)'); do
1291 # the daemonpid gets reexeced on HUP (service reloads), keeping its same old timestamp
1292 if [[ $pid == "$daemonpid" ]]; then
1295 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
1296 if (( configtime
> now
- piduptime
)); then
1302 # exim tail but only watch lines from new pids
1305 for pid
in $
(eoldpids
); do
1308 if [[ $oldpids ]]; then
1309 etail |
awk '$3 !~ /^\[('"${oldpids%|}"')\]$/'
1314 # exim watch as old pids go away
1316 local configtime pid piduptime now tmpstr
1322 mapfile
-t oldpids
<<<"$tmpstr"
1323 if (( ! ${#oldpids[@]} )); then
1326 # print the date every 20 iterations
1327 if (( ! count
% 20 )); then
1331 ps
-f -p "${oldpids[*]}"
1337 less /var
/log
/exim
4/mainlog
1341 exiqgrep
-ir.\
* -o 60 |
while read -r i
; do
1344 hlm exigrep
$i /var
/log
/exim
4/mainlog |
cat ||
:
1348 # other ways to get the list of message ids:
1349 # exim -bp | awk 'NF == 4 {print $3}'
1350 # # this is slower 160ms, vs 60.
1352 exiqgrep
-ir.\
* |
xargs exim
-Mrm
1357 mkdir
-p /tmp
/edev
/etc
1358 cp -ra /etc
/exim4
/tmp
/edev
/etc
1359 cp -ra /etc
/alias* /tmp
/edev
/etc
1360 find /tmp
/edev
/etc
/exim4
-type f
-execdir sed -i "s,/etc/,/tmp/edev/etc/,g" '{}' +
1364 update-exim4.conf
-d /tmp
/edev
/etc
/exim4
-o /tmp
/edev
/e.conf
1368 # show important information about incoming mail in the exim log
1370 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' <${1:-/var/log/exim4/mainlog}
1373 # 2nd line is message-id:
1375 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog}
1378 tail -F /var
/log
/exim
4/mainlog |
sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p'
1385 # find array. make an array of file names found by find into $x
1386 # argument: find arguments
1387 # return: find results in an array $x
1388 while read -rd ''; do
1390 done < <(find "$@" -print0);
1393 # shellcheck disable=SC2120
1394 faf
() { # find all files. use -L to follow symlinks
1395 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1396 -o -name .hg
-prune -o -name .editor-backups
-prune \
1397 -o -name .undo-tree-history
-prune \
) -type f
2>/dev
/null
1400 # usage ffconcat FILES_TO_CONCAT OUTPUT_FILE
1404 printf "file '%s'\n" "$1" >$tmpf
1405 while (( $# > 1 )); do
1407 printf "file '%s'\n" "$1" >>$tmpf
1409 # https://trac.ffmpeg.org/wiki/Concatenate
1410 ffmpeg
-f concat
-safe 0 -i $tmpf -c copy
"$1"
1415 if (( $# == 0 )); then
1416 echo ffremux error expected args
>&2
1421 tmpf
=$tmpd/"${f##*/}"
1422 ffmpeg
-i "$f" -c:v copy
-c:a copy
$tmpf
1430 # absolute path of file/dir without resolving symlinks.
1432 # Most of the time, I want this where I would normally use readlink.
1433 # This is what realpath -s does in most cases, but sometimes it
1434 # actually resolves symlinks, at least when they are in /.
1436 # Note, if run on a dir, if the final component is relative, it won't
1437 # resolve that. Use the below fpd for that.
1439 # note: we could make a variation of this which
1440 # assigns to a variable name using eval, so that we don't have to do
1441 # x=$(fp somepath), which might save subshell overhead and look nice,
1442 # but I'm not going to bother.
1444 local initial_oldpwd initial_pwd dir base
1445 initial_oldpwd
="$OLDPWD"
1447 if [[ $1 == */* ]]; then
1450 # CDPATH because having it set will cause cd to possibly print output
1452 printf "%s%s\n" "$PWD" "$base"
1453 CDPATH
='' cd "$initial_pwd"
1454 OLDPWD
="$initial_oldpwd"
1456 printf "%s/%s\n" "$PWD" "$1"
1459 # full path of directory without resolving symlinks
1461 local initial_oldpwd initial_pwd dir
1462 initial_oldpwd
="$OLDPWD"
1466 printf "%s%s\n" "$PWD" "$base"
1468 OLDPWD
="$initial_oldpwd"
1475 sudo mailq |gr frozen|
awk '{print $3}' |
while read -r id
; do
1481 echo -e '\n\n##############################\n'
1482 done |
tee -a /tmp
/frozen
1486 while read -r line
; do
1487 printf '%s\n' "$line"
1488 ids
+=("$(printf '%s\n' "$line" |gr frozen|awk '{print $3}')")
1490 echo "sleeping for 2 in case you change your mind"
1492 sudo exim
-Mrm "${ids[@]}"
1496 # like -e for functions. returns on error.
1497 # at the end of the function, disable with:
1499 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
1500 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
1506 local help="Usage: getdir [--help] PATH
1507 Output the directory of PATH, or just PATH if it is a directory."
1508 if [[ $1 == --help ]]; then
1512 if [[ $# -ne 1 ]]; then
1513 echo "getdir error: expected 1 argument, got $#"
1516 if [[ -d $1 ]]; then
1520 dir
="$(dirname "$1")"
1521 if [[ -d $dir ]]; then
1524 echo "getdir error: directory does not exist"
1530 git_empty_branch
() { # start an empty git branch. carefull, it deletes untracked files.
1531 [[ $# == 1 ]] ||
{ echo 'need a branch name!'; return 1;}
1533 root
=$
(gitroot
) ||
return 1 # function to set gitroot
1535 git symbolic-ref HEAD refs
/heads
/$1
1540 # shellcheck disable=SC2120
1542 local help="Usage: gitroot [--help]
1543 Print the full path to the root of the current git repo
1545 Handles being within a .git directory, unlike git rev-parse --show-toplevel,
1546 and works in older versions of git which did not have that."
1547 if [[ $1 == --help ]]; then
1552 p
=$
(git rev-parse
--git-dir) ||
{ echo "error: not in a git repo" ; return 1; }
1553 [[ $p != /* ]] && p
=$PWD
1559 local args gdb
=false
1561 if [[ $EMACSDIR ]]; then
1562 path-add
"$EMACSDIR/lib-src" "$EMACSDIR/src"
1565 if [[ $DISPLAY ]]; then
1569 if (( $# == 0 )); then
1572 # duplicate -c, but oh well
1573 if ! pgrep
-u $EUID emacsclient
; then
1574 if (( $# == 0 )) && type -p gdb
&>/dev
/null
; then
1580 if [[ $EMACSDIR ]]; then
1582 # todo: we don't have to alter HOME since emacs 29+, we can set
1583 # user-emacs-directory with the flag --init-directory
1585 # Alter the path here, otherwise the nfs mount gets triggered on the
1586 # first path lookup when emacs is not being used.
1587 # shellcheck disable=SC2098 disable=SC2097 # false positive
1588 PATH
="$EMACSDIR/lib-src:$EMACSDIR/src:$PATH" EHOME
=$HOME HOME
=$EMACSDIR m emacsclient
-a "" $args "$@"
1591 # due to a bug, we cant debug from the start unless we get a new gdb
1592 # https://sourceware.org/bugzilla/show_bug.cgi?id=24454
1593 # m gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacs --daemon
1594 m emacsclient
-a "" $args "$@"
1596 cd "/a/opt/emacs-$(distro-name)$(distro-num)"
1597 s gdb
-p "$(pgrep -f 'emacs --daemon')" -ex c
1600 m emacsclient
-a "" $args "$@"
1605 # g pipe. like: cmd | emacs. save cmd output to tmp file, then edit.
1611 #like cmd &> tempfile; emacs tempfile
1613 # note: a useful workflow for doing mass replace on my files:
1615 ## remove any false positives, or manually edit them. rename files if needed.
1616 # sedi 's/REGEX/REPLACEMENT/' $(gr '^/' /a/tmp/gtmp)
1621 # g command substitution.
1623 # shellcheck disable=SC2046 # i want word splitting for this hackery
1627 # force terminal version
1633 # quit will prompt if the program crashes.
1634 gdb
-ex=r
-ex=quit
--args emacs
"$@"; r
;
1638 # kill the emacs daemon
1643 grep -iIP --color=auto
"$@" ||
return $?
1645 grr
() { # grep recursive
1646 # Don't return 1 on nonmatch because this is meant to be
1647 # interactive, not in a conditional.
1648 if [[ ${#@} == 1 ]]; then
1649 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto
"$@" . ||
[[ $?
== 1 ]]
1651 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -riIP --color=auto
"$@" ||
[[ $?
== 1 ]]
1659 # recursive everything. search for files/dirs and lines. rs = easy chars to press
1663 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1664 -o -name .hg
-prune -o -name .editor-backups
-prune \
1665 -o -name .undo-tree-history
-prune \
) 2>/dev
/null |
grep -iP --color=auto
"$query"
1669 # horizontal row. used to break up output
1672 # 180 is long enough.
1673 blocks
=██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
1674 printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)${blocks:0:${COLUMNS:-180}}$(tput sgr0 2>/dev/null||:)"
1678 local col input_len
=0
1680 input_len
=$
((input_len
+ 1 + ${#arg}))
1682 col=$
((60 - input_len
))
1683 printf "\e[1;97;41m%s" "$*"
1684 if (( col > 0 )); then
1685 # shellcheck disable=SC2046 # needed to work as intended. a better way would be like hr above.
1686 printf "\e[1;97;41m \e[0m%.0s" $
(eval echo "{1..${col}}")
1690 hlm
() { hl
"$*"; "$@"; }
1692 hrcat
() { local f
; for f
; do [[ -f $f ]] ||
continue; hr
; echo "$f"; cat "$f"; done }
1695 # github-release-dl restic/restic restic_ _linux_amd64.bz2
1697 # https://github.com/restic/restic/releases/download/v0.16.3/restic_0.16.3_linux_amd64.bz2
1698 github-release-dl
() {
1699 local github_path file_prefix file_suffix latest_prefix version redir_path
1703 if (( $# != 3 )); then
1704 echo "$0: error, expected 3 arguments" >&2
1707 redir_path
="https://github.com/$github_path/releases/latest/download/"
1708 latest_prefix
=$
(curl
-s -I "$redir_path" |
awk 'tolower($1) == "location:" {print $2}')
1709 # it has a trailing /r at the end. just kill any whitespace.
1710 latest_prefix
="${latest_prefix//[$'\t\r\n ']}"
1711 if [[ ! $latest_prefix ]]; then
1712 echo "failed to find latest path. Tried to find case insensitive 'location:' in the curl output:"
1713 m curl
-s -I "$redir_path"
1716 version
="${latest_prefix##*/}"
1717 version
="${version#v}"
1718 m wget
-- "$latest_prefix/$file_prefix$version$file_suffix"
1722 # go-github-install restic/restic restic_ _linux_amd64.bz2
1723 # go-github-install restic/rest-server rest-server_ _linux_amd64.tar.gz
1725 # common pattern among go binaries on github
1726 go-github-install
() {
1727 local tmpd targetf tmp files src
1732 tmp
="${file_prefix##*[[:alnum:]]}"
1733 targetf
="${file_prefix%"$tmp"}"
1734 echo targetf
: $targetf
1735 github-release-dl
"$@"
1737 case $file_suffix in
1745 rm -f -- "${files[@]}"
1747 # Here we detect and handle 2 cases: either we extracted a single
1748 # binary which we have to rename or a folder with a binary named
1749 # $targetf in it which is all we care about.
1750 if (( ${#files[@]} == 1 )) && [[ -f ${files[0]} ]]; then
1752 mv -- .
/* /usr
/local
/bin
/$targetf
1754 files
=(.
/*/$targetf)
1755 if [[ -f $targetf ]]; then
1757 elif [[ -f ${files[0]} ]]; then
1761 mv -- "$src" /usr
/local
/bin
1767 ## 2024: I'm using gh instead of hub, but leaving this just in case.
1768 ## I tried the github cli tool (gh) and it seems easier than
1771 ## hub predated github's 2020 official cli tool gh.
1773 ## https://raw.githubusercontent.com/cli/cli/trunk/docs/gh-vs-hub.md
1774 # get latest hub and run it
1775 # main command to use:
1776 # hub pull-request --no-edit
1777 # --no-edit means to use the first commit\'s message as the pull request message.
1778 # If that fails, try doing
1779 # hub pull-request --no-edit -b UPSTREAM_OWNER:branch
1780 # where branch is usually master. it does the pr against your current branch.
1782 # On first use, you input username/pass and it gets an oath token so you dont have to repeat
1783 # it\'s at ~/.config/hub
1785 local up uptar updir p re
1786 # example https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz
1787 up
=$
(wget
-q -O- https
://api.github.com
/repos
/github
/hub
/releases
/latest | jq
-r .assets
[].browser_download_url |
grep linux-amd64
)
1789 if [[ ! $up ||
$up =~
$re ]]; then
1790 echo "failed to get good update url. got: $up"
1794 if [[ ! -e /a
/opt
/$updir ]]; then
1795 rm -rf /a
/opt
/hub-linux-amd64
*
1797 tar -C /a
/opt
-zxf /a
/opt
/$uptar
1800 if ! which hub
&>/dev
/null
; then
1801 sudo
/a
/opt
/$updir/install
1804 # save token across computers
1805 if [[ ! -L ~
/.config
/hub
]]; then
1806 if [[ -e ~
/.config
/hub
]]; then
1807 mv ~
/.config
/hub
/p
/c
/subdir_files
/.config
/
1809 if [[ -e /p
/c
/subdir_files
/.config
/hub
]]; then
1823 # cvs update -C FILE
1828 # potentially useful command translation
1829 # https://fling.seas.upenn.edu/~giesen/dynamic/wordpress/equivalent-commands-for-git-svn-and-cvs/
1831 # importing cvs repo into git using git-cvs package:
1832 # /f/www $ /usr/lib/git-core/git-cvsimport -C /f/www-git
1848 find -L "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1849 -o -name .hg
-prune -o -name .editor-backups
-prune \
1850 -o -name .undo-tree-history
-prune \
) -iname "*$glob*" 2>/dev
/null
1854 # insensitive find here. args are combined into the search string.
1855 # -L = follow symlinks
1856 find -L .
-not \
( -name .svn
-prune -o -name .git
-prune \
1857 -o -name .hg
-prune -o -name .editor-backups
-prune \
1858 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1862 # insensitive find directory
1863 find -L .
-type d
-not \
( -name .svn
-prune -o -name .git
-prune \
1864 -o -name .hg
-prune -o -name .editor-backups
-prune \
1865 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1870 sudo iptables
-A INPUT
-s $1 -j DROP
1875 grep -Il "" "$@" &>/dev
/null
1882 # journalctl with times in the format the --since= and --until= options accept
1883 jrt
() { journalctl
-e -n100000 -o short-full
"$@"; }
1884 jr
() { journalctl
-e -n100000 "$@" ; }
1885 jrf
() { journalctl
-n1000 -f "$@" ; }
1887 # the invocation id is "assigned each time the unit changes from an inactive
1888 # state into an activating or active state" man systemd.exec
1889 journalctl
-e --no-tail -u exim4 _SYSTEMD_INVOCATION_ID
="$(systemctl show -p InvocationID --value $1)"
1891 ccomp journalctl jr jrf jru
1896 if [[ $PWD == /[iap
] ]]; then
1897 command ls -A --color=auto
-I lost
+found
"$@"
1899 command ls -A --color=auto
"$@"
1903 lcn
() { locate -i "*$**"; }
1905 lg
() { LC_COLLATE
=C.UTF-8 ll
--group-directories-first "$@"; }
1907 lt
() { ll
-tr "$@"; }
1909 lld
() { ll
-d "$@"; }
1911 ccomp
ls l lg lt lld ll
1917 for dirs in false true
; do
1919 if [[ -d $f ]]; then
1921 # reverse the order to rename the nested dirs first.
1922 # note: 0 element is the dir itself
1923 for ((i
=${#all[@]}-1; i
>=1; i--
)); do
1925 if $dirs && [[ -d $a ]]; then
1926 # e dirs low "$a" # debug
1928 elif ! $dirs && [[ ! -d $a && -e $a ]]; then
1930 # e not dirs low "$a" # debug
1935 # just rename all the top level args on the second pass
1937 # e final dirs low "$f" # debug
1944 low
() { # make filenames lowercase, remove bad chars
1947 arg
="${arg%%+(/)}" # remove trailing slashes. assumes we have extglob on.
1949 if (( ${#dir} == ${#arg} )); then
1953 new
="${f,,}" # downcase
1954 # shellcheck disable=SC2031 # seems like a shellcheck bug
1955 new
="${new//[^a-zA-Z0-9._-]/_}" # sub bad chars
1956 new
="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
1957 new
="${new%"${new##*[[:alnum:]]}"}"
1958 # remove bad underscores, like __ and _._
1959 new
=$
(echo $new |
sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
1960 safe_rename
"$dir/$f" "$dir/$new" ||
return 1
1965 lower
() { # make first letter of filenames lowercase.
1968 if [[ ${x::1} == [A-Z
] ]]; then
1969 y
=$
(tr '[:upper:]' '[:lower:]' <<<"${x::1}")"${x:1}"
1970 safe_rename
"$x" "$y" ||
return 1
1976 k
() { # history search
1977 grep -iP --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
tail -n 80 ||
[[ $?
== 1 ]];
1979 ks
() { # history search with context
1980 # args are an extended regex used by sed
1981 history |
sed -nr "h;s/^\s*(\S+\s+){4}//;/$*/{g;p}" |
tail -n 80 ||
[[ $?
== 1 ]];
1983 ksu
() { # history search unique
1984 grep -P --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
uniq ||
[[ $?
== 1 ]];
1987 # todo: id like to do maybe a daily or hourly cronjob to
1988 # check that my history file size is increasing. Ive had it
1989 # inexplicably truncated in the past.
1992 HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/'
1993 read -r -p "press anything but contrl-c to delete"
1994 for entry
in $
(HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/ { print a }' |
tac); do
2000 # history without the date
2002 history "$@" | cut
-d' ' -f 7-
2005 ccomp
grep k ks ksu histrm
2009 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
2010 make -qp |
awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
2022 # mkdir the last arg, cp the rest into it
2025 cp "${@:1:$#-1}" "${@: -1}"
2029 mv "${@:1:$#-1}" "${@: -1}"
2032 mkt
() { # mkdir and touch file
2034 mkdir
-p "$(dirname "$path")"
2038 # shellcheck disable=SC2032
2039 mkdir
() { command mkdir
-p "$@"; }
2042 # https://github.com/HenriWahl/Nagstamon/issues/357
2043 if ! pgrep
-f /usr
/bin
/dunst
>/dev
/null
; then
2046 /usr
/bin
/nagstamon
&
2051 screen
-RD -S profanity
2054 # i dont want to wait for konsole to exit...
2056 command prof
&>/dev
/null
&
2061 printf '\033[1A\033[K'; printf "%s\n" "$l"| ts
"%F %T" |
tee -a /p
/self-chat.log
2066 # cant use s because sudo -i doesnt work for passwordless sudo command
2069 sudo nmtui-connect
"$@"
2079 if shopt nullglob
>/dev
/null
; then
2093 # shellcheck disable=SC2024
2095 for f
in /var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
; do
2097 if [[ -s $f ]]; then
2098 echo ================== $f =============
2099 s
tee -a /var
/log
/exim
4/$base-archive <$f
2107 ping() { command ping -O "$@"; }
2108 p8
() { ping "$@" 8.8.8.8; }
2109 p6
() { ping6
"$@" 2001:4860:4860::8888; }
2111 pkx
() { # package extract
2112 local pkg cached tmp f
2115 # shellcheck disable=SC2012
2116 cached
=$
(ls -t /var
/cache
/apt
/archives
/${pkg}_
* 2>/dev
/null |
tail -n1 2>/dev
/null
) ||
:
2117 if [[ $cached ]]; then
2120 m aptitude download
$pkg ||
return 1
2122 tmp
=(*); f
=${tmp[0]} # only 1 expected
2131 tmpf
=$
(pgrep
-f "$*")
2132 mapfile
-t pids
<<<"$tmpf"
2135 # shellcheck disable=SC2128
2141 0) echo "no pid found" ;;
2150 help="Usage: psg [--help] GREP_ARGS
2151 grep ps and output in a nice format"
2152 if [[ $1 == --help ]]; then
2157 # final grep is because some commands tend to have a lot of trailing spaces
2158 y
=$
(echo "$x" |
grep -iP "$@" |
grep -o '.*[^ ]') ||
:
2160 echo "$x" |
head -n 1 ||
[[ $?
== 141 ]]
2165 pubip
() { curl
-4s https
://icanhazip.com
; }
2166 pubip6
() { curl
-6s https
://icanhazip.com
; }
2167 whatismyip
() { pubip
; }
2170 q
() { # start / launch a program in the backround and redir output to null
2174 # shellcheck disable=SC2120
2176 if [[ $HISTFILE ]]; then
2177 history -a # save history
2179 trap ERR
# this avoids a segfault
2181 # i had this redir, not sure why
2182 # exit "$@" 2>/dev/null
2185 # scp is insecure and deprecated.
2187 rsync
-Pt --inplace "$@"
2192 # available high ports are 1024-65535,
2193 # but lets skip things that are more likely to be in use
2196 print(secrets.SystemRandom().randrange(10002,65500))
2202 # shellcheck disable=SC1090 # expected to not follow
2212 # rsync, root is required to keep permissions right.
2213 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
2214 # --no-times --delete
2215 # basically, make an exact copy, use checksums instead of file times to be more accurate
2216 rsync
-ahvic --delete "$@"
2219 # like rlu, but dont delete files on the target end which
2220 # do not exist on the original end.
2224 # rl without preserving modification time.
2225 rsync
-ahvic --delete --no-t "$@"
2227 # [RSYNC_OPTS] HOST PATH
2229 # eg. rsu -opts frodo /testpath
2230 # relative paths will expanded with readlink -f.
2231 opts
=("${@:1:$#-2}") # 1 to last -2
2232 path
="${*:$#}" # last
2233 host="${*:$#-1:1}" # last -1
2234 if [[ $path == .
* ]]; then
2235 path
=$
(readlink
-f $path)
2237 m rsync
-ahvi --relative --no-implied-dirs "${opts[@]}" "$path" "root@$host:/";
2239 ccomp rsync rsd rsa rst rsu
2241 # find programs listening on a port
2244 # to figure out these args, i had to look at the man page from git version, as of 2022-04.
2245 s ss
-lpn state listening sport
= $port
2250 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2255 hr
; s ss
-lpn sport
= 53
2256 if systemctl is-enabled dnsmasq
&>/dev
/null ||
[[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2257 # this will fail is dnsmasq is failed
2258 hr
; m ser status dnsmasq |
cat ||
:
2260 hr
; echo $f:; ccat
$f
2261 hr
; m grr
'^ *(servers-file|server) *=|^ *no-resolv *$' /etc
/dnsmasq.conf
/etc
/dnsmasq.d
2262 f
=/etc
/dnsmasq-servers.conf
2263 hr
; echo $f:; ccat
$f
2266 echo /etc
/nsswitch.conf
:
2267 grep '^ *hosts:' /etc
/nsswitch.conf
2268 if systemctl is-enabled systemd-resolved
&>/dev
/null ||
[[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2269 hr
; m ser status systemd-resolved |
cat ||
:
2270 hr
; m resolvectl status |
cat
2278 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2282 m sudo nscd
-i hosts
2284 if [[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2285 m sudo systemctl restart dnsmasq
2287 if [[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2288 m sudo systemctl restart systemd-resolved
2290 if type -P resolvectl
&>/dev
/null
; then
2291 resolvectl flush-caches
2295 # add annoyingly long argument which should be the default
2297 sed -i --follow-symlinks "$@"
2302 # todo: test variable assignment with newlines here.
2303 # https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
2305 # beware that it only works on the assumption that any special
2306 # characters in the input string are intended to be escaped, not to work
2307 # as special chacters.
2309 LC_ALL
=C
sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
2313 ssh fencepost
head -n 300 /gd
/gnuorg
/EventAndTravelInfo
/rms-current-trips.txt |
less
2322 command sudo
"$@" ||
return $?
2327 # I use a function because otherwise we cant use in a script,
2328 # cant assign to variable.
2330 # note: gksudo is recommended for X apps because it does not set the
2331 # home directory to the same, and thus apps writing to ~ fuck things up
2332 # with root owned files.
2334 if [[ $EUID != 0 ||
$1 == -* ]]; then
2335 # shellcheck disable=SC2034
2336 SUDOD
="$PWD" command sudo
-i "$@"
2342 sb
() { # sudo bash -c
2343 # use sb instead of s is for sudo redirections,
2344 # eg. sb 'echo "ok fine" > /etc/file'
2345 # shellcheck disable=SC2034
2347 sudo
-i bash
-c "$@"
2350 se
() { s urun
0077 "$@"; }
2353 safe_rename
() { # warn and dont rename if file exists.
2354 # mv -n exists, but it\'s silent
2355 if [[ $# != 2 ]]; then
2356 echo safe_rename error
: $# args
, need
2 >&2
2359 if [[ $1 != "$2" ]]; then # yes, we want to silently ignore this
2360 if [[ -e $2 ||
-L $2 ]]; then
2361 echo "Cannot rename $1 to $2 as it already exists."
2370 sudo
dd status
=none of
="$1"
2374 if type -p systemctl
&>/dev
/null
; then
2377 if (( $# >= 3 )); then
2378 echo iank
: ser expected
2 or
less arguments
2385 systemctl
-n 40 status
"$@"
2388 seru
() { systemctl
--user "$@"; }
2389 # like restart, but do nothing if its not already started
2392 if [[ $
(s systemctl
--no-pager show
-p ActiveState
$service ) == ActiveState
=active
]]; then
2393 systemctl restart
$service
2397 setini
() { # set a value in a .ini style file
2398 key
="$1" value
="$2" section
="$3" file="$4"
2399 if [[ -s $file ]]; then
2400 sed -ri -f - "$file" <<EOF
2401 # remove existing keys
2402 / *\[$section\]/,/^ *\[[^]]+\]/{/^\s*${key}[[:space:]=]/d}
2404 /^\s*\[$section\]/a $key=$value
2405 # from section to eof, do nothing
2406 /^\s*\[$section\]/,\$b
2407 # on the last line, if we haven't found section yet, add section and key
2419 sgo
() { # service go
2421 ser restart
$service ||
return 1
2422 if type -p systemctl
&>/dev
/null
; then
2428 # ignore services that dont exist
2429 if systemctl
cat $service &>/dev
/null
; then
2431 ser disable
$service
2437 systemctl list-unit-files | rg
"$@"
2440 # check whether we generally want to do sk on the file
2442 [[ ! -L $f ]] && istext
"$1" && [[ $
(head -n1 "$1" 2>/dev
/null
) == '#!/bin/bash'* ]]
2446 # see https://savannah.gnu.org/maintenance/fsf/bash-style-guide/ for justifications
2447 local quotes others ret
2448 quotes
=2048,2068,2086,2206,2254
2449 others
=2029,2032,2033,2054,2164
2450 # Dont complain about sourced files if we aren't asking for them.
2451 if [[ $1 != -x ]]; then
2454 shellcheck
-W 999 -e $quotes,$others "$@" || ret
=$?
2455 if (( ret
>= 1 )); then
2456 echo "A template comment to disable is now in clipboard. eg: # shellcheck disable=SC2206 # reason"
2457 cbs
"# shellcheck disable=SC"
2464 # sk with quotes. For checking scripts that we expect to take untrusted
2465 # input in order to verify we quoted vars.
2468 others
=2029,2033,2054,2164
2469 shellcheck
-W 999 -x -e $others "$@" ||
return $?
2472 # sk on all modified files in current git repo
2475 for f
in $
(i s |
awk '$1 == "modified:" {print $2}'); do
2483 # sk on all the files in current git repo (except those excluded)
2485 local f toplevel orig_dir tmp skip pattern
2486 local -a ls_files excludes
2487 toplevel
=$
(git rev-parse
--show-toplevel)
2488 if [[ $PWD != "$toplevel" ]]; then
2496 # sourced from .bash_profile
2502 tmp
=$
(git ls-files | shuf
)
2503 mapfile
-t ls_files
<<<"$tmp"
2504 for f
in "${ls_files[@]}"; do
2506 for pattern
in "${excludes[@]}"; do
2507 if [[ $f == $pattern ]]; then
2512 if $skip; then continue; fi
2515 for pattern
in "${no_check_sourced[@]}"; do
2516 if [[ $f == "$pattern" ]]; then
2522 printf "================= %s\n" "$f"
2523 sk
$check_source --color=always
$f ||
[[ $?
== 1 ]]
2526 if [[ $orig_dir ]]; then
2532 # sl: ssh, but firsh rsync our bashrc and related files to a special
2533 # directory on the remote host if needed.
2535 # Some environment variables and files need to be setup for this to work
2536 # (mine are set at the beginning of this file)
2538 # SL_FILES_DIR: Environment variable. Path to folder which should at
2539 # least have a .bashrc file or symlink. This dir will be rsynced to ~ on
2540 # remote hosts (top level symlinks are resolved) unless the host already
2541 # has a $SL_FILES_DIR/.bashrc. In that case, we assume it is a host you
2542 # control and sync files to separately and already has the ~/.bashrc you
2543 # want. The remote bash will also take its .inputrc config from this
2544 # folder (default of not existing is fine). Mine looks like this:
2545 # https://iankelling.org/git/?p=distro-setup;a=tree;f=sl/.iank
2547 # SL_INFO_DIR: Environment variable. This folder stores info about what
2548 # we detected on the remote system and when we last synced. It will be created
2549 # if it does not exist. Sometimes you may want to forget about a
2550 # remote system, you can use sl --rsync, or the function for that slr
2553 # SL_TEST_CMD: Env var. Meant to be used to vary the files synced
2554 # depending on the remote host. Run this string on the remote host the
2555 # first time sl is run (or if we run slr). The result is passed to
2556 # SL_TEST_HOOK. For example,
2557 # export SL_TEST_CMD=". /etc/os-release ; echo \${VERSION//[^a-zA-Z0-9]/}"
2559 # SL_TEST_HOOK: Env var. It is run as $SL_TEST_HOOK. This can set
2560 # $SL_FILES_DIR to vary the files synced.
2562 # SL_RSYNC_ARGS: Env var. String of arguments passed to rsync. For
2563 # example to exclude files within a directory. Note, excluded
2564 # files wont be deleted on rsync, you can add --delete-excluded
2565 # to the rsync command if that is desired.
2567 # SL_SSH_ARGS: Env var. Default arguments passed to ssh.
2569 # For when ~/.bashrc is already customized on the remote server, you
2570 # might find it problematic that ~/.bashrc is sourced for ALL ssh
2571 # commands, even in scripts. This paragraph is all about that. bash
2572 # scripts dont source ~/.bashrc, but call ssh in scripts and you get
2573 # ~/.bashrc. You dont want this. .bashrc is meant for interactive shells
2574 # and if you customize it, probably has bugs from time to time. This is
2575 # bad. Here's how I fix it. I have a special condition to "return" in my
2576 # .bashrc for noninteractive ssh shells (copy that code). Then use this
2577 # function or similar that passes LC_USEBASHRC=t when sshing and I want
2578 # my bashrc. Also, I don't keep most of my bashrc in .bashrc, i source a
2579 # separate file because even if I return early on, the whole file gets
2580 # parsed which can fail if there is a syntax error.
2582 # Background on LC_USEBASHRC var (no need to read if you just want to
2583 # use this function): env variables sent across ssh are strictly
2584 # limited, but we get LC_* at least in debian based machines, so we
2585 # just make that * be something no normal program would use. Note, on
2586 # hosts that dont allow LC_* I start an inner shell with LC_USEBASHRC
2587 # set, and the inner shell also allows running a nondefault
2588 # .bashrc. This means the outer shell still ran the default .bashrc,
2589 # but that is the best we can do.
2591 local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2
type info_sec force_rsync \
2592 sync_dirname testcmd extra_info testbool files_sec sl_test_cmd sl_test_hook
2593 declare -a args tmpa
2597 # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
2598 # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address]
2599 # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option]
2600 # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname
2603 # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
2604 # [-D [bind_address:]port] [-E log_file] [-e escape_char]
2605 # [-F configfile] [-I pkcs11] [-i identity_file]
2606 # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]
2607 # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
2608 # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
2611 if [[ $1 == --rsync ]]; then
2615 # shellcheck disable=SC2153 # intentional
2616 sl_test_cmd
=$SL_TEST_CMD
2617 # shellcheck disable=SC2153 # intentional
2618 sl_test_hook
=$SL_TEST_HOOK
2619 # shellcheck disable=SC2153 # intentional
2620 sl_rsync_args
=$SL_RSYNC_ARGS
2647 # note we dont support things like -4oOption
2648 -[46AaCfGgKkMNnqsTtVvXxYy
]*)
2651 -[bcDEeFIiJLlmOopQRSWw
]*)
2652 # -oOption etc is valid
2653 if (( ${#1} >= 3 )); then
2656 args
+=("$1" "$2"); shift 2
2665 if [[ ! $remote ]]; then
2666 echo $0: error hostname required
>&2
2671 if [[ ! $SL_INFO_DIR ]]; then
2672 echo 'error: missing SL_INFO_DIR env var' >&2
2678 tmpa
=($SL_INFO_DIR/???????????
"$remote")
2680 if [[ -e $sshinfo ]]; then
2681 if $force_rsync; then
2688 tmp
=${sshinfo[0]##*/}
2691 extra_info
=$
(cat $sshinfo)
2693 # we test for string to know ssh succeeded
2694 testbool
="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
2695 testcmd
="if $testbool; then printf y; else printf n; fi"
2696 if ! tmp
=$
(LC_USEBASHRC
=y
command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
2697 echo failed sl
test. doing plain
ssh -v
2698 command ssh -v "${args[@]}" "$remote"
2700 if [[ $tmp == y
* ]]; then
2706 extra_info
="${tmp:1}"
2708 if [[ $sl_test_hook ]]; then
2709 RSYNC_RSH
="ssh ${args[*]}" $sl_test_hook "$extra_info" "$remote"
2712 if $haveinfo && [[ $type == b
]]; then
2714 read -r files_sec _
< <(find -L $SL_FILES_DIR -printf "%T@ %p\n" |
sort -nr ||
[[ $?
== 141 ||
${PIPESTATUS[0]} == 32 ]] )
2715 files_sec
=${files_sec%%.*}
2716 if (( files_sec
> info_sec
)); then
2722 sync_dirname
=${SL_FILES_DIR##*/}
2724 if [[ ! $SL_FILES_DIR ]]; then
2725 echo 'error: missing SL_FILES_DIR env var' >&2
2730 RSYNC_RSH
="ssh ${args[*]}" m rsync
-rptL --delete $sl_rsync_args $SL_FILES_DIR "$remote":
2732 if $dorsync ||
! $haveinfo; then
2733 sshinfo
=$SL_INFO_DIR/$EPOCHSECONDS$type"$remote"
2734 [[ -e $SL_INFO_DIR ]] || mkdir
-p $SL_INFO_DIR
2735 printf "%s\n" "$extra_info" >$sshinfo
2738 if [[ $type == b
]]; then
2739 if (( ${#@} )); then
2740 # Theres a couple ways to pass arguments, im not sure whats best,
2741 # but relying on bash 4.4+ escape quoting seems most reliable.
2742 command ssh "${args[@]}" "$remote" \
2743 LC_USEBASHRC
=t bash
-c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash
${@@Q}
2744 elif [[ ! -t 0 ]]; then
2745 # This case is when commands are being piped to ssh.
2746 # Normally, no bashrc gets sourced.
2747 # But, since we are doing all this, lets source it because we can.
2748 cat <(echo .
$sync_dirname/.bashrc
) - |
command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2750 command ssh -t "${args[@]}" "$remote" LC_USEBASHRC
=t INPUTRC
=$sync_dirname/.inputrc bash
--rcfile $sync_dirname/.bashrc
2754 LC_USEBASHRC
=t
command ssh "${args[@]}" "$remote" ${@@Q}
2756 command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2759 # this function inspired from https://github.com/Russell91/sshrc
2766 sl
-oControlMaster=no
-oControlPath=/ "$@"
2768 # kill off old shared socket then ssh
2770 m
ssh -O exit "$@" ||
[[ $?
== 255 ]]
2773 ccomp
ssh sl slr sss ssk
2776 LC_USEBASHRC
=t
command ssh "$@"
2781 # log with script. timing is $1.t and script is $1.s
2782 # -l to save to ~/typescripts/
2783 # -t to add a timestamp to the filenames
2784 local logdir do_stamp arg_base
2785 (( $# >= 1 )) ||
{ echo "arguments wrong"; return 1; }
2788 while getopts "lt" option
2791 l
) arg_base
=$logdir ;;
2794 echo error
: bad option
2799 shift $
((OPTIND
- 1))
2801 [[ -e $logdir ]] || mkdir
-p $logdir
2802 $do_stamp && arg_base
+=$
(date +%F.
%T
%z
)
2803 script -t $arg_base.s
2> $arg_base.t
2805 splay
() { # script replay
2806 #logRoot="$HOME/typescripts/"
2807 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
2808 scriptreplay
"$1.t" "$1.s"
2812 # sudo redo. be aware, this command may not work right on strange distros or earlier software
2813 if [[ $# == 0 ]]; then
2814 sudo
-E bash
-c -l "$(history -p '!!')"
2816 echo this
command redos last
history item. no argument is accepted
2821 # with -ll, less secure but faster.
2822 command srm
-ll "$@"
2827 ssh $1 "/tmp/${2##*/}" "$(printf "%q
\n" "${@:2}")"
2839 tclock
() { # terminal clock
2844 # this goes to full width
2845 #len=${1:-$((COLUMNS -7))}
2848 if (( x
== len
)); then
2850 d
="$(date +%l:%_M) "
2853 d
=$
(date +%l
:%M
:%_S
)
2857 for ((i
=0; i
<x
; i
++)); do
2858 if (( i
% 6 )); then
2876 # test existence / exists
2879 [[ -e "$x" ||
-L "$x" ]] || ret
=1
2885 # normally, i would just execute these commands in the function.
2886 # however, DEBUG is not inherited, so we need to run it outside a function.
2887 # And we want to run set -x afterwards to avoid spam, so we cram everything
2888 # in here, and then it will run after this function is done.
2889 # # set as array to satisfy shellcheck, but it is equivalent to setting it as non-array
2890 PROMPT_COMMAND
=('trap DEBUG; unset PROMPT_COMMAND; PS1="\w \$ "')
2893 PROMPT_COMMAND
=(prompt-command
)
2894 if [[ $TERM == *(screen
*|xterm
*|rxvt
*) ]]; then
2895 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
2899 # prometheus node curl
2902 host=${1:-127.0.0.1}
2903 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
2906 tx
() { # toggle set -x, and the prompt so it doesnt spam
2907 if [[ $
- == *x
* ]]; then
2916 # show all processes in the network namespace $1.
2917 # blank entries appear to be subprocesses/threads
2921 sudo
find -L /proc
/[1-9]*/task
/*/ns
/net
-samefile /run
/netns
/$netns | cut
-d/ -f5 | \
2923 x
=$
(ps
-w --no-headers -p $l);
2924 if [[ $x ]]; then echo "$x"; else echo $l; fi;
2928 if ! s ip netns list |
grep -Fx nonet
&>/dev
/null
; then
2929 s ip netns add nonet
2931 sudo
-E env
/sbin
/ip netns
exec nonet sudo
-E -u iank
/bin
/bash
2934 m
() { printf "%s\n" "$*"; "$@"; }
2935 m2
() { printf "%s\n" "$*" >&2; "$@"; }
2937 # update file. note: duplicated in mail-setup.
2938 # updates $ur u result to true or false
2939 # updates $reload to true if file updated is in /etc/systemd/system
2941 local tmp tmpdir dest
="$1"
2942 local base
="${dest##*/}"
2943 local dir
="${dest%/*}"
2944 if [[ $dir != "$base" ]]; then
2945 # dest has a directory component
2948 # shellcheck disable=SC2034 # see comment at top of function
2950 tmpdir
="$(mktemp -d)"
2951 cat >$tmpdir/"$base"
2952 tmp
=$
(rsync
-ic $tmpdir/"$base" "$dest")
2954 printf "%s\n" "$tmp"
2955 # shellcheck disable=SC2034 # see comment at top of function
2957 if [[ $dest == /etc
/systemd
/system
/* ]]; then
2958 # shellcheck disable=SC2034 # see comment at top of function
2967 if type -p uprecords
&>/dev
/null
; then
2975 for x
in "$@"; do virsh destroy
"$x"; virsh undefine
"$x"; done
2983 sudo virsh dumpxml
$vm |
sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
2984 sed -r "s/listen='[^']+/listen='$ip/"> $t
2985 sudo virsh undefine
$vm
2986 sudo virsh define
$t
2991 vm-set-listen
$1 0.0.0.0
2996 vm-set-listen
$1 127.0.0.1
3001 interfaces
=$
(iw dev |
awk '$1 == "Interface" {print $2}')
3002 for i
in $interfaces; do
3003 echo "myiwscan: considering $i"
3004 # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines.
3005 # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h}
3006 sudo iw dev
$i scan |
sed -rn "
3007 s/^\Wcapability: (.*)/\1/;Ta;h;b
3008 :a;s/^\Wsignal: -([^.]+).*/\1/;Tb;H;b
3009 # padded to min width of 20
3010 :b;s/\WSSID: (.*)/\1 /;T;s/^(.{20}(.*[^ ])?) */\1/;H;g;s/(.*)\n(.*)\n(.*)/\2 \3 \1/gp;b
3015 # Run script by copying it to a temporary location first,
3016 # and changing directory, so we don't have any open
3017 # directories or files that could cause problems when
3034 # spark 1 5 22 13 53
3038 # Copyright (c) Zach Holman, https://zachholman.com
3039 # https://github.com/holman/spark
3041 # As of 2022-10-28, I reviewed github forks that had several newer
3042 # commits, none had anything interesting. I did a little refactoring
3043 # mostly to fix emacs indent bug.
3045 # Generates sparklines.
3048 if [ "X$1" = "X-n" ]; then
3062 # find min/max values
3063 local min
=0xffffffff max
=0
3067 # on Linux (or with bash4) we could use `printf %.0f $n` here to
3068 # round the number but that doesn't work on OS X (bash3) nor does
3069 # `awk '{printf "%.0f",$1}' <<< $n` work, so just cut it off
3071 (( n
< min
)) && min
=$n
3072 (( n
> max
)) && max
=$n
3073 numbers
=$numbers${numbers:+ }$n
3077 local ticks
=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █
)
3079 # use a high tick if data is constant
3080 (( min
== max
)) && ticks
=(▅ ▆
)
3083 f
=$
(( ( (max-min
) <<8)/( tc - 1) ))
3088 _spark_echo -n ${ticks[$(( (((n-min)<<8)/f) ))]}
3093 pdfwc() { local f; for f; do echo "$f" "$(pdfinfo "$f" | awk '/^Pages:/ {print $2}')"; done }
3096 # nvm install script appended this to my .bashrc. I dont want to run it all the time,
3097 # so put it in a function.
3099 export NVM_DIR="$HOME/.nvm"
3100 # shellcheck disable=SC1091 # may not exist, & third party
3101 [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" # This loads nvm
3102 # shellcheck disable=SC1091 # may not exist, & third party
3103 [ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion" # This loads nvm bash_completion
3108 if date -d 'february 29' &>/dev/null; then
3118 if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
3125 # make vim work with my light colortheme terminal.
3127 if [[ -e ~/.vimrc ]]; then
3130 command vim -c ':colorscheme peachpuff' "$@"
3134 # ls count. usage: pass a directory, get the number of files.
3135 # https://unix.stackexchange.com/questions/90106/whats-the-most-resource-efficient-way-to-count-how-many-files-are-in-a-director
3137 # shellcheck disable=SC2790 disable=SC2012 # intentional
3141 # run then notify. close notification after the next prompt.
3144 dunstify -u critical -h string:x-dunst-stack-tag:profanity "$*"
3145 _psrun=(dunstctl close-all)
3148 dunstify -u critical -h string:x-dunst-stack-tag:profanity n
3149 _psrun=(dunstctl close-all)
3155 # shellcheck disable=SC2030
3156 inotifywait -m "$dir" -e create -e moved_to | while read -r _ _ file; do
3170 if ! type -p sponge &>/dev/null; then
3171 echo "$0: error: missing dependency: sudo apt install moreutils" >&2
3176 echo "adding header to $f"
3177 if [[ -s $f ]]; then
3182 cat - "${f_maybe[@]}" <<EOF | sponge "$f"
3183 The following is the GNU All-permissive License as recommended in
3184 <https://www.gnu.org/licenses/license-recommendations.en.html>
3186 Copyright (C) $(date +%Y) Free Software Foundation <sysadmin@fsf.org>
3188 Copying and distribution of this file, with or without modification,
3189 are permitted in any medium without royalty provided the copyright
3190 notice and this notice are preserved. This file is offered as-is,
3191 without any warranty.
3193 Contributions are welcome. See <https://savannah.gnu.org/maintenance/fsf/>.
3199 # note, there is also the tool gron which is meant for this, but
3200 # this is good enough to not bother installing another tool
3202 # https://stackoverflow.com/questions/59700329/how-to-print-path-and-key-values-of-json-file-using-jq
3203 jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' "$@"
3207 "$@" |& ts || return $?
3214 if $use_color && type -p tput &>/dev/null; then
3215 # this is nice for a dark background terminal:
3216 # https://github.com/trapd00r/LS_COLORS
3217 # I would like if there was something similar for light.
3219 # https://www.bigsoft.co.uk/blog/2008/04/11/configuring-ls_colors
3220 # change the hard to read turqouise.
3221 # defaults dircolors --print-database.
3223 # the default bold green is too light.
3224 # this explains the codes: https://gist.github.com/thomd/7667642
3225 export LS_COLORS="ex=1:ln=00;31"
3227 term_bold="$(tput bold)"
3228 term_red="$(tput setaf 1)"
3229 term_green="$(tput setaf 2)"
3230 # shellcheck disable=SC2034 # expected
3231 term_yellow="$(tput setaf 3)"
3232 term_purple="$(tput setaf 5)"
3233 term_nocolor="$(tput sgr0)" # no font attributes
3235 # unused so far. commented for shellcheck
3236 # term_underl="$(tput smul)"
3237 # term_blue="$(tput setaf 4)"
3238 # term_cyan="$(tput setaf 6)"
3240 # Try to keep environment pollution down, EPA loves us.
3241 unset safe_term match_lhs use_color
3246 if [[ $- == *i* ]]; then
3251 if [[ $EUID == 1000 ]]; then
3258 # this needs to come before next ps1 stuff
3259 # this stuff needs bash 4, feb 2009,
3260 # old enough to no longer condition on $BASH_VERSION anymore
3264 if [[ $- == *i* ]] && [[ ! $LC_INSIDE_EMACS ]]; then
3266 bind -m vi-command B:shell-backward-word
3267 bind -m vi-command W:shell-forward-word
3270 if [[ $SSH_CLIENT || $SUDO_USER ]]; then
3271 unset PROMPT_DIRTRIM
3275 # emacs terminal has problems if this runs slowly,
3276 # so I've thrown a bunch of things at the wall to speed it up.
3278 local return=$? # this MUST COME FIRST
3280 # all usable colors:
3282 # green nonzero exit (pri 1)
3285 # red pwd different owner & group & not writable (pri 2)
3286 # red bold pwd different owner & group & writable (pri 2)
3289 local ps_char ps_color
3292 if [[ $HISTFILE ]]; then
3293 history -a # save history
3296 ps_color="$term_purple"
3298 if [[ ! -O . ]]; then # not owner
3299 if [[ -w . ]]; then # writable
3300 ps_color="$term_bold$term_red"
3302 ps_color="$term_red"
3306 if [[ $return != 0 ]]; then
3307 ps_color="$term_green"
3308 ps_char="$return \\$"
3311 # faster than sourceing the file im guessing
3312 if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then
3313 eval "$(< /dev/shm/iank-status)"
3315 if [[ $MAIL_HOST && $MAIL_HOST != "$HOSTNAME" ]]; then
3316 ps_char="@ $ps_char"
3319 if [[ $(jobs -p) ]]; then
3324 # allow a function to specify a command to run after we run the next
3325 # command. Use case: a function makes a persistent notification. If
3326 # we happen to be using that terminal, we can just keep working by
3327 # entering our next command, even a noop in order to dismiss the
3328 # notification, instead of having to explicitly dismiss it.
3329 if [[ ${_psrun[*]} ]]; then
3330 if (( _psrun_count >= 1 )); then
3336 _psrun_count=$(( _psrun_count + 1 ))
3342 # We could test if sudo is active with sudo -nv
3343 # but then we get an email and log of lots of failed sudo commands.
3344 # We could turn those off, but seems better not to.
3345 if [[ $EUID != 0 ]] && [[ $DID_SUDO ]]; then
3346 psudo="\[$term_bold$term_red\]s\[$term_nocolor\] "
3348 if [[ ! $HISTFILE ]]; then
3349 ps_char="NOHIST $ps_char"
3351 PS1="${PS1%"${PS1#*[wW]}"} $jobs_char$psudo\[$ps_color\]$ps_char\[$term_nocolor\] "
3353 # copy of what is automatically added by guix.
3354 # adds [env] to PS1 if GUIX_ENVIRONMENT is set and PS1 contains '$';
3355 if [ -n "$GUIX_ENVIRONMENT" ]; then
3356 if [[ $PS1 =~ (.*)"\\$" ]]; then
3357 PS1="${BASH_REMATCH[1]} [env]\\\$ "
3362 # set titlebar. instead, using more advanced
3364 #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007"
3366 PROMPT_COMMAND=(prompt-command)
3368 if [[ $TERM == screen* ]]; then
3369 _title_escape="\033]..2;"
3371 # somme sites recommend this, i dunno what the diff is.
3372 #_title_escape="\033]30;"
3373 _title_escape="\033]0;"
3376 # make the titlebar be the last command and the current directory.
3377 auto-window-title () {
3380 # These are some checks to help ensure we dont set the title at
3381 # times that the debug trap is running other than the case we
3382 # want. Some of them might not be needed.
3383 if (( ${#FUNCNAME[@]} != 1 || ${#BASH_ARGC[@]} != 2 || BASH_SUBSHELL != 0 )); then
3386 if [[ $1 == prompt-command ]]; then
3389 echo -ne "$_title_escape ${PWD/#$HOME/~} "
3394 # note, this wont work:
3395 # x=$(mktemp); cp a $x
3396 # I havnt figured out why, bigger fish to fry.
3399 # condition from the screen man page i think.
3400 # note: duplicated in tx()
3401 if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
3402 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
3409 # * stuff that makes sense to be at the end
3415 if [[ -s "$HOME/.rvm/scripts/rvm" ]]; then
3416 # shellcheck disable=SC1091
3417 source "$HOME/.rvm/scripts/rvm"
3420 # I had this idea to start a bash shell which would run an initial
3421 # command passed through this env variable, then continue on
3422 # interactively. But the use case I had in mind went away.
3424 # if [[ $MY_INIT_CMD ]]; then
3425 # "${MY_INIT_CMD[@]}"
3429 # ensure no bad programs appending to this file will have an affect