2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
9 # Copyright 2024 Ian Kelling
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
15 # http://www.apache.org/licenses/LICENSE-2.0
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
23 # this gets sourced. shebang is just for file mode detection
25 # Use source ~/.bashrc instead of doing bash -l when running a script
26 # so this can set extdebug and avoid the bash debugger.
29 if [[ -s /a
/bin
/bash-bear-trap
/bash-bear
]]; then
30 # shellcheck source=/a/bin/bash-bear-trap/bash-bear
31 source /a
/bin
/bash-bear-trap
/bash-bear
32 # wtf, shellcheck doesn't allow disabling warnings in elifs
34 # bleh shellcheck can't handle disabling in an elif, so nesting this if.
35 # shellcheck disable=SC2154 # set in .bashrc
36 if [[ -s $bashrc_dir/bash-bear
]]; then
37 # shellcheck source=/a/bin/bash-bear-trap/bash-bear
38 source $bashrc_dir/bash-bear
42 # In t8, it runs clear_console for login shells by default. I don't want
43 # my console cleared. And linux ttys get cleared without this.
44 if shopt login_shell
>/dev
/null
&& [[ -e ~
/.bash_logout
]]; then
48 # if [[ -s /usr/share/bash-completion/completions/git ]]; then
49 # source /usr/share/bash-completion/completions/git
51 # if [[ -s /usr/share/bash-completion/completions/gitk ]]; then
52 # source /usr/share/bash-completion/completions/gitk
55 # for testing error catching:
71 # remove all aliases. aliases provided by the system tend to get in the way,
72 # for example, error happens if I try to define a function the same name as an alias
75 # remove gnome keyring warning messages
76 # there is probably a more proper way, but I didnt find any easily on google
77 # now using xfce+xmonad instead of vanilla xmonad, so disabling this
78 #unset GNOME_KEYRING_CONTROL
80 # use extra globing features.
82 # include .files when globbing, but ignore files name . and ..
83 # setting this also sets dotglob.
84 export GLOBIGNORE
="*/.:*/.."
86 # Useful info. see man bash.
90 # broken with bash_completion package. Saw a bug for this once. dont anymore.
91 # still broken in wheezy
92 # still buggered in latest stable from the web, version 2.1
93 # perhaps its fixed in newer git version, which fails to make for me
94 # this note is from 6-2014.
95 # still broken in flidas.
98 # make tab on an empty line do nothing
99 shopt -s no_empty_cmd_completion
101 # fix spelling errors for cd, only in interactive shell
103 # append history instead of overwritting it
105 # for compatibility, per gentoo/debian bashrc
106 shopt -s checkwinsize
107 # attempt to save multiline single commands as single history entries.
114 if [[ $LC_INSIDE_EMACS ]]; then
115 # EMACS is used by bash on startup, but we dont need it anymore.
116 # plus I hit a bug in a makefile which inherited it
118 export LC_INSIDE_EMACS
121 # scp completion does not work, but this doesnt fix it. todo, figure this out
122 #complete -r scp &> /dev/null
123 # todo, remote file completion fails, figure out how to turn it off
124 export NODE_DISABLE_COLORS
=1
125 # This gets rid of ugly terminal escape chars in node repl
126 # sometime, Id like to have completion working in emacs shell for node
127 # the offending chars can be found in lib/readline.js,
128 # things that do like:
129 # stream.write('\x1b[' + (x + 1) + 'G');
130 # We can remove them and keep readline, for example by doing this
132 #!/usr/bin/env nodejs
133 # var readline = require('readline');
134 # readline.cursorTo = function(a,b,c) {};
135 # readline.clearScreenDown = function(a) {};
136 # const repl = require('repl');
137 # var replServer = repl.start('');
139 # no prompt, or else readline complete seems to be confused, based
140 # on our column being different? node probably needs to send
141 # different kind of escape sequence that is not ugly. Anyways,
142 # completion doesnt work yet even with the ugly prompt, so whatever
144 export NODE_NO_READLINE
=1
148 export SSH_CONFIG_FILE_OVERRIDE
=/root
/.ssh
/confighome
152 # emacs has a different default search path than the info command. This
153 # adds the info defaults to emacs. This is commented because after
154 # various upgrades this is no longer a problem: for the directories that
155 # exist on my system, emacs already includes the ones that info
158 # but not the reverse, because I dun
159 # care much about the cli. The search path is only on the cli if you run
160 # "info xxx", or in emacs if you run '(info xxx)', so not that
161 # important and i don't bother fixing it.
163 # # info info says this path is what was compiled, and its not documented
164 # # anywhere. Through source grepping, i found it in files.h of the info
165 # # source in trisquel flidas.
167 # # Trailing : means for emacs to add its own stuff on to the end.
169 # # A problem with this is that directories which are not readable breaks info. And of course, this hard coding is not nice.
170 # # I removed PATH from the start, because I've never seen an info file in PATH. And removed ".", because I can just specify the full file name in that case.
172 # # https://raw.githubusercontent.com/debian-tex/texinfo/master/info/filesys.h
175 # # note: to split up the var like this, do:
176 # # IFS=:; printf '%s\n' $INFOPATH
181 # /usr/local/lib/info
183 # /usr/local/gnu/info
184 # /usr/local/gnu/lib/info
189 # /usr/share/lib/info
190 # /usr/local/share/info
191 # /usr/local/share/lib/info
192 # /usr/gnu/lib/emacs/info
193 # /usr/local/gnu/lib/emacs/info
194 # /usr/local/lib/emacs/info
195 # /usr/local/emacs/info
198 # for d in ${dirs[@]}; do
199 # if [[ -r $d ]]; then
200 # INFOPATH="$d:$INFOPATH"
206 # note: guix bash config does this automatically.
207 if [[ $INFOPATH != *: ]]; then
208 INFOPATH
="$INFOPATH:"
211 # info parameter expansion
215 # / search, {, }: next/prev match
216 # ctrl/alt-v scroll forward/backward within this node
217 # l: go to previous node
220 info bash
'Basic Shell Features' 'Shell Expansions' 'Shell Parameter Expansion'
224 # for openwrt system that has no stty, this is easier than
225 # guarding every time i use it.
226 if ! type -p stty
>/dev
/null
; then
232 if [[ $
- == *i
* ]]; then
233 # for readline-complete.el
234 if [[ $LC_INSIDE_EMACS ]]; then
235 # all for readline-complete.el
237 bind 'set horizontal-scroll-mode on'
238 bind 'set print-completions-horizontally on'
239 bind '"\C-i": self-insert'
243 if [[ $TERM != dumb
]] && test -t 1; then
247 # todo: not sure this works in sakura
249 #bind "\C-w": kill-region
250 # sakura == xterm-256color
252 if [[ $TERM == xterm
* ]]; then
253 # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
254 bind '"\e[1;5C": shell-forward-word' 2>/dev
/null
255 bind '"\e[1;5D": shell-backward-word' 2>/dev
/null
257 # make ctrl-backspace work. for konsole, i fixed it through
258 # /home/iank/.local/share/konsole/default.keytab
260 bind '"\eOc": shell-forward-word'
261 bind '"\eOd": shell-backward-word'
263 # i cant remember why i did this, probably to free up some keys to bind
264 # to other things in bash.
265 # other than C-c and C-z, the rest defined by stty -a are, at least in
266 # gnome-terminal, overridden by bash, or disabled by the system
267 stty lnext undef stop undef start undef
272 export BC_LINE_LENGTH
=0
275 export PROFILE_TASKS_TASK_OUTPUT_LIMIT
=100
277 # note, if I use a machine I dont want files readable by all users, set
278 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
280 # i for insensitive. the rest from
281 # X means dont remove the current screenworth of output upon exit
282 # R means to show colors n things
283 # a useful flag is -F aka --quit-if-one-screen
285 export SYSTEMD_LESS
=$LESS
288 export NNN_COLORS
=2136
290 export SL_FILES_DIR
=/b
/ds
/sl
/.iank
291 export SL_INFO_DIR
=/p
/sshinfo
296 # this is adapted from things printed to term after install
297 # pyenv. commented for now since I'm not actually using pyenv.
299 # export PYENV_ROOT="$HOME/.pyenv"
300 # command -v pyenv &>/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
301 # command -v pyenv &>/dev/null && eval "$(pyenv init -)"
304 # output showed this example for pyenv-virtualenv, which i have no idea
305 # what it is, but leaving it as a comment in case I end up doing python
308 #eval "$(pyenv virtualenv-init -)"
309 ### end begin pyenv ###
315 if [[ -s $bashrc_dir/path-add-function
]]; then
316 source $bashrc_dir/path-add-function
317 if [[ $SSH_CLIENT ]]; then
322 # if someone exported $SOE (stop on error), catch errors.
324 # Note, on debian this results in the following warning when in ssh,
325 # hich I haven't figured out how to fix. It doesn't happen if we source
326 # after the shell has started
328 # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory
329 # bash: warning: cannot start debugger; debugging mode disabled
331 if [[ -e /a
/bin
/bash-bear-trap
/bash-bear
]]; then
332 source /a
/bin
/bash-bear-trap
/bash-bear
337 path-add
--ifexists /usr
/local
/go
/bin
345 if [[ -s $path ]]; then
346 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
348 elif [[ -s $bashrc_dir/$file ]]; then
349 # shellcheck disable=SC1090 # this is dynamic, shellcheck can't follow it.
350 source $bashrc_dir/$file
355 mysrc
/a
/bin
/small-misc-bash
/ll-function
356 mysrc
/a
/bin
/distro-functions
/src
/package-manager-abstractions
358 # things to remember:
359 # ALT-C - cd into the selected directory
360 # CTRL-T - Paste the selected file path into the command line
362 # good guide to some of its basic features is the readme file
363 # https://github.com/junegunn/fzf
365 # if [[ -s /usr/share/doc/fzf/examples/key-bindings.bash ]]; then
366 # source /usr/share/doc/fzf/examples/key-bindings.bash
372 # temporary functions
374 m
"${@//spring/fall}"
377 e
"${@//spring/fall}"
381 ### begin FSF section ###
383 # Comments before functions are meant to be good useful
384 # documentation. If they fail at that, please improve them or send Ian a
387 ## copy bash completion
389 # It copies how the bash completion works from one command to other
390 # commands. Generally just use within a .bashrc.
392 # Usage: ORIGINAL_COMMAND TARGET_COMMAND...
398 if ! c
=$
(complete
-p $src 2>/dev
/null
); then
399 _completion_loader
$src &>/dev
/null ||
:
400 c
=$
(complete
-p $src 2>/dev
/null
) ||
return 0
408 ## BEGIN functions to change directory better than cd ##
412 # c: acts like cd, but stores directory history: you could alias to cd if you wanted.
415 # cl: list recent directories and optionally choose one.
417 # Finer details you may want to skip:
419 # bl: print the list of back and forward directories.
421 # We keep 2 stacks of directories, forward and back. Unlike with a web
422 # browser, the forward stack is not erased when going somewhere new.
424 # Recent directories are stored in ~/.cdirs.
426 declare -a _dir_forward _dir_back
428 # normally, the top of _dir_back is our current dir. if it isn't,
429 # put it on there, except we don't want to do that when we
430 # just launched a shell
431 if [[ $OLDPWD ]]; then
432 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
437 if (( ${#_dir_back[@]} == 0 )) ||
[[ ${_dir_back[-1]} != "$PWD" ]]; then
440 echo "$PWD" >> ~
/.cdirs
447 if (( ${#_dir_back[@]} == 0 )); then
448 echo "nothing left to go back to" >&2
451 top_back
="${_dir_back[-1]}"
453 if [[ $top_back == "$PWD" ]] && (( ${#_dir_back[@]} == 1 )); then
454 echo "already on last back entry" >&2
459 if [[ $top_back == "$PWD" ]]; then
460 # add to dirf if not already there
461 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$top_back" ]]; then
462 _dir_forward
+=("$top_back")
464 unset "_dir_back[-1]"
465 command cd "${_dir_back[-1]}"
467 if (( ${#_dir_forward[@]} == 0 )) ||
[[ ${_dir_forward[-1]} != "$PWD" ]]; then
468 _dir_forward
+=("$PWD")
470 command cd "$top_back"
473 # Interesting feature, not sure I want it.
474 # give us a peek at what is next in the list
475 # if (( ${#_dir_back[@]} >= 2 )); then
476 # printf "%s\n" "${_dir_back[-2]}"
480 # c/b/f Implementation notes:
482 # The top of the back is $PWD
483 # as long as the last directory change was due to c,b,or cl.
485 # Example of stack changes:
511 if (( ${#_dir_forward[@]} == 0 )); then
512 echo "no forward dir left" >&2
515 top_forward
="${_dir_forward[-1]}"
516 unset "_dir_forward[-1]"
519 # give us a peek at what is next in the list
520 # if (( ${#_dir_forward[@]} )); then
521 # printf "%s\n" "${_dir_forward[-1]}"
526 local i line input start
527 local -A buttondirs alines
528 local -a buttons
dirs lines
529 buttons
=( {a..z
} {2.
.9} )
530 if [[ ! -s ~
/.cdirs
]]; then
531 echo nothing
in ~
/.cdirs
537 mapfile
-t lines
<~
/.cdirs
538 start
=$
(( ${#lines[@]} - 1 ))
540 # we have ~33 buttons as of this writing, so lets
541 # prune down the history every once in a while.
542 if (( start
> 500 )); then
543 tac ~
/.cdirs |
awk '!seen[$0]++' |
head -n 200 |
tac | sponge ~
/.cdirs ||
[[ $?
== 141 ]]
546 for (( j
=start
; j
>= 0; j--
)); do
548 if [[ ! $line ||
${alines[$line]} ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
552 buttondirs
[${buttons[i]}]="$line"
553 printf "%s %s\n" ${buttons[i]} "$line"
554 # the LINES bit is for when we have a short terminal, just dont print all
555 # the directories. alternative would be to do something like less the list.
556 if (( i
== ${#buttons[@]} - 1 )) ||
{ [[ $LINES ]] && (( i
== LINES
- 3 )); }; then
562 if (( i
== 0 )); then
563 echo "no dirs in ~/.cdirs"
567 if [[ $input != $
'\n' ]]; then
568 c
"${buttondirs[$input]}"
571 # bl = back list. lists the back and forward directories. i tend to
572 # forget this exists and use cl instead.
576 start
=$
(( ${#_dir_back[@]} - 1 ))
578 # cleanup possible repeating of pwd
579 if (( start
>= 0 )) && [[ ${_dir_back[$start]} == "$PWD" ]]; then
580 start
=$
(( start
- 1 ))
583 if (( start
>= 0 )); then
584 for (( i
=start
; i
>= 0 ; i--
)); do
585 printf "%s %s\n" $j ${_dir_back[i]}
587 if (( j
>= max
)); then
594 start
=$
(( ${#_dir_forward[@]} - 1 ))
596 # cleanup possible repeating of pwd
597 if (( start
>= 0 )) && [[ ${_dir_forward[$start]} == "$PWD" ]]; then
598 start
=$
(( start
- 1 ))
600 if (( start
< 0 )); then
605 for (( i
=start
; i
>= 0 ; i--
)); do
606 printf "%s %s\n" $j ${_dir_forward[i]}
608 if (( j
>= max
)); then
613 # like running cl <enter> a <enter>
616 mapfile
-t lines
<~
/.cdirs
617 start
=$
(( ${#lines[@]} - 1 ))
618 for (( j
=start
; j
>= 0; j--
)); do
620 if [[ ! $line ||
! -d "$line" ||
$line == "$PWD" || line
== "$HOME" ]]; then
628 ## END functions to change directory better than cd ##
630 # pee do. run args as a command with output copied to syslog.
632 # Usage: pd [-t TAG] COMMAND...
634 # -t TAG Override the tag in the syslog. The default is COMMAND with
635 # any path part is removed, eg. for /bin/cat the tag is cat.
637 # You can view the log via "journalctl -t TAG"
643 -t) tag
="$2"; shift 2 ;;
645 echo "PWD=$PWD command: $*" | logger
-t $tag
646 "$@" |
& pee
cat "logger -t $tag" || ret
=$?
647 echo "exited with status=$ret" | pee
cat "logger -t $tag"
648 # this avoids any err-catch
649 (( ret
== 0 )) ||
return $ret
653 # jdo = journal do. Run command as transient systemd service, tailing
654 # its output in the journal until it completes.
656 # Usage: jdo COMMAND...
658 # Compared to pd: commands recognize this is a non-interactive shell.
659 # The service is unaffected if our ssh connection dies, no need to run
662 # Note: The last few lines of any existing entries for a unit by that
663 # name will be output first, and there will be a few second delay at the
664 # start of the command, and a second or so at the end.
666 # Note: Functions and aliases obviously won't work, we resolve the
669 # Note: requires running as root.
671 local cmd cmd_name jr_pid ret
675 if [[ $EUID != 0 ]]; then
676 echo "jdo: error: rerun as root"
680 if [[ $cmd != /* ]]; then
681 cmd
=$
(type -P "$cmd")
684 journalctl
-qn2 -f -u "$cmd_name" &
686 # Trial and error of time needed to avoid missing initial lines.
687 # .5 was not reliable. 1 was not reliable. 2 was not reliable
689 systemd-run
--unit "$cmd_name" --wait --collect "$cmd" "$@" || ret
=$?
690 # The sleep lets the journal output its last line
691 # before the prompt comes up.
693 kill $jr_pid &>/dev
/null ||
:
696 # this avoids any err-catch
697 (( ret
== 0 )) ||
return $ret
701 # standard date as used in logs
706 # date in log appropriate format
713 command ts
"%F %T" "$@"
716 # ts log. log command to log file.
717 # usage: tsl LOG_PATH_PREFIX COMMAND...
718 # example: tsl /root/command
719 # log file will be like /root/command-2024-02-10.log
721 local log_prefix log_path appending ret
722 if (( $# < 2 )); then
723 echo "tsl: error: expected >= 2 arguments, got $#" >&2
727 if [[ $log_prefix == */* && ! -d ${log_prefix%*/} ]]; then
728 echo "tsl: error: expected directory at ${log_prefix%*/}" >&2
731 log_path
=$log_prefix-$
(date +%Y-
%m-
%d
).log
733 if [[ -s $log_path ]]; then
737 printf "%s\n" "CWD: $PWD, log: $log_path, running $*" | ts
"%F %T" |
tee -a "$log_path"
739 "$@" |
& ts
"%F %T" |
tee -a "$log_path" || ret
=$?
740 printf "%s\n" "exit code $ret from command: $*" | ts
"%F %T" |
tee -a "$log_path"
742 printf "%s\n" "note: this log file contains logs before those of previous command" | ts
"%F %T" |
tee -a "$log_path"
748 mapfile
-t cmds
<<'EOF'
749 tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab
752 ls -la /dev/disk/by-id
755 for cmd
in "${cmds[@]}"; do
772 local ip port xoffset
773 read -r ip port xoffset
<<<"$@"
777 if [[ ! $port ]]; then
782 # By default, plugged in screen goes to the right side, so we need an
783 # offset that is the same as the laptop's x resolution. If we are in
784 # mirror mode, then we don't need an offset.
785 if [[ ! $xoffset ]]; then
787 laptop_x
=$
(xrandr |
awk '$1 == "LVDS-1" {print $4}' |
sed 's/x.*//') ||
{ sleep 1; continue; }
788 total_x
=$
(xdpyinfo|
awk '$1 == "dimensions:" {print $2}' |
sed 's/x.*//') ||
{ sleep 1; continue; }
789 screen2_res
=$
(xrandr |
awk '$2 == "connected" && $1 != "LVDS-1" { print $3 }' |
sed 's/+.*//')
790 if (( laptop_x
< total_x
)); then
795 m ffmpeg
-probesize 50M
-thread_queue_size 50 \
796 -video_size $screen2_res -f x11grab
-framerate 30 -i :0.0+$xoffset.0 \
797 -vcodec libx264
-g 1 -tune zerolatency
-preset ultrafast
-pix_fmt yuv420p
-x264-params repeat-headers
=1 \
798 -f rtp_mpegts rtp
://$ip:$port ||
:
806 if [[ ! $DISPLAY ]]; then
809 if [[ ! $XAUTHORITY ]]; then
810 export XAUTHORITY
=$HOME/.Xauthority
819 ....
() { c ..
/..
/..
; }
820 .....
() { c ..
/..
/..
/..
; }
821 ......
() { c ..
/..
/..
/..
/..
; }
826 path
=$
(readlink
-e "$f")
827 echo "cat >$path <<'EOF'"
834 # file cut copy and paste, like the text buffers :)
835 # I havnt tested these.
836 _fbufferinit
() { # internal use
837 ! [[ $my_f_tempdir ]] && my_f_tempdir
="$(mktemp -d)"
838 rm -rf "${my_f_tempdir:?}"/*
842 cp "$@" "$my_f_tempdir"/
846 mv "$@" "$my_f_tempdir"/
848 fpst
() { # file paste
849 [[ $2 ]] && { echo too many arguments
; return 1; }
851 cp "$my_f_tempdir"/* "$target"
855 local host ip port
file key tmp ssh_host
alias
860 # note ":graph:" is needed or else we get a trailing \r out of ssh,
861 # dunno why. web search says terminals add \r, so I tried adding -T
862 # to turn off psuedo terminal, but it didnt help.
863 } < <(timeout
-s 9 2 ssh -TN -oBatchMode=yes -oControlMaster=no
-oControlPath=/ -v $ssh_host |
&
864 sed -rn "s/debug1: Connecting to ([^ ]+) \[([^\]*)] port ([0-9]+).*/\1 \2 \3/p;
865 s/^debug1: using hostkeyalias: ([[:graph:]]*).*/\1/p" ||
: )
866 file=$
(readlink
-f ~
/.ssh
/known_hosts
)
868 echo "khfix: ssh failed"
873 if [[ $alias ]]; then
876 if [[ $port != 22 ]]; then
877 ip_entry
="[$ip]:$port"
878 if [[ ! $alias ]]; then
879 host_entry
="[$host]:$port"
882 if [[ $host_entry != "$ip_entry" ]]; then
884 ssh-keygen
-F "$host_entry" -f $file >$tmp ||
[[ $?
== 1 ]] # 1 when it doesnt exist in the file
885 if [[ -s $tmp ]]; then
886 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
888 echo "khfix WARNING: did not find host entry:$host_entry in known_hosts"
892 grep -Fv "$key" "$file" | sponge
"$file"
897 ssh-keygen
-F "$ip_entry" -f $file >$tmp ||
[[ $?
== 1 ]]
898 if [[ -s $tmp ]]; then
899 key
=$
(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp)
901 echo "khfix WARNING: did not find ip entry:$ip_entry in known_hosts"
905 grep -Fv "$key" "$file" | sponge
"$file"
908 khfix-r
() { # known hosts fix without syncing to root user
909 _khfix-common
"$@" ||
return 1
913 _khfix-common
"$@" ||
return 1
918 # copy path into clipboard
921 x
=$
(readlink
-nf "${1:-$PWD}")
922 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
923 # And, summarizing this:
924 # https://askubuntu.com/questions/705620/xclip-vs-xsel
925 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
929 # clipboard a string (into selection & clipboard buffer)
931 # yes, its kinda dumb that xclip/xsel cant do this in one invocation.
932 # And, summarizing this:
933 # https://askubuntu.com/questions/705620/xclip-vs-xsel
934 # xclip has a few more options. xclip has a bug in tmux / forwarded x sessions.
935 printf "%s" "$*" | xclip
-selection clipboard
936 printf "%s" "$*" | xclip
939 # a1 = awk {print $1}
940 for field
in {1.
.20}; do
941 eval a
$field"() { awk '{print \$$field}'; }"
944 for num
in {1.
.9}; do
945 eval h
$num"() { head -n$num || [[ \$? == 141 ]]; }"
950 # shellcheck disable=SC2046 disable=SC2001 disable=SC2183 # hacks, expected
951 printf '%d.%d.%d.%d\n' $
(echo $1 |
sed 's/../0x& /g')
955 local f out outdir
in fname origdir skip1
959 while [[ $1 == -* ]]; do
961 # if we got interrupted after 1st phase
973 # first pass only uses about 1 cpu, so run in parallel
977 if [[ $f == /* ]]; then
982 out
="$origdir/$outdir/$fname"
983 mkdir
-p /tmp
/vp
9/$fname
985 if ! $skip1 && [[ ! -s ffmpeg2pass-0.log
]]; then
986 # -nostdin or else wait causes ffmpeg to go into stopped state. dunno why, random stackoverflow answer.
987 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
989 if [[ -e $out ]]; then rm -f $out; fi
990 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
997 utcl
() { # utc 24 hour time to local hour 24 hour time
998 echo "print( ($1 $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
1006 # for running in a fai rescue. iank specific.
1008 d
=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
1009 for f
in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V
; do
1010 cryptsetup luksOpen
--key-file /p
/dev
/$f/root crypt-
$f-root
1011 cryptsetup luksOpen
--key-file /p
/dev
/$f/o crypt-
$f-o
1013 mount
-o subvol
=root_trisquelaramo
/dev
/mapper
/crypt-
$d-root /mnt
1014 mount
-o subvol
=a
/dev
/mapper
/crypt-
$d-root /mnt
/a
1015 mount
-o subvol
=o
/dev
/mapper
/crypt-
$d-o /mnt
/o
1016 mount
-o subvol
=boot_trisquelaramo
/dev
/sda2
/mnt
/boot
1024 c4
() { c
/var
/log
/exim4
; }
1026 caa
() { git commit
--amend --no-edit -a; }
1039 find -L "$@" -type f
-not \
( -name .svn
-prune -o -name .git
-prune \
1040 -o -name .hg
-prune -o -name .editor-backups
-prune \
1041 -o -name .undo-tree-history
-prune \
) -printf '%h\0%d\0%p\n' |
sort -t '\0' -n \
1042 |
awk -F '\0' '{print $3}' 2>/dev
/null |
while read -r file; do
1045 # if the file is nonempty and the last char is nonempty, it is not
1046 # newline terminated.
1047 if [[ -s "$file" && "$(tail -c 1 "$file")" ]]; then
1054 calc
() { echo "scale=3; $*" |
bc -l; }
1055 # no having to type quotes, but also no command history:
1059 echo "scale=3; $x" |
bc -l
1070 ccat
() { # config cat. see a config without extra lines.
1071 sed -r '/^[[:space:]]*([;#]|--|\/\/|$)/d' "$@"
1077 # dev/pts needed for pacman signature check
1078 for d
in dev proc sys dev
/pts
; do
1080 if ! mountpoint
$d &>/dev
/null
; then
1081 m s mount
-o bind /$d $d
1087 # dev/pts needed for pacman signature check
1088 for d
in dev
/pts dev proc sys
; do
1090 if mountpoint
$d &>/dev
/null
; then
1098 # join options which are continued to multiples lines onto one line
1100 while IFS
= read -r line
; do
1101 # remove leading spaces/tabs. assumes extglob
1102 if [[ $line == "[ ]*" ]]; then
1103 line
="${line##+( )}"
1108 elif [[ $line == *=* ]]; then
1109 echo "$pastline" >> "$2"
1112 pastline
="$pastline $line"
1114 done < <(grep -vE '^([ \t]*#|^[ \t]*$)' "$1")
1115 echo "$pastline" >> "$2"
1119 # diff config files,
1120 # setup for format of postfix, eg:
1123 local pastline unified f1 f2
1127 _cdiff-prep
"$1" "$f1"
1128 _cdiff-prep
"$2" "$f2"
1129 cat "$f1" "$f2" |
grep -Po '^[^=]+=' |
sort |
uniq > "$unified"
1130 while IFS
= read -r line
; do
1131 # the default bright red / blue doesnt work in emacs shell
1132 dwdiff
-cblue,red
-A best
-d " ," <(grep "^$line" "$f1" ||
echo ) <(grep "^$line" "$f2" ||
echo ) | colordiff
1138 local start
=$SECONDS
1140 # shellcheck disable=SC2030
1141 inotifywait
-m "$dir" -e create
-e moved_to | \
1142 while read -r filedir _
file; do
1145 calc $
((SECONDS
- start
)) / 60
1152 s chown
-R $USER:$USER "$@"
1155 # shellcheck disable=SC2032
1157 # makes it so chown -R symlink affects the symlink and its target.
1158 if [[ $1 == -R ]]; then
1160 command chown
-h "$@"
1161 command chown
-R "$@"
1172 d
() { builtin bg "$@"; }
1175 # f would be more natural, but i already am using it for something
1176 z
() { builtin fg "$@"; }
1179 x
() { builtin kill %%; }
1182 diff --strip-trailing-cr -w "$@" # diff content
1190 safe_rename
"$x" "$y"
1195 # usage: dfp MOUNTPOINT [SECOND_INTERVAL]
1196 # SECOND_INTERVAL defaults to 90
1199 local a b mp interval
1202 if [[ ! $mp ]]; then
1203 echo "dfp: error, missing 1st arg" >&2
1207 a
=$
(df
--output=used
$mp |
tail -n1)
1209 b
=$
(df
--output=used
$mp |
tail -n1)
1210 printf "used mib: %'d mib/min: %s\n" $
(( b
/1000 )) $
(( (b-a
) / (interval
* 1000 / 60 ) ))
1214 # get ipv4 ip from HOST. or if it is already a number, return that
1222 getent ahostsv4
"$host" |
awk '{ print $1 }' |
head -n1
1228 command dig +nostats
+nocmd
"$@"
1230 # Output with sections sorted, and removal of query id, so 2 dig outputs can be diffed.
1234 dig +nordflag
"$@" |
sed -r 's/^(;; ->>HEADER<<-.*), id: .*/\1/' |
while read -r l
; do
1235 if [[ $l == [^\
;]* ]]; then
1239 printf "%s" "$sec" |
sort
1247 # compare digs to the 2 servers
1248 # usage: digdiff @server1 @server2 DIG_ARGS
1249 # note: only the soa master nameserver will respond with
1250 # ra "recursive answer" flag. That difference is meaningless afaik.
1257 digsort
$s1 "$@" |
tee /tmp
/digdiff
1258 diff -u /tmp
/digdiff
<(digsort
$s2 "$@")
1261 # date in a format i like reading
1263 date "+%A, %B %d, %r" "$@"
1268 # date with all digits in a format i like
1272 ccomp
date dt dtr dtd
1274 dus
() { # du, sorted, default arg of
1275 du
-sh ${@:-*} |
sort -h
1280 e
() { printf "%s\n" "$*"; }
1288 printf "%qEOL\n" "${arg}"
1289 printf "%s" "${arg}" |
& hexdump -C
1293 # echo variables. print var including escapes, etc, like xxd for variable
1299 if [[ -v $arg ]]; then
1300 printf "%qEOL\n" "${!arg}"
1301 printf "%s" "${!arg}" |
& hexdump -C
1303 echo arg
$arg is
unset
1309 [[ ${#@} == 2 ]] ||
{ echo "error: ediff requires 2 arguments"; return 1; }
1310 emacs
--eval "(ediff-files \"$1\" \"$2\")"
1314 # shellcheck disable=SC2120 # we expect to pass arguments in use outside this file
1317 tail -F /var
/log
/exim
4/mainlog
/var
/log
/exim
4/*main
/var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
-n 200 "$@"
1321 tail -F /var
/log
/exim
4/mainlog
-n 200 "$@"
1324 tail -F /var
/log
/exim
4/nondmain
-n 200 "$@"
1326 # shortcut for tail -F
1330 ccomp
tail etail etail2 ta
1332 # ran into this online, trying it out
1334 ( "$@" &>/dev
/null
& disown )
1338 ssh "$@" cat .ssh
/authorized_keys
{,2}
1342 # print exim old pids
1344 local configtime pid piduptime now daemonpid
1345 printf -v now
'%(%s)T' -1
1346 configtime
=$
(stat
-c%Y
/var
/lib
/exim
4/config.autogenerated
)
1347 if [[ -s /run
/exim
4/exim.pid
]]; then
1348 daemonpid
=$
(cat /run
/exim
4/exim.pid
)
1350 for pid
in $
(pgrep
-f '^/usr/sbin/exim4( |$)'); do
1351 # the daemonpid gets reexeced on HUP (service reloads), keeping its same old timestamp
1352 if [[ $pid == "$daemonpid" ]]; then
1355 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
1356 if (( configtime
> now
- piduptime
)); then
1362 # exim tail but only watch lines from new pids
1365 for pid
in $
(eoldpids
); do
1368 if [[ $oldpids ]]; then
1369 etail |
awk '$3 !~ /^\[('"${oldpids%|}"')\]$/'
1374 # exim watch as old pids go away
1376 local configtime pid piduptime now tmpstr
1382 mapfile
-t oldpids
<<<"$tmpstr"
1383 if (( ! ${#oldpids[@]} )); then
1386 # print the date every 20 iterations
1387 if (( ! count
% 20 )); then
1391 ps
-f -p "${oldpids[*]}"
1397 less /var
/log
/exim
4/mainlog
1401 exiqgrep
-ir.\
* -o 60 |
while read -r i
; do
1404 hlm exigrep
$i /var
/log
/exim
4/mainlog |
cat ||
:
1408 # other ways to get the list of message ids:
1409 # exim -bp | awk 'NF == 4 {print $3}'
1410 # # this is slower 160ms, vs 60.
1412 exiqgrep
-ir.\
* |
xargs exim
-Mrm
1417 mkdir
-p /tmp
/edev
/etc
1418 cp -ra /etc
/exim4
/tmp
/edev
/etc
1419 cp -ra /etc
/alias* /tmp
/edev
/etc
1420 find /tmp
/edev
/etc
/exim4
-type f
-execdir sed -i "s,/etc/,/tmp/edev/etc/,g" '{}' +
1424 update-exim4.conf
-d /tmp
/edev
/etc
/exim4
-o /tmp
/edev
/e.conf
1428 # show important information about incoming mail in the exim log
1430 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' <${1:-/var/log/exim4/mainlog}
1433 # 2nd line is message-id:
1435 sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog}
1443 tail "${tail_arg[@]}" -F /var
/log
/exim
4/mainlog |
sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p'
1450 # find array. make an array of file names found by find into $x
1451 # argument: find arguments
1452 # return: find results in an array $x
1453 while read -rd ''; do
1455 done < <(find "$@" -print0);
1458 # shellcheck disable=SC2120
1459 faf
() { # find all files. use -L to follow symlinks
1460 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1461 -o -name .hg
-prune -o -name .editor-backups
-prune \
1462 -o -name .undo-tree-history
-prune \
) -type f
2>/dev
/null
1465 # usage ffconcat FILES_TO_CONCAT OUTPUT_FILE
1469 printf "file '%s'\n" "$1" >$tmpf
1470 while (( $# > 1 )); do
1472 printf "file '%s'\n" "$1" >>$tmpf
1474 # https://trac.ffmpeg.org/wiki/Concatenate
1475 ffmpeg
-f concat
-safe 0 -i $tmpf -c copy
"$1"
1480 if (( $# == 0 )); then
1481 echo ffremux error expected args
>&2
1486 tmpf
=$tmpd/"${f##*/}"
1487 ffmpeg
-i "$f" -c:v copy
-c:a copy
$tmpf
1495 # absolute path of file/dir without resolving symlinks.
1497 # Most of the time, I want this where I would normally use readlink.
1498 # This is what realpath -s does in most cases, but sometimes it
1499 # actually resolves symlinks, at least when they are in /.
1501 # Note, if run on a dir, if the final component is relative, it won't
1502 # resolve that. Use the below fpd for that.
1504 # note: we could make a variation of this which
1505 # assigns to a variable name using eval, so that we don't have to do
1506 # x=$(fp somepath), which might save subshell overhead and look nice,
1507 # but I'm not going to bother.
1509 local initial_oldpwd initial_pwd dir base
1510 initial_oldpwd
="$OLDPWD"
1512 if [[ $1 == */* ]]; then
1515 # CDPATH because having it set will cause cd to possibly print output
1517 printf "%s%s\n" "$PWD" "$base"
1518 CDPATH
='' cd "$initial_pwd"
1519 OLDPWD
="$initial_oldpwd"
1521 printf "%s/%s\n" "$PWD" "$1"
1524 # full path of directory without resolving symlinks
1526 local initial_oldpwd initial_pwd dir
1527 initial_oldpwd
="$OLDPWD"
1531 printf "%s%s\n" "$PWD" "$base"
1533 OLDPWD
="$initial_oldpwd"
1540 sudo mailq |gr frozen|
awk '{print $3}' |
while read -r id
; do
1546 echo -e '\n\n##############################\n'
1547 done |
tee -a /tmp
/frozen
1551 while read -r line
; do
1552 printf '%s\n' "$line"
1553 ids
+=("$(printf '%s\n' "$line" |gr frozen|awk '{print $3}')")
1555 echo "sleeping for 2 in case you change your mind"
1557 sudo exim
-Mrm "${ids[@]}"
1561 # like -e for functions. returns on error.
1562 # at the end of the function, disable with:
1564 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
1565 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
1571 local help="Usage: getdir [--help] PATH
1572 Output the directory of PATH, or just PATH if it is a directory."
1573 if [[ $1 == --help ]]; then
1577 if [[ $# -ne 1 ]]; then
1578 echo "getdir error: expected 1 argument, got $#"
1581 if [[ -d $1 ]]; then
1585 dir
="$(dirname "$1")"
1586 if [[ -d $dir ]]; then
1589 echo "getdir error: directory does not exist"
1595 git_empty_branch
() { # start an empty git branch. carefull, it deletes untracked files.
1596 [[ $# == 1 ]] ||
{ echo 'need a branch name!'; return 1;}
1598 root
=$
(gitroot
) ||
return 1 # function to set gitroot
1600 git symbolic-ref HEAD refs
/heads
/$1
1605 # shellcheck disable=SC2120
1607 local help="Usage: gitroot [--help]
1608 Print the full path to the root of the current git repo
1610 Handles being within a .git directory, unlike git rev-parse --show-toplevel,
1611 and works in older versions of git which did not have that."
1612 if [[ $1 == --help ]]; then
1617 p
=$
(git rev-parse
--git-dir) ||
{ echo "error: not in a git repo" ; return 1; }
1618 [[ $p != /* ]] && p
=$PWD
1623 # g pipe. like: cmd | emacs. save cmd output to tmp file, then edit.
1629 #like cmd &> tempfile; emacs tempfile
1631 # note: a useful workflow for doing mass replace on my files:
1633 ## remove any false positives, or manually edit them. rename files if needed.
1634 # sedi 's/REGEX/REPLACEMENT/' $(gr '^/' /a/tmp/gtmp)
1639 # g command substitution.
1641 # shellcheck disable=SC2046 # i want word splitting for this hackery
1645 # force terminal version
1651 # quit will prompt if the program crashes.
1652 gdb
-ex=r
-ex=quit
--args emacs
"$@"; r
;
1656 # kill the emacs daemon
1661 grep -iIP --color=auto
"$@" ||
return $?
1663 grr
() { # grep recursive
1664 # Don't return 1 on nonmatch because this is meant to be
1665 # interactive, not in a conditional.
1666 if [[ ${#@} == 1 ]]; then
1667 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -rniIP --color=auto
"$@" . ||
[[ $?
== 1 ]]
1669 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -rniIP --color=auto
"$@" ||
[[ $?
== 1 ]]
1677 # recursive everything. search for files/dirs and lines. rs = easy chars to press
1681 find "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1682 -o -name .hg
-prune -o -name .editor-backups
-prune \
1683 -o -name .undo-tree-history
-prune \
) 2>/dev
/null |
grep -iP --color=auto
"$query"
1687 # horizontal row. used to break up output
1689 local start end end_count arg
1690 # 180 is long enough. 5 for start.
1691 start
=█████ end
=█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
1692 end_count
=$
(( ${COLUMNS:-180} - 5 ))
1695 end_count
=$
(( end_count
- 2 - ${#arg} ))
1696 start
="$start $arg "
1698 if (( end_count
>= 1 )); then
1699 end
=${end:0:$end_count}
1703 printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)$start$end$(tput sgr0 2>/dev/null||:)"
1707 local col input_len
=0
1709 input_len
=$
((input_len
+ 1 + ${#arg}))
1711 col=$
((60 - input_len
))
1712 printf "\e[1;97;41m%s" "$*"
1713 if (( col > 0 )); then
1714 # shellcheck disable=SC2046 # needed to work as intended. a better way would be like hr above.
1715 printf "\e[1;97;41m \e[0m%.0s" $
(eval echo "{1..${col}}")
1719 hlm
() { hl
"$*"; "$@"; }
1721 hrcat
() { local f
; for f
; do [[ -f $f ]] ||
continue; hr
; echo "$f"; cat "$f"; done }
1724 # github-release-dl restic/restic restic_ _linux_amd64.bz2
1726 # https://github.com/restic/restic/releases/download/v0.16.3/restic_0.16.3_linux_amd64.bz2
1727 github-release-dl
() {
1728 local github_path file_prefix file_suffix latest_prefix version redir_path
1732 if (( $# != 3 )); then
1733 echo "$0: error, expected 3 arguments" >&2
1736 redir_path
="https://github.com/$github_path/releases/latest/download/"
1737 latest_prefix
=$
(curl
-s -I "$redir_path" |
awk 'tolower($1) == "location:" {print $2}')
1738 # it has a trailing /r at the end. just kill any whitespace.
1739 latest_prefix
="${latest_prefix//[$'\t\r\n ']}"
1740 if [[ ! $latest_prefix ]]; then
1741 echo "failed to find latest path. Tried to find case insensitive 'location:' in the curl output:"
1742 m curl
-s -I "$redir_path"
1745 version
="${latest_prefix##*/}"
1746 version
="${version#v}"
1747 m wget
-- "$latest_prefix/$file_prefix$version$file_suffix"
1751 # go-github-install restic/restic restic_ _linux_amd64.bz2
1752 # go-github-install restic/rest-server rest-server_ _linux_amd64.tar.gz
1754 # common pattern among go binaries on github
1755 go-github-install
() {
1756 local tmpd targetf tmp files src
1761 tmp
="${file_prefix##*[[:alnum:]]}"
1762 targetf
="${file_prefix%"$tmp"}"
1763 echo targetf
: $targetf
1764 github-release-dl
"$@"
1766 case $file_suffix in
1774 rm -f -- "${files[@]}"
1776 # Here we detect and handle 2 cases: either we extracted a single
1777 # binary which we have to rename or a folder with a binary named
1778 # $targetf in it which is all we care about.
1779 if (( ${#files[@]} == 1 )) && [[ -f ${files[0]} ]]; then
1781 mv -- .
/* /usr
/local
/bin
/$targetf
1783 files
=(.
/*/$targetf)
1784 if [[ -f $targetf ]]; then
1786 elif [[ -f ${files[0]} ]]; then
1790 mv -- "$src" /usr
/local
/bin
1796 ## 2024: I'm using gh instead of hub, but leaving this just in case.
1797 ## I tried the github cli tool (gh) and it seems easier than
1800 ## hub predated github's 2020 official cli tool gh.
1802 ## https://raw.githubusercontent.com/cli/cli/trunk/docs/gh-vs-hub.md
1803 # get latest hub and run it
1804 # main command to use:
1805 # hub pull-request --no-edit
1806 # --no-edit means to use the first commit\'s message as the pull request message.
1807 # If that fails, try doing
1808 # hub pull-request --no-edit -b UPSTREAM_OWNER:branch
1809 # where branch is usually master. it does the pr against your current branch.
1811 # On first use, you input username/pass and it gets an oath token so you dont have to repeat
1812 # it\'s at ~/.config/hub
1814 local up uptar updir p re
1815 # example https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz
1816 up
=$
(wget
-q -O- https
://api.github.com
/repos
/github
/hub
/releases
/latest | jq
-r .assets
[].browser_download_url |
grep linux-amd64
)
1818 if [[ ! $up ||
$up =~
$re ]]; then
1819 echo "failed to get good update url. got: $up"
1823 if [[ ! -e /a
/opt
/$updir ]]; then
1824 rm -rf /a
/opt
/hub-linux-amd64
*
1826 tar -C /a
/opt
-zxf /a
/opt
/$uptar
1829 if ! which hub
&>/dev
/null
; then
1830 sudo
/a
/opt
/$updir/install
1833 # save token across computers
1834 if [[ ! -L ~
/.config
/hub
]]; then
1835 if [[ -e ~
/.config
/hub
]]; then
1836 mv ~
/.config
/hub
/p
/c
/subdir_files
/.config
/
1838 if [[ -e /p
/c
/subdir_files
/.config
/hub
]]; then
1852 # cvs update -C FILE
1857 # potentially useful command translation
1858 # https://fling.seas.upenn.edu/~giesen/dynamic/wordpress/equivalent-commands-for-git-svn-and-cvs/
1860 # importing cvs repo into git using git-cvs package:
1861 # /f/www $ /usr/lib/git-core/git-cvsimport -C /f/www-git
1877 find -L "$@" -not \
( -name .svn
-prune -o -name .git
-prune \
1878 -o -name .hg
-prune -o -name .editor-backups
-prune \
1879 -o -name .undo-tree-history
-prune \
) -iname "*$glob*" 2>/dev
/null
1883 # insensitive find here. args are combined into the search string.
1884 # -L = follow symlinks
1885 find -L .
-not \
( -name .svn
-prune -o -name .git
-prune \
1886 -o -name .hg
-prune -o -name .editor-backups
-prune \
1887 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1891 # insensitive find directory
1892 find -L .
-type d
-not \
( -name .svn
-prune -o -name .git
-prune \
1893 -o -name .hg
-prune -o -name .editor-backups
-prune \
1894 -o -name .undo-tree-history
-prune \
) -iname "*$**" 2>/dev
/null
1899 sudo iptables
-A INPUT
-s $1 -j DROP
1904 grep -Il "" "$@" &>/dev
/null
1911 # journalctl with times in the format the --since= and --until= options accept
1912 jrt
() { journalctl
-e -n100000 -o short-full
"$@"; }
1913 jr
() { journalctl
-e -n100000 "$@" ; }
1914 jrf
() { journalctl
-n1000 -f "$@" ; }
1916 # the invocation id is "assigned each time the unit changes from an inactive
1917 # state into an activating or active state" man systemd.exec
1918 journalctl
-e --no-tail -u exim4 _SYSTEMD_INVOCATION_ID
="$(systemctl show -p InvocationID --value $1)"
1920 ccomp journalctl jr jrf jru
1925 if [[ $PWD == /[iap
] ]]; then
1926 command ls -A --color=auto
-I lost
+found
"$@"
1928 command ls -A --color=auto
"$@"
1932 lcn
() { locate -i "*$**"; }
1934 lg
() { LC_COLLATE
=C.UTF-8 ll
--group-directories-first "$@"; }
1936 lt
() { ll
-tr "$@"; }
1938 lld
() { ll
-d "$@"; }
1940 ccomp
ls l lg lt lld ll
1946 for dirs in false true
; do
1948 if [[ -d $f ]]; then
1950 # reverse the order to rename the nested dirs first.
1951 # note: 0 element is the dir itself
1952 for ((i
=${#all[@]}-1; i
>=1; i--
)); do
1954 if $dirs && [[ -d $a ]]; then
1955 # e dirs low "$a" # debug
1957 elif ! $dirs && [[ ! -d $a && -e $a ]]; then
1959 # e not dirs low "$a" # debug
1964 # just rename all the top level args on the second pass
1966 # e final dirs low "$f" # debug
1973 low
() { # make filenames lowercase, remove bad chars
1976 arg
="${arg%%+(/)}" # remove trailing slashes. assumes we have extglob on.
1978 if (( ${#dir} == ${#arg} )); then
1982 new
="${f,,}" # downcase
1983 # shellcheck disable=SC2031 # seems like a shellcheck bug
1984 new
="${new//[^a-zA-Z0-9._-]/_}" # sub bad chars
1985 new
="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
1986 new
="${new%"${new##*[[:alnum:]]}"}"
1987 # remove bad underscores, like __ and _._
1988 new
=$
(echo $new |
sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
1989 safe_rename
"$dir/$f" "$dir/$new" ||
return 1
1994 lower
() { # make first letter of filenames lowercase.
1997 if [[ ${x::1} == [A-Z
] ]]; then
1998 y
=$
(tr '[:upper:]' '[:lower:]' <<<"${x::1}")"${x:1}"
1999 safe_rename
"$x" "$y" ||
return 1
2005 k
() { # history search
2006 grep -iP --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
tail -n 80 ||
[[ $?
== 1 ]];
2008 ks
() { # history search with context
2009 # args are an extended regex used by sed
2010 history |
sed -nr "h;s/^\s*(\S+\s+){4}//;/$*/{g;p}" |
tail -n 80 ||
[[ $?
== 1 ]];
2012 ksu
() { # history search unique
2013 grep -P --binary-files=text
"$@" ${HISTFILE:-~/.bash_history} |
uniq ||
[[ $?
== 1 ]];
2016 # remove lines from history matching $1
2018 # todo: id like to do maybe a daily or hourly cronjob to
2019 # check that my history file size is increasing. Ive had it
2020 # inexplicably truncated in the past.
2023 HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/'
2024 read -r -p "press anything but contrl-c to delete"
2025 for entry
in $
(HISTTIMEFORMAT
='' history |
awk -v IGNORECASE
=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/ { print a }' |
tac); do
2031 # history without the date
2033 history "$@" | cut
-d' ' -f 7-
2036 ccomp
grep k ks ksu histrm
2040 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
2041 make -qp |
awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
2053 # mkdir the last arg, cp the rest into it
2056 cp "${@:1:$#-1}" "${@: -1}"
2060 mv "${@:1:$#-1}" "${@: -1}"
2063 mkt
() { # mkdir and touch file
2065 mkdir
-p "$(dirname "$path")"
2069 # shellcheck disable=SC2032
2070 mkdir
() { command mkdir
-p "$@"; }
2073 # https://github.com/HenriWahl/Nagstamon/issues/357
2074 if ! pgrep
-f /usr
/bin
/dunst
>/dev
/null
; then
2077 /usr
/bin
/nagstamon
&
2082 screen
-L profanity a
2085 # i dont want to wait for konsole to exit...
2087 command prof
&>/dev
/null
&
2092 printf '\033[1A\033[K'; printf "%s\n" "$l"| ts
"%F %T" |
tee -a /p
/self-chat.log
2097 # cant use s because sudo -i doesnt work for passwordless sudo command
2100 sudo nmtui-connect
"$@"
2110 if shopt nullglob
>/dev
/null
; then
2124 # shellcheck disable=SC2024
2126 for f
in /var
/log
/exim
4/paniclog
/var
/log
/exim
4/*panic
; do
2128 if [[ -s $f ]]; then
2129 echo ================== $f =============
2130 s
tee -a /var
/log
/exim
4/$base-archive <$f
2138 ping() { command ping -O "$@"; }
2139 p8
() { ping "$@" 8.8.8.8; }
2140 p6
() { ping6
"$@" 2001:4860:4860::8888; }
2142 pkx
() { # package extract
2143 local pkg cached tmp f
2146 # shellcheck disable=SC2012
2147 cached
=$
(ls -t /var
/cache
/apt
/archives
/${pkg}_
* 2>/dev
/null |
tail -n1 2>/dev
/null
) ||
:
2148 if [[ $cached ]]; then
2151 m aptitude download
$pkg ||
return 1
2153 tmp
=(*); f
=${tmp[0]} # only 1 expected
2162 tmpf
=$
(pgrep
-f "$*")
2163 mapfile
-t pids
<<<"$tmpf"
2166 # shellcheck disable=SC2128
2172 0) echo "no pid found" ;;
2181 help="Usage: psg [--help] GREP_ARGS
2182 grep ps and output in a nice format"
2183 if [[ $1 == --help ]]; then
2188 # final grep is because some commands tend to have a lot of trailing spaces
2189 y
=$
(echo "$x" |
sed -r 's,//[^[:space:]:@/]+:[^[:space:]:@/]+@,//REDACTED_URL_USER@PASS/,g' |
grep -iP "$@" |
grep -o '.*[^ ]') ||
:
2191 echo "$x" |
head -n 1 ||
[[ $?
== 141 ]]
2196 pubip
() { curl
-4s https
://icanhazip.com
; }
2197 pubip6
() { curl
-6s https
://icanhazip.com
; }
2198 whatismyip
() { pubip
; }
2201 q
() { # start / launch a program in the backround and redir output to null
2205 # shellcheck disable=SC2120
2207 if [[ $HISTFILE ]]; then
2208 history -a # save history
2210 trap ERR
# this avoids a segfault
2212 # i had this redir, not sure why
2213 # exit "$@" 2>/dev/null
2216 # scp is insecure and deprecated.
2218 rsync
-Pt --inplace "$@"
2223 # available high ports are 1024-65535,
2224 # but lets skip things that are more likely to be in use
2227 print(secrets.SystemRandom().randrange(10002,65500))
2233 # shellcheck disable=SC1090 # expected to not follow
2243 # rsync, root is required to keep permissions right.
2244 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
2245 # --no-times --delete
2246 # basically, make an exact copy, use checksums instead of file times to be more accurate
2247 rsync
-ahvic --delete "$@"
2250 # like rlu, but dont delete files on the target end which
2251 # do not exist on the original end.
2255 # rl without preserving modification time.
2256 rsync
-ahvic --delete --no-t "$@"
2258 # [RSYNC_OPTS] HOST PATH
2260 # eg. rsu -opts frodo /testpath
2261 # relative paths will expanded with readlink -f.
2262 opts
=("${@:1:$#-2}") # 1 to last -2
2263 path
="${*:$#}" # last
2264 host="${*:$#-1:1}" # last -1
2265 if [[ $path == .
* ]]; then
2266 path
=$
(readlink
-f $path)
2268 m rsync
-ahvi --relative --no-implied-dirs "${opts[@]}" "$path" "root@$host:/";
2270 ccomp rsync rsd rsa rst rsu
2272 # find programs listening on a port
2275 # to figure out these args, i had to look at the man page from git version, as of 2022-04.
2276 s ss
-lpn state listening sport
= $port
2281 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2286 hr
; s ss
-lpn sport
= 53
2287 if systemctl is-enabled dnsmasq
&>/dev
/null ||
[[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2288 # this will fail is dnsmasq is failed
2289 hr
; m ser status dnsmasq |
cat ||
:
2291 hr
; echo $f:; ccat
$f
2292 hr
; m grr
'^ *(servers-file|server) *=|^ *no-resolv *$' /etc
/dnsmasq.conf
/etc
/dnsmasq.d
2293 f
=/etc
/dnsmasq-servers.conf
2294 hr
; echo $f:; ccat
$f
2297 echo /etc
/nsswitch.conf
:
2298 grep '^ *hosts:' /etc
/nsswitch.conf
2299 if systemctl is-enabled systemd-resolved
&>/dev
/null ||
[[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2300 hr
; m ser status systemd-resolved |
cat ||
:
2301 hr
; m resolvectl status |
cat
2309 if [[ $
(systemctl is-active nscd ||
:) != inactive
]]; then
2313 m sudo nscd
-i hosts
2315 if [[ $
(systemctl is-active dnsmasq ||
:) != inactive
]]; then
2316 m sudo systemctl restart dnsmasq
2318 if [[ $
(systemctl is-active systemd-resolved ||
:) != inactive
]]; then
2319 m sudo systemctl restart systemd-resolved
2321 if type -P resolvectl
&>/dev
/null
; then
2322 resolvectl flush-caches
2326 # add annoyingly long argument which should be the default
2328 sed -i --follow-symlinks "$@"
2333 # todo: test variable assignment with newlines here.
2334 # https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
2336 # beware that it only works on the assumption that any special
2337 # characters in the input string are intended to be escaped, not to work
2338 # as special chacters.
2340 LC_ALL
=C
sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
2344 ssh fencepost
head -n 300 /gd
/gnuorg
/EventAndTravelInfo
/rms-current-trips.txt |
less
2353 command sudo
"$@" ||
return $?
2358 # I use a function because otherwise we cant use in a script,
2359 # cant assign to variable.
2361 # note: gksudo is recommended for X apps because it does not set the
2362 # home directory to the same, and thus apps writing to ~ fuck things up
2363 # with root owned files.
2365 if [[ $EUID != 0 ||
$1 == -* ]]; then
2366 # shellcheck disable=SC2034
2367 SUDOD
="$PWD" command sudo
-i "$@"
2373 sb
() { # sudo bash -c
2374 # use sb instead of s is for sudo redirections,
2375 # eg. sb 'echo "ok fine" > /etc/file'
2376 # shellcheck disable=SC2034
2378 sudo
-i bash
-c "$@"
2381 se
() { s urun
0077 "$@"; }
2384 safe_rename
() { # warn and dont rename if file exists.
2385 # mv -n exists, but it\'s silent
2386 if [[ $# != 2 ]]; then
2387 echo safe_rename error
: $# args
, need
2 >&2
2390 if [[ $1 != "$2" ]]; then # yes, we want to silently ignore this
2391 if [[ -e $2 ||
-L $2 ]]; then
2392 echo "Cannot rename $1 to $2 as it already exists."
2401 sudo
dd status
=none of
="$1"
2405 if type -p systemctl
&>/dev
/null
; then
2408 if (( $# >= 3 )); then
2409 echo iank
: ser expected
2 or
less arguments
2416 systemctl
-n 40 status
"$@"
2419 # assume last arg is a service and we want to tail its log.
2421 local service jr_pid ret
2424 journalctl
-qn2 -f -u "$service" &
2426 s systemctl
"$@" || ret
=$?
2429 (( ret
== 0 )) ||
return $ret
2432 seru
() { systemctl
--user "$@"; }
2433 # like restart, but do nothing if its not already started
2436 if [[ $
(s systemctl
--no-pager show
-p ActiveState
$service ) == ActiveState
=active
]]; then
2437 systemctl restart
$service
2441 setini
() { # set a value in a .ini style file
2442 key
="$1" value
="$2" section
="$3" file="$4"
2443 if [[ -s $file ]]; then
2444 sed -ri -f - "$file" <<EOF
2445 # remove existing keys
2446 / *\[$section\]/,/^ *\[[^]]+\]/{/^\s*${key}[[:space:]=]/d}
2448 /^\s*\[$section\]/a $key=$value
2449 # from section to eof, do nothing
2450 /^\s*\[$section\]/,\$b
2451 # on the last line, if we haven't found section yet, add section and key
2463 sgo
() { # service go
2465 ser restart
$service ||
return 1
2466 if type -p systemctl
&>/dev
/null
; then
2472 # ignore services that dont exist
2473 if systemctl
cat $service &>/dev
/null
; then
2475 ser disable
$service
2481 systemctl list-unit-files | rg
"$@"
2484 # check whether we generally want to do sk on the file
2486 [[ ! -L $f ]] && istext
"$1" && [[ $
(head -n1 "$1" 2>/dev
/null
) == '#!/bin/bash'* ]]
2490 # see https://savannah.gnu.org/maintenance/fsf/bash-style-guide/ for justifications
2491 local quotes others ret
2492 quotes
=2048,2068,2086,2206,2254
2493 others
=2029,2032,2033,2054,2164
2494 shellcheck
-x -W 999 -e $quotes,$others "$@" || ret
=$?
2495 if (( ret
>= 1 )); then
2496 echo "A template comment to disable is now in clipboard. eg: # shellcheck disable=SC2206 # reason"
2497 cbs
"# shellcheck disable=SC"
2502 # sk with quotes. For checking scripts that we expect to take untrusted
2503 # input in order to verify we quoted vars.
2506 others
=2029,2033,2054,2164
2507 shellcheck
-W 999 -x -e $others "$@" ||
return $?
2510 # sk on all modified & new files in current git repo. must git add for new files.
2513 for f
in $
(i s |
awk '$1 == "modified:" {print $2}; $1 == "new" {print $3}'); do
2521 # sk on all the files in current git repo
2523 local f toplevel orig_dir tmp
2524 local -a ls_files sk_files
2525 toplevel
=$
(git rev-parse
--show-toplevel)
2526 if [[ $PWD != "$toplevel" ]]; then
2530 # tracked & untracked files
2531 tmp
=$
(git ls-files
&& git ls-files
--others --exclude-standard)
2532 mapfile
-t ls_files
<<<"$tmp"
2533 for f
in "${ls_files[@]}"; do
2539 if [[ $orig_dir ]]; then
2545 # sl: ssh, but firsh rsync our bashrc and related files to a special
2546 # directory on the remote host if needed.
2548 # Some environment variables and files need to be setup for this to work
2549 # (mine are set at the beginning of this file)
2551 # SL_FILES_DIR: Environment variable. Path to folder which should at
2552 # least have a .bashrc file or symlink. This dir will be rsynced to ~ on
2553 # remote hosts (top level symlinks are resolved) unless the host already
2554 # has a $SL_FILES_DIR/.bashrc. In that case, we assume it is a host you
2555 # control and sync files to separately and already has the ~/.bashrc you
2556 # want. The remote bash will also take its .inputrc config from this
2557 # folder (default of not existing is fine). Mine looks like this:
2558 # https://iankelling.org/git/?p=distro-setup;a=tree;f=sl/.iank
2560 # SL_INFO_DIR: Environment variable. This folder stores info about what
2561 # we detected on the remote system and when we last synced. It will be created
2562 # if it does not exist. Sometimes you may want to forget about a
2563 # remote system, you can use sl --rsync, or the function for that slr
2566 # SL_TEST_CMD: Env var. Meant to be used to vary the files synced
2567 # depending on the remote host. Run this string on the remote host the
2568 # first time sl is run (or if we run slr). The result is passed to
2569 # SL_TEST_HOOK. For example,
2570 # export SL_TEST_CMD=". /etc/os-release ; echo \${VERSION//[^a-zA-Z0-9]/}"
2572 # SL_TEST_HOOK: Env var. It is run as $SL_TEST_HOOK. This can set
2573 # $SL_FILES_DIR to vary the files synced.
2575 # SL_RSYNC_ARGS: Env var. String of arguments passed to rsync. For
2576 # example to exclude files within a directory. Note, excluded
2577 # files wont be deleted on rsync, you can add --delete-excluded
2578 # to the rsync command if that is desired.
2580 # SL_SSH_ARGS: Env var. Default arguments passed to ssh.
2582 # For when ~/.bashrc is already customized on the remote server, you
2583 # might find it problematic that ~/.bashrc is sourced for ALL ssh
2584 # commands, even in scripts. This paragraph is all about that. bash
2585 # scripts dont source ~/.bashrc, but call ssh in scripts and you get
2586 # ~/.bashrc. You dont want this. .bashrc is meant for interactive shells
2587 # and if you customize it, probably has bugs from time to time. This is
2588 # bad. Here's how I fix it. I have a special condition to "return" in my
2589 # .bashrc for noninteractive ssh shells (copy that code). Then use this
2590 # function or similar that passes LC_USEBASHRC=t when sshing and I want
2591 # my bashrc. Also, I don't keep most of my bashrc in .bashrc, i source a
2592 # separate file because even if I return early on, the whole file gets
2593 # parsed which can fail if there is a syntax error.
2595 # Background on LC_USEBASHRC var (no need to read if you just want to
2596 # use this function): env variables sent across ssh are strictly
2597 # limited, but we get LC_* at least in debian based machines, so we
2598 # just make that * be something no normal program would use. Note, on
2599 # hosts that dont allow LC_* I start an inner shell with LC_USEBASHRC
2600 # set, and the inner shell also allows running a nondefault
2601 # .bashrc. This means the outer shell still ran the default .bashrc,
2602 # but that is the best we can do.
2604 local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2
type info_sec force_rsync \
2605 sync_dirname testcmd extra_info testbool files_sec sl_test_cmd sl_test_hook
2606 declare -a args tmpa
2610 # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
2611 # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address]
2612 # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option]
2613 # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname
2616 # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
2617 # [-D [bind_address:]port] [-E log_file] [-e escape_char]
2618 # [-F configfile] [-I pkcs11] [-i identity_file]
2619 # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]
2620 # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
2621 # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
2624 if [[ $1 == --rsync ]]; then
2628 # shellcheck disable=SC2153 # intentional
2629 sl_test_cmd
=$SL_TEST_CMD
2630 # shellcheck disable=SC2153 # intentional
2631 sl_test_hook
=$SL_TEST_HOOK
2632 # shellcheck disable=SC2153 # intentional
2633 sl_rsync_args
=$SL_RSYNC_ARGS
2660 # note we dont support things like -4oOption
2661 -[46AaCfGgKkMNnqsTtVvXxYy
]*)
2664 -[bcDEeFIiJLlmOopQRSWw
]*)
2665 # -oOption etc is valid
2666 if (( ${#1} >= 3 )); then
2669 args
+=("$1" "$2"); shift 2
2678 if [[ ! $remote ]]; then
2679 echo $0: error hostname required
>&2
2684 if [[ ! $SL_INFO_DIR ]]; then
2685 echo 'error: missing SL_INFO_DIR env var' >&2
2691 tmpa
=($SL_INFO_DIR/???????????
"$remote")
2693 if [[ -e $sshinfo ]]; then
2694 if $force_rsync; then
2701 tmp
=${sshinfo[0]##*/}
2704 extra_info
=$
(cat $sshinfo)
2706 # we test for string to know ssh succeeded
2707 testbool
="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
2708 testcmd
="if $testbool; then printf y; else printf n; fi"
2709 if ! tmp
=$
(LC_USEBASHRC
=y
command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
2710 echo failed sl
test. doing plain
ssh -v
2711 command ssh -v "${args[@]}" "$remote"
2713 if [[ $tmp == y
* ]]; then
2719 extra_info
="${tmp:1}"
2721 if [[ $sl_test_hook ]]; then
2722 RSYNC_RSH
="ssh ${args[*]}" $sl_test_hook "$extra_info" "$remote"
2725 if $haveinfo && [[ $type == b
]]; then
2727 read -r files_sec _
< <(find -L $SL_FILES_DIR -printf "%T@ %p\n" |
sort -nr ||
[[ $?
== 141 ||
${PIPESTATUS[0]} == 32 ]] )
2728 files_sec
=${files_sec%%.*}
2729 if (( files_sec
> info_sec
)); then
2735 sync_dirname
=${SL_FILES_DIR##*/}
2737 if [[ ! $SL_FILES_DIR ]]; then
2738 echo 'error: missing SL_FILES_DIR env var' >&2
2743 RSYNC_RSH
="ssh ${args[*]}" m rsync
-rptL --delete $sl_rsync_args $SL_FILES_DIR "$remote":
2745 if $dorsync ||
! $haveinfo; then
2746 sshinfo
=$SL_INFO_DIR/$EPOCHSECONDS$type"$remote"
2747 [[ -e $SL_INFO_DIR ]] || mkdir
-p $SL_INFO_DIR
2748 printf "%s\n" "$extra_info" >$sshinfo
2751 if [[ $type == b
]]; then
2752 if (( ${#@} )); then
2753 # Theres a couple ways to pass arguments, im not sure whats best,
2754 # but relying on bash 4.4+ escape quoting seems most reliable.
2755 command ssh "${args[@]}" "$remote" \
2756 LC_USEBASHRC
=t bash
-c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash
${@@Q}
2757 elif [[ ! -t 0 ]]; then
2758 # This case is when commands are being piped to ssh.
2759 # Normally, no bashrc gets sourced.
2760 # But, since we are doing all this, lets source it because we can.
2761 cat <(echo .
$sync_dirname/.bashrc
) - |
command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2763 command ssh -t "${args[@]}" "$remote" LC_USEBASHRC
=t INPUTRC
=$sync_dirname/.inputrc bash
--rcfile $sync_dirname/.bashrc
2767 LC_USEBASHRC
=t
command ssh "${args[@]}" "$remote" ${@@Q}
2769 command ssh "${args[@]}" "$remote" LC_USEBASHRC
=t bash
2772 # this function inspired from https://github.com/Russell91/sshrc
2782 # WARNING: If you are trying to use -i, remember that keys added to
2783 # agent previously will still be tried. Use ssh-add -D to remove all
2784 # keys from the agent.
2786 ssh -oControlMaster=no
-oControlPath=/ "$@"
2788 # kill off old shared socket then ssh
2790 m
ssh -O exit "$@" ||
[[ $?
== 255 ]]
2793 ccomp
ssh sl slr sss ssk
2796 LC_USEBASHRC
=t
command ssh "$@"
2801 # log with script. timing is $1.t and script is $1.s
2802 # -l to save to ~/typescripts/
2803 # -t to add a timestamp to the filenames
2804 local logdir do_stamp arg_base
2805 (( $# >= 1 )) ||
{ echo "arguments wrong"; return 1; }
2808 while getopts "lt" option
2811 l
) arg_base
=$logdir ;;
2814 echo error
: bad option
2819 shift $
((OPTIND
- 1))
2821 [[ -e $logdir ]] || mkdir
-p $logdir
2822 $do_stamp && arg_base
+=$
(date +%F.
%T
%z
)
2823 script -t $arg_base.s
2> $arg_base.t
2825 splay
() { # script replay
2826 #logRoot="$HOME/typescripts/"
2827 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
2828 scriptreplay
"$1.t" "$1.s"
2832 # sudo redo. be aware, this command may not work right on strange distros or earlier software
2833 if [[ $# == 0 ]]; then
2834 sudo
-E bash
-c -l "$(history -p '!!')"
2836 echo this
command redos last
history item. no argument is accepted
2841 # with -ll, less secure but faster.
2842 command srm
-ll "$@"
2847 ssh $1 "/tmp/${2##*/}" "$(printf "%q
\n" "${@:2}")"
2859 tclock
() { # terminal clock
2864 # this goes to full width
2865 #len=${1:-$((COLUMNS -7))}
2868 if (( x
== len
)); then
2870 d
="$(date +%l:%_M) "
2873 d
=$
(date +%l
:%M
:%_S
)
2877 for ((i
=0; i
<x
; i
++)); do
2878 if (( i
% 6 )); then
2896 # test existence / exists
2899 [[ -e "$x" ||
-L "$x" ]] || ret
=1
2905 # normally, i would just execute these commands in the function.
2906 # however, DEBUG is not inherited, so we need to run it outside a function.
2907 # And we want to run set -x afterwards to avoid spam, so we cram everything
2908 # in here, and then it will run after this function is done.
2909 # shellcheck disable=SC2178 # intentional
2910 PROMPT_COMMAND
='trap DEBUG; unset PROMPT_COMMAND; PS1=" \w \$ "'
2914 # shellcheck disable=SC2178 # intentional
2915 PROMPT_COMMAND
='trap DEBUG; unset PROMPT_COMMAND'
2916 PS1
='\[\e]133;L\a\]\[\e]133;D;$?\]\[\e]133;A\a\]\w \$ \[\e]133;B\a\]' ;
2917 PS2
='\[\e]133;A\a\]'$PS2'\[\e]133;B\a\]' ;
2918 PS0
='\[\e]133;C\a\]'
2923 PROMPT_COMMAND
=(prompt-command
)
2924 if [[ $TERM == *(screen
*|xterm
*|rxvt
*) ]]; then
2925 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
2929 # prometheus node curl
2932 host=${1:-127.0.0.1}
2933 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
2936 tx
() { # toggle set -x, and the prompt so it doesnt spam
2937 if [[ $
- == *x
* ]]; then
2946 # show all processes in the network namespace $1.
2947 # blank entries appear to be subprocesses/threads
2951 sudo
find -L /proc
/[1-9]*/task
/*/ns
/net
-samefile /run
/netns
/$netns | cut
-d/ -f5 | \
2953 x
=$
(ps
-w --no-headers -p $l);
2954 if [[ $x ]]; then echo "$x"; else echo $l; fi;
2958 if ! s ip netns list |
grep -Fx nonet
&>/dev
/null
; then
2959 s ip netns add nonet
2961 sudo
-E env
/sbin
/ip netns
exec nonet sudo
-E -u iank
/bin
/bash
2964 m
() { printf "%s\n" "$*"; "$@"; }
2965 m2
() { printf "%s\n" "$*" >&2; "$@"; }
2967 # update file. note: duplicated in mail-setup.
2968 # updates $ur u result to true or false
2969 # updates $reload to true if file updated is in /etc/systemd/system
2971 local tmp tmpdir dest
="$1"
2972 local base
="${dest##*/}"
2973 local dir
="${dest%/*}"
2974 if [[ $dir != "$base" ]]; then
2975 # dest has a directory component
2978 # shellcheck disable=SC2034 # see comment at top of function
2980 tmpdir
="$(mktemp -d)"
2981 cat >$tmpdir/"$base"
2982 tmp
=$
(rsync
-ic $tmpdir/"$base" "$dest")
2984 printf "%s\n" "$tmp"
2985 # shellcheck disable=SC2034 # see comment at top of function
2987 if [[ $dest == /etc
/systemd
/system
/* ]]; then
2988 # shellcheck disable=SC2034 # see comment at top of function
2997 if type -p uprecords
&>/dev
/null
; then
3005 for x
in "$@"; do virsh destroy
"$x"; virsh undefine
"$x"; done
3013 sudo virsh dumpxml
$vm |
sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
3014 sed -r "s/listen='[^']+/listen='$ip/"> $t
3015 sudo virsh undefine
$vm
3016 sudo virsh define
$t
3021 vm-set-listen
$1 0.0.0.0
3026 vm-set-listen
$1 127.0.0.1
3031 interfaces
=$
(iw dev |
awk '$1 == "Interface" {print $2}')
3032 for i
in $interfaces; do
3033 echo "myiwscan: considering $i"
3034 # find input, copy to pattern space, when we find the first field, print the copy in different order without newlines.
3035 # instead of using labels, we could just match a line and group, eg: /signal:/,{s/signal:(.*)/\1/h}
3036 sudo iw dev
$i scan |
sed -rn "
3037 s/^\Wcapability: (.*)/\1/;Ta;h;b
3038 :a;s/^\Wsignal: -([^.]+).*/\1/;Tb;H;b
3039 # padded to min width of 20
3040 :b;s/\WSSID: (.*)/\1 /;T;s/^(.{20}(.*[^ ])?) */\1/;H;g;s/(.*)\n(.*)\n(.*)/\2 \3 \1/gp;b
3045 # Run script by copying it to a temporary location first,
3046 # and changing directory, so we don't have any open
3047 # directories or files that could cause problems when
3064 # spark 1 5 22 13 53
3068 # Copyright (c) Zach Holman, https://zachholman.com
3069 # https://github.com/holman/spark
3071 # As of 2022-10-28, I reviewed github forks that had several newer
3072 # commits, none had anything interesting. I did a little refactoring
3073 # mostly to fix emacs indent bug.
3075 # Generates sparklines.
3078 if [ "X$1" = "X-n" ]; then
3092 # find min/max values
3093 local min
=0xffffffff max
=0
3097 # on Linux (or with bash4) we could use `printf %.0f $n` here to
3098 # round the number but that doesn't work on OS X (bash3) nor does
3099 # `awk '{printf "%.0f",$1}' <<< $n` work, so just cut it off
3101 (( n
< min
)) && min
=$n
3102 (( n
> max
)) && max
=$n
3103 numbers
=$numbers${numbers:+ }$n
3107 local ticks
=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █
)
3109 # use a high tick if data is constant
3110 (( min
== max
)) && ticks
=(▅ ▆
)
3113 f
=$
(( ( (max-min
) <<8)/( tc - 1) ))
3118 _spark_echo -n ${ticks[$(( ((n-min)<<8)/f ))]}
3123 pdfwc() { local f; for f; do echo "$f" "$(pdfinfo "$f" | awk '/^Pages:/ {print $2}')"; done }
3126 # nvm install script appended this to my .bashrc. I dont want to run it all the time,
3127 # so put it in a function.
3129 export NVM_DIR="$HOME/.nvm"
3130 # shellcheck disable=SC1091 # may not exist, & third party
3131 [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" # This loads nvm
3132 # shellcheck disable=SC1091 # may not exist, & third party
3133 [ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion" # This loads nvm bash_completion
3138 if date -d 'february 29' &>/dev/null; then
3148 if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
3155 # make vim work with my light colortheme terminal.
3157 if [[ -e ~/.vimrc ]]; then
3160 command vim -c ':colorscheme peachpuff' "$@"
3164 # ls count. usage: pass a directory, get the number of files.
3165 # https://unix.stackexchange.com/questions/90106/whats-the-most-resource-efficient-way-to-count-how-many-files-are-in-a-director
3167 # shellcheck disable=SC2790 disable=SC2012 # intentional
3171 # run then notify. close notification after the next prompt.
3174 dunstify -u critical -h string:x-dunst-stack-tag:profanity "$*"
3175 _psrun=(dunstctl close-all)
3178 dunstify -u critical -h string:x-dunst-stack-tag:profanity n
3179 _psrun=(dunstctl close-all)
3185 # shellcheck disable=SC2030
3186 inotifywait -m "$dir" -e create -e moved_to | while read -r _ _ file; do
3200 if ! type -p sponge &>/dev/null; then
3201 echo "$0: error: missing dependency: sudo apt install moreutils" >&2
3206 echo "adding header to $f"
3207 if [[ -s $f ]]; then
3212 cat - "${f_maybe[@]}" <<EOF | sponge "$f"
3213 The following is the GNU All-permissive License as recommended in
3214 <https://www.gnu.org/licenses/license-recommendations.en.html>
3216 Copyright (C) $(date +%Y) Free Software Foundation <sysadmin@fsf.org>
3218 Copying and distribution of this file, with or without modification,
3219 are permitted in any medium without royalty provided the copyright
3220 notice and this notice are preserved. This file is offered as-is,
3221 without any warranty.
3223 Contributions are welcome. See <https://savannah.gnu.org/maintenance/fsf/>.
3229 # note, there is also the tool gron which is meant for this, but
3230 # this is good enough to not bother installing another tool
3232 # https://stackoverflow.com/questions/59700329/how-to-print-path-and-key-values-of-json-file-using-jq
3233 jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' "$@"
3237 "$@" |& ts || return $?
3244 if $use_color && type -p tput &>/dev/null; then
3245 # this is nice for a dark background terminal:
3246 # https://github.com/trapd00r/LS_COLORS
3247 # I would like if there was something similar for light.
3249 # https://www.bigsoft.co.uk/blog/2008/04/11/configuring-ls_colors
3250 # change the hard to read turqouise.
3251 # defaults dircolors --print-database.
3253 # the default bold green is too light.
3254 # this explains the codes: https://gist.github.com/thomd/7667642
3255 export LS_COLORS="ex=1:ln=00;31"
3257 term_bold="$(tput bold)"
3258 term_red="$(tput setaf 1)"
3259 term_green="$(tput setaf 2)"
3260 # shellcheck disable=SC2034 # expected
3261 term_yellow="$(tput setaf 3)"
3262 term_purple="$(tput setaf 5)"
3263 term_nocolor="$(tput sgr0)" # no font attributes
3265 # unused so far. commented for shellcheck
3266 # term_underl="$(tput smul)"
3267 # term_blue="$(tput setaf 4)"
3268 # term_cyan="$(tput setaf 6)"
3270 # Try to keep environment pollution down, EPA loves us.
3271 unset safe_term match_lhs use_color
3276 if [[ $- == *i* ]]; then
3281 if [[ $EUID == 1000 ]]; then
3288 # this needs to come before next ps1 stuff
3289 # this stuff needs bash 4, feb 2009,
3290 # old enough to no longer condition on $BASH_VERSION anymore
3294 if [[ $- == *i* ]] && [[ ! $LC_INSIDE_EMACS ]]; then
3296 bind -m vi-command B:shell-backward-word
3297 bind -m vi-command W:shell-forward-word
3300 if [[ $SSH_CLIENT || $SUDO_USER ]]; then
3301 unset PROMPT_DIRTRIM
3305 # emacs terminal has problems if this runs slowly,
3306 # so I've thrown a bunch of things at the wall to speed it up.
3308 local return=$? # this MUST COME FIRST
3311 # all usable colors:
3313 # green nonzero exit (pri 1)
3316 # red pwd different owner & group & not writable (pri 2)
3317 # red bold pwd different owner & group & writable (pri 2)
3320 local ps_char ps_color
3323 if [[ $HISTFILE ]]; then
3324 history -a # save history
3325 if [[ -e $HOME/.iank-stream-on ]]; then
3326 if [[ $HISTFILE == $HOME/.bh ]]; then
3329 elif [[ $HISTFILE == /a/bin/data/stream_hist ]]; then
3334 ps_color="$term_purple"
3335 ps_char="$ps_char"'\$'
3336 if [[ ! -O . ]]; then # not owner
3337 if [[ -w . ]]; then # writable
3338 ps_color="$term_bold$term_red"
3340 ps_color="$term_red"
3344 if [[ $return != 0 ]]; then
3345 ps_color="$term_green"
3346 ps_char="$return \\$"
3349 # faster than sourceing the file im guessing
3350 if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then
3351 eval "$(< /dev/shm/iank-status)"
3353 if [[ $MAIL_HOST && $MAIL_HOST != "$HOSTNAME" ]]; then
3354 ps_char="@ $ps_char"
3357 if [[ $(jobs -p) ]]; then
3358 jobs_char="$(jobs -p)"'j\j '
3362 # allow a function to specify a command to run after we run the next
3363 # command. Use case: a function makes a persistent notification. If
3364 # we happen to be using that terminal, we can just keep working by
3365 # entering our next command, even a noop in order to dismiss the
3366 # notification, instead of having to explicitly dismiss it.
3367 if [[ ${_psrun[*]} ]]; then
3368 if (( _psrun_count >= 1 )); then
3374 _psrun_count=$(( _psrun_count + 1 ))
3380 # We could test if sudo is active with sudo -nv
3381 # but then we get an email and log of lots of failed sudo commands.
3382 # We could turn those off, but seems better not to.
3383 if [[ $EUID != 0 ]] && [[ $DID_SUDO ]]; then
3384 psudo="\[$term_bold$term_red\]s\[$term_nocolor\] "
3386 if [[ ! $HISTFILE ]]; then
3387 ps_char="NOHIST $ps_char"
3389 PS1="${PS1%"${PS1#*[wW]}"} $jobs_char$psudo\[$ps_color\]$ps_char\[$term_nocolor\] "
3393 # copy of what is automatically added by guix.
3394 # adds [env] to PS1 if GUIX_ENVIRONMENT is set and PS1 contains '$';
3395 if [ -n "$GUIX_ENVIRONMENT" ]; then
3396 if [[ $PS1 =~ (.*)"\\$" ]]; then
3397 PS1="${BASH_REMATCH[1]} [env]\\\$ "
3402 # set titlebar. instead, using more advanced
3404 #echo -ne "$_title_escape $HOSTNAME ${PWD/#$HOME/~} \007"
3406 if [[ $KONSOLE_VERSION ]]; then
3407 # from konsole, output via ctrl-alt-]
3408 if [[ ! $PS1 =~ 133 ]] ; then
3409 PS1='\[\e]133;L\a\]\[\e]133;D;$?\]\[\e]133;A\a\]'$PS1'\[\e]133;B\a\]' ;
3410 PS2='\[\e]133;A\a\]'$PS2'\[\e]133;B\a\]' ;
3411 PS0='\[\e]133;C\a\]' ; fi
3415 PROMPT_COMMAND=(prompt-command)
3417 if [[ $TERM == screen* ]]; then
3418 _title_escape="\033]..2;"
3420 # somme sites recommend this, i dunno what the diff is.
3421 #_title_escape="\033]30;"
3422 _title_escape="\033]0;"
3425 # make the titlebar be the last command and the current directory.
3426 auto-window-title () {
3429 # These are some checks to help ensure we dont set the title at
3430 # times that the debug trap is running other than the case we
3431 # want. Some of them might not be needed.
3432 if (( ${#FUNCNAME[@]} != 1 || ${#BASH_ARGC[@]} != 2 || BASH_SUBSHELL != 0 )); then
3435 if [[ $1 == prompt-command ]]; then
3438 echo -ne "$_title_escape ${PWD/#$HOME/~} "
3443 # note, this wont work:
3444 # x=$(mktemp); cp a $x
3445 # I havnt figured out why, bigger fish to fry.
3448 # condition from the screen man page i think.
3449 # note: duplicated in tx()
3450 if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
3451 trap 'auto-window-title "$BASH_COMMAND"' DEBUG
3462 rooms=(jupiter saturn)
3463 for ip in 209.51.188.25 live.fsf.org; do
3464 out=$(curl -sS --insecure https://$ip/)
3469 # shellcheck disable=SC2004 # false positive
3470 roomv[$i]=$(( ${roomv[$i]} + n ))
3471 done < <(printf "%s\n" "$out" | grep -Po "$room.*?current[^0-9]*[0-9]*" | grep -o '[0-9]*$' )
3474 printf "total: %s " $v
3477 printf "$room: %s " "${roomv[$i]}"
3483 local default_route_dev
3484 default_route_dev=$(ip r show default | sed 's/.*dev \([^ ]*\).*/\1/' | head -n1)
3485 m s ip n flush dev "$default_route_dev"
3492 # cat or bat with color if we have it
3494 if type -t batcat >/dev/null; then
3495 # note: another useful useful style is "header"
3496 batcat --color always --style plain --theme Coldark-Cold -P "$@"
3502 # Combine files $@ into a single file with comments between them which
3503 # allow splitting them back with fsplit.
3505 # Assumes file names do not have newlines in them.
3508 # generated with cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c8
3509 comment='# jvvuyUsq '
3513 echo "$comment$f" >>$out
3518 local f fin line fin_lines
3521 fin_lines=$(wc -l "$fin" | awk '{print $1}')
3522 comment='# jvvuyUsq '
3523 while (( line <= fin_lines )); do
3524 f=$(sed -n "${line}s/^$comment//p" "$fin")
3525 sed -n "$line,/^$comment/{/^$comment/d;p}" "$fin" >"$f"
3526 line=$(( line + 1 + $(wc -l "$f" | awk '{print $1}') ))
3530 # * stuff that makes sense to be at the end
3536 if [[ -s "$HOME/.rvm/scripts/rvm" ]]; then
3537 # shellcheck disable=SC1091
3538 source "$HOME/.rvm/scripts/rvm"
3541 # I had this idea to start a bash shell which would run an initial
3542 # command passed through this env variable, then continue on
3543 # interactively. But the use case I had in mind went away.
3545 # if [[ $MY_INIT_CMD ]]; then
3546 # "${MY_INIT_CMD[@]}"
3550 # ensure no bad programs appending to this file will have an affect