9e368d471a21a93ec3d4786e2513c0f442f5be5f
[distro-setup] / brc
1 #!/bin/bash
2 # Copyright (C) 2019 Ian Kelling
3 # SPDX-License-Identifier: AGPL-3.0-or-later
4 # this gets sourced. shebang is just for file mode detection
5
6 # note, to catch errors in functions but not outside, do:
7 # set -E -o pipefail
8 # trap return ERR
9 # trap 'trap ERR' RETURN
10
11
12 # * settings
13
14 CDPATH=.
15
16 set -o pipefail
17
18 # remove all aliases. aliases provided by the system tend to get in the way,
19 # for example, error happens if I try to define a function the same name as an alias
20 unalias -a
21
22 # remove gnome keyring warning messages
23 # there is probably a more proper way, but I didnt find any easily on google
24 # now using xfce+xmonad instead of vanilla xmonad, so disabling this
25 #unset GNOME_KEYRING_CONTROL
26
27 # use extra globing features.
28 shopt -s extglob
29 # include .files when globbing, but ignore files name . and ..
30 # setting this also sets dotglob.
31 export GLOBIGNORE="*/.:*/.."
32
33 # broken with bash_completion package. Saw a bug for this once. dont anymore.
34 # still broken in wheezy
35 # still buggered in latest stable from the web, version 2.1
36 # perhaps its fixed in newer git version, which fails to make for me
37 # this note is from 6-2014.
38 # still broken in flidas.
39 #shopt -s nullglob
40
41 # make tab on an empty line do nothing
42 shopt -s no_empty_cmd_completion
43
44 # fix spelling errors for cd, only in interactive shell
45 shopt -s cdspell
46 # append history instead of overwritting it
47 shopt -s histappend
48 # for compatibility, per gentoo/debian bashrc
49 shopt -s checkwinsize
50 # attempt to save multiline single commands as single history entries.
51 shopt -s cmdhist
52 # enable **
53 shopt -s globstar
54
55
56 # inside emacs fixes
57 if [[ $RLC_INSIDE_EMACS ]]; then
58 # EMACS is used by bash on startup, but we dont need it anymore.
59 # plus I hit a bug in a makefile which inherited it
60 unset EMACS
61 export RLC_INSIDE_EMACS
62 export PAGER=cat
63 export MANPAGER=cat
64 # scp completion does not work, but this doesnt fix it. todo, figure this out
65 complete -r scp &> /dev/null
66 # todo, remote file completion fails, figure out how to turn it off
67 export NODE_DISABLE_COLORS=1
68 # This gets rid of ugly terminal escape chars in node repl
69 # sometime, Id like to have completion working in emacs shell for node
70 # the offending chars can be found in lib/readline.js,
71 # things that do like:
72 # stream.write('\x1b[' + (x + 1) + 'G');
73 # We can remove them and keep readline, for example by doing this
74 # to start a repl:
75 #!/usr/bin/env nodejs
76 # var readline = require('readline');
77 # readline.cursorTo = function(a,b,c) {};
78 # readline.clearScreenDown = function(a) {};
79 # const repl = require('repl');
80 # var replServer = repl.start('');
81 #
82 # no prompt, or else readline complete seems to be confused, based
83 # on our column being different? node probably needs to send
84 # different kind of escape sequence that is not ugly. Anyways,
85 # completion doesnt work yet even with the ugly prompt, so whatever
86 #
87 export NODE_NO_READLINE=1
88
89 fi
90
91 # emacs has a different default search path than the info command. This
92 # adds the info defaults to emacs, but not the reverse, because I dun
93 # care much about the cli. The search path is only on the cli if you run
94 # "info xxx", or in emacs if you run '(info xxx)', so not that
95 # important, but might as well fix it.
96
97 # info info says this path is what was compiled, and its not documented
98 # anywhere. Through source grepping, i found it in filesys.h of the info
99 # source in trisquel flidas.
100 #
101 # Traling : means for emacs to add its own stuff on to the end.
102
103 export INFOPATH=$PATH:/usr/local/info:/usr/info:/usr/local/lib/info:/usr/lib/info:/usr/local/gnu/info:/usr/local/gnu/lib/info:/usr/gnu/info:/usr/gnu/lib/info:/opt/gnu/info:/usr/share/info:/usr/share/lib/info:/usr/local/share/info:/usr/local/share/lib/info:/usr/gnu/lib/emacs/info:/usr/local/gnu/lib/emacs/info:/usr/local/lib/emacs/info:/usr/local/emacs/info:.:
104
105 if [[ $- == *i* ]]; then
106 # for readline-complete.el
107 if [[ $RLC_INSIDE_EMACS ]]; then
108 # all for readline-complete.el
109 stty echo
110 bind 'set horizontal-scroll-mode on'
111 bind 'set print-completions-horizontally on'
112 bind '"\C-i": self-insert'
113 else
114
115 if [[ $KONSOLE_PROFILE_NAME ]]; then
116 TERM=xterm-256color
117 fi
118
119 # todo: not sure this works in sakura
120 #stty werase undef
121 #bind "\C-w": kill-region
122 # sakura == xterm-256color
123 # konsole == xterm
124 if [[ $TERM == xterm* ]]; then
125 # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
126 bind '"\e[1;5C": shell-forward-word' 2>/dev/null
127 bind '"\e[1;5D": shell-backward-word' 2>/dev/null
128 else
129 # make ctrl-backspace work. for konsole, i fixed it through
130 # /home/iank/.local/share/konsole/default.keytab
131 stty werase '^h'
132 bind '"\eOc": shell-forward-word'
133 bind '"\eOd": shell-backward-word'
134 fi
135 # i cant remember why i did this, probably to free up some keys to bind
136 # to other things in bash.
137 # other than C-c and C-z, the rest defined by stty -a are, at least in
138 # gnome-terminal, overridden by bash, or disabled by the system
139 stty lnext undef stop undef start undef
140 fi
141
142 fi
143
144
145 # history number. History expansion is good.
146 PS4='$LINENO+ '
147 # history file size limit, set to unlimited.
148 # this needs to be different from the default because
149 # default HISTFILESIZE is 500 and could clobber our history
150 HISTFILESIZE=
151 # max commands 1 session can append/read from history
152 HISTSIZE=1000000
153 # the time format display when doing the history command
154 # also, setting this makes the history file record time
155 # of each command as seconds from the epoch
156 HISTTIMEFORMAT="%Y-%m-%d %I:%M %p "
157 # consecutive duplicate lines dont go in history
158 HISTCONTROL=ignoredups
159 # works in addition to HISTCONTROL to do more flexible things
160 # it could also do the same things as HISTCONTROL and thus replace it,
161 # but meh. dunno why, but just " *" does glob expansion, so use [ ] to avoid it.
162 HISTIGNORE='pass *:[ ]*:otp *:oathtool *'
163
164 export BC_LINE_LENGTH=0
165
166 export PROFILE_TASKS_TASK_OUTPUT_LIMIT=100
167
168 # note, if I use a machine I dont want files readable by all users, set
169 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
170
171 # i for insensitive. the rest from
172 # X means dont remove the current screenworth of output upon exit
173 # R means to show colors n things
174 export LESS=RXi
175 export SYSTEMD_LESS=$LESS
176
177
178 # * include files
179
180
181
182 # if someone exported $SOE (stop on error), catch errors.
183 #
184 # Note, on debian this results in the following warning when in ssh,
185 # hich I haven't figured out how to fix. It doesn't happen if we source
186 # after the shell has started
187 #
188 # bash: /usr/share/bashdb/bashdb-main.inc: No such file or directory
189 # bash: warning: cannot start debugger; debugging mode disabled
190 if [[ $SOE ]]; then
191 if [[ -e /a/bin/errhandle/err ]]; then
192 source /a/bin/errhandle/err
193 fi
194 fi
195
196 # based on readme.debian. dunno if this will break on other distros.
197 if [[ -s /usr/share/wcd/wcd-include.sh ]]; then
198 source /usr/share/wcd/wcd-include.sh
199 fi
200
201 if [[ -s /a/bin/small-misc-bash/ll-function ]]; then
202 # shellcheck source=/a/bin/small-misc-bash/ll-function
203 source /a/bin/small-misc-bash/ll-function
204 elif [[ -s ~/.iank/ll-function ]]; then
205 source ~/.iank/ll-function
206 fi
207
208 # * functions
209
210
211 ..() { c ..; }
212 ...() { c ../..; }
213 ....() { c ../../..; }
214 .....() { c ../../../..; }
215 ......() { c ../../../../..; }
216
217 # file cut copy and paste, like the text buffers :)
218 # I havnt tested these.
219 _fbufferinit() { # internal use
220 ! [[ $my_f_tempdir ]] && my_f_tempdir=$(mktemp -d)
221 rm -rf "${my_f_tempdir:?}"/*
222 }
223 fcp() { # file cp
224 _fbufferinit
225 cp "$@" "$my_f_tempdir"/
226 }
227 fct() { # file cut
228 _fbufferinit
229 mv "$@" "$my_f_tempdir"/
230 }
231 fpst() { # file paste
232 [[ $2 ]] && { echo too many arguments; return 1; }
233 target=${1:-.}
234 cp "$my_f_tempdir"/* "$target"
235 }
236
237 _khfix_common() {
238 local host=${1##*@}
239 local ip port
240 read -r ip port < <(timeout 1 ssh -oBatchMode=yes -oControlMaster=no -oControlPath=/ -v $1 |& sed -rn "s/debug1: Connecting to $host \[([^\]*)] port ([0-9]+).*/\1 \2/p")
241 if [[ ! $ip ]]; then
242 echo "khfix: ssh failed"
243 return 1
244 fi
245 if [[ $port != 22 ]]; then
246 ip_entry="[$ip]:$port"
247 host_entry="[$host]:$port"
248 else
249 ip_entry=$ip
250 host_entry=$host
251 fi
252 ssh-keygen -R "$host_entry" -f $(readlink -f ~/.ssh/known_hosts)
253 echo "khfix: removing key for $ip_entry"
254 ssh-keygen -R "$ip_entry" -f $(readlink -f ~/.ssh/known_hosts)
255 }
256 khfix() { # known hosts fix
257 _khfix_common "$@" || return 1
258 ssh $1 :
259 }
260 khcopy() {
261 _khfix_common "$@"
262 ssh-copy-id $1
263 }
264
265 a() {
266 local x
267 x=$(readlink -nf "${1:-$PWD}")
268 # yes, its kinda dumb that xclip/xsel cant do this in one invocation
269 echo -n "$x" | xclip -selection clipboard
270 echo -n "$x" | xclip
271 }
272
273 ack() { ack-grep "$@"; }
274
275 b() {
276 # backwards
277 c -
278 }
279
280
281 # c. better cd
282 if type -p wcd &>/dev/null; then
283 if [[ $RLC_INSIDE_EMACS ]]; then
284 c() { wcd -c -z 50 -o "$@"; }
285 else
286 # lets see what the fancy terminal does from time to time
287 c() { wcd -c -z 50 "$@"; }
288 fi
289 else
290 c() { cd "$@"; }
291 fi
292
293 c4() { c /var/log/exim4; }
294
295 caa() { git commit --amend --no-edit -a; }
296
297 caf() {
298 # shellcheck disable=SC2033
299 find -L $1 -type f -not \( -name .svn -prune -o -name .git -prune \
300 -o -name .hg -prune -o -name .editor-backups -prune \
301 -o -name .undo-tree-history -prune \) \
302 -exec bash -lc 'hr; echo "$1"; hr; cat "$1"' _ {} \; 2>/dev/null
303
304 }
305
306 calc() { echo "scale=3; $*" | bc -l; }
307 # no having to type quotes, but also no command history:
308 clc() {
309 local x
310 read -r x
311 echo "scale=3; $x" | bc -l
312 }
313
314 cam() {
315 git commit -am "$*"
316 }
317
318
319 ccat () { # config cat. see a config without extra lines.
320 grep '^\s*[^;[:space:]#]' "$@"
321 }
322
323
324 _cdiff-prep() {
325 # join options which are continued to multiples lines onto one line
326 local first=true
327 while IFS= read -r line; do
328 # remove leading spaces/tabs. assumes extglob
329 if [[ $line == "[ ]*" ]]; then
330 line="${line##+( )}"
331 fi
332 if $first; then
333 pastline="$line"
334 first=false
335 elif [[ $line == *=* ]]; then
336 echo "$pastline" >> "$2"
337 pastline="$line"
338 else
339 pastline="$pastline $line"
340 fi
341 done < <(grep -vE '^([ \t]*#|^[ \t]*$)' "$1")
342 echo "$pastline" >> "$2"
343 }
344
345 cdiff() {
346 # diff config files,
347 # setup for format of postfix, eg:
348 # option = stuff[,]
349 # [more stuff]
350 local pastline unified f1 f2
351 unified="$(mktemp)"
352 f1="$(mktemp)"
353 f2="$(mktemp)"
354 _cdiff-prep "$1" "$f1"
355 _cdiff-prep "$2" "$f2"
356 cat "$f1" "$f2" | grep -Po '^[^=]+=' | sort | uniq > "$unified"
357 while IFS= read -r line; do
358 # the default bright red / blue doesnt work in emacs shell
359 dwdiff -cblue,red -A best -d " ," <(grep "^$line" "$f1" || echo ) <(grep "^$line" "$f2" || echo ) | colordiff
360 done < "$unified"
361 }
362
363
364 cat-new-files() {
365 local start=$SECONDS
366 local dir="$1"
367 inotifywait -m "$dir" -e create -e moved_to |
368 # shellcheck disable=SC2030
369 while read -r filedir _ file; do
370 cat "$filedir$file"
371 hr
372 calc $((SECONDS - start)) / 60
373 sleep 5
374 done
375
376 }
377
378 # shellcheck disable=SC2032
379 chown() {
380 # makes it so chown -R symlink affects the symlink and its target.
381 if [[ $1 == -R ]]; then
382 shift
383 command chown -h "$@"
384 command chown -R "$@"
385 else
386 command chown "$@"
387 fi
388 }
389
390 cim() {
391 git commit -m "$*"
392 }
393
394 cl() {
395 # choose recent directory. cl = cd list
396 c =
397 }
398
399 d() { builtin bg; }
400 complete -A stopped -P '"%' -S '"' d
401
402
403 dc() {
404 diff --strip-trailing-cr -w "$@" # diff content
405 }
406
407 despace() {
408 local x y
409 for x in "$@"; do
410 y="${x// /_}"
411 safe_rename "$x" "$y"
412 done
413 }
414
415 dt() {
416 date "+%A, %B %d, %r" "$@"
417 }
418
419 dus() { # du, sorted, default arg of
420 du -sh ${@:-*} | sort -h
421 }
422
423
424
425 e() { echo "$@"; }
426
427 # echo args
428 ea() {
429 if (( ! $# )); then
430 echo no args
431 fi
432 for arg; do
433 printf "%qEOL\n" "${arg}"
434 printf "%s" "${arg}" |& hexdump -C
435 done
436 }
437 # echo vars. print var including escapes, etc
438 ev() {
439 if (( ! $# )); then
440 echo no args
441 fi
442 for arg; do
443 printf "%qEOL\n" "${!arg}"
444 printf "%s" "${!arg}" |& hexdump -C
445 done
446 }
447
448
449 ediff() {
450 [[ ${#@} == 2 ]] || { echo "error: ediff requires 2 arguments"; return 1; }
451 emacs --eval "(ediff-files \"$1\" \"$2\")"
452 }
453
454 # mail related
455 etail() {
456 tail -F /var/log/exim4/mainlog -n 200
457 }
458 eless() {
459 less /var/log/exim4/mainlog
460 }
461 eqcat() {
462 exiqgrep -i | while read i; do
463 exim -Mvh $i; hr; exim -Mvb $i; hr;
464 exigrep $i /var/log/exim4/mainlog; hr
465 done
466 }
467
468
469 # shellcheck disable=SC2032
470 f() {
471 # cd forward
472 c +
473 }
474
475 fa() {
476 # find array. make an array of file names found by find into $x
477 # argument: find arguments
478 # return: find results in an array $x
479 while read -rd ''; do
480 x+=("$REPLY");
481 done < <(find "$@" -print0);
482 }
483
484 faf() { # find all files. use -L to follow symlinks
485 find $@ -not \( -name .svn -prune -o -name .git -prune \
486 -o -name .hg -prune -o -name .editor-backups -prune \
487 -o -name .undo-tree-history -prune \) -type f 2>/dev/null
488 }
489
490 # mail related
491 frozen() {
492 rm -rf /tmp/frozen
493 s mailq |gr frozen|awk '{print $3}' | while read -r id; do
494 s exim -Mvl $id
495 echo
496 s exim -Mvh $id
497 echo
498 s exim -Mvb $id
499 echo -e '\n\n##############################\n'
500 done | tee -a /tmp/frozen
501 }
502 frozenrm() {
503 local ids=()
504 while read -r line; do
505 printf '%s\n' "$line"
506 ids+=($(printf '%s\n' "$line" |gr frozen|awk '{print $3}'))
507 done < <(s mailq)
508 echo "sleeping for 2 in case you change your mind"
509 sleep 2
510 s exim -Mrm "${ids[@]}"
511 }
512
513 funce() {
514 # like -e for functions. returns on error.
515 # at the end of the function, disable with:
516 # trap ERR
517 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
518 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
519 trap ERR
520 return' ERR
521 }
522
523 getdir () {
524 local help="Usage: getdir [--help] PATH
525 Output the directory of PATH, or just PATH if it is a directory."
526 if [[ $1 == --help ]]; then
527 echo "$help"
528 return 0
529 fi
530 if [[ $# -ne 1 ]]; then
531 echo "getdir error: expected 1 argument, got $#"
532 return 1
533 fi
534 if [[ -d $1 ]]; then
535 echo "$1"
536 else
537 local dir
538 dir="$(dirname "$1")"
539 if [[ -d $dir ]]; then
540 echo "$dir"
541 else
542 echo "getdir error: directory does not exist"
543 return 1
544 fi
545 fi
546 }
547
548 git_empty_branch() { # start an empty git branch. carefull, it deletes untracked files.
549 [[ $# == 1 ]] || { echo 'need a branch name!'; return 1;}
550 local root
551 root=$(gitroot) || return 1 # function to set gitroot
552 builtin cd "$root"
553 git symbolic-ref HEAD refs/heads/$1
554 rm .git/index
555 git clean -fdx
556 }
557
558 # shellcheck disable=SC2120
559 gitroot() {
560 local help="Usage: gitroot [--help]
561 Print the full path to the root of the current git repo
562
563 Handles being within a .git directory, unlike git rev-parse --show-toplevel,
564 and works in older versions of git which did not have that."
565 if [[ $1 == --help ]]; then
566 echo "$help"
567 return
568 fi
569 local p
570 p=$(git rev-parse --git-dir) || { echo "error: not in a git repo" ; return 1; }
571 [[ $p != /* ]] && p=$PWD
572 echo "${p%%/.git}"
573 }
574
575 gh() {
576 # i got an error, gh not found when doing a pull request, it seems like it wants itself in it\'s path.
577 local _oldpath="$PATH"
578 PATH="$PATH:$HOME/node_modules/.bin"
579 command gh "$@"
580 PATH="$_oldpath"
581 }
582
583 gmacs() {
584 # quit will prompt if the program crashes.
585 gdb -ex=r -ex=quit --args emacs "$@"; r;
586 }
587
588 gdkill() {
589 # kill the emacs daemon
590 pk1 emacs --daemon
591 }
592
593 gr() {
594 grep -iIP --color=auto "$@"
595 }
596
597 grr() { # grep recursive
598 if [[ ${#@} == 1 ]]; then
599 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -RiIP --color=auto "$@" .
600 else
601 grep --exclude-dir='*.emacs.d' --exclude-dir='*.git' -RiIP --color=auto "$@"
602 fi
603 }
604 rg() {
605 command rg -i -M 200 "$@"
606 }
607
608 hr() { # horizontal row. used to break up output
609 printf "$(tput setaf 5)â–ˆ$(tput sgr0)%.0s" $(seq ${COLUMNS:-60})
610 echo
611 }
612
613 hrcat() { local f; for f; do [[ -f $f ]] || continue; hr; echo "$f"; cat "$f"; done }
614
615 # get latest hub and run it
616 # main command to use:
617 # hub pull-request --no-edit
618 # --no-edit means to use the first commit\'s message as the pull request message.
619 # Also, you need to use a feature branch, not master in your fork.
620 # On first use, you input username/pass and it gets an oath token so you dont have to repeat
621 # it\'s at ~/.config/hub
622 hub() {
623 local up uptar updir p
624 p=/github/hub/releases/
625 up=https://github.com/$(curl -s https://github.com$p| grep -o $p'download/[^/]*/hub-linux-amd64[^"]*' | head -n1)
626 uptar=${up##*/}
627 updir=${uptar%.tgz}
628 if [[ ! -e /a/opt/$updir ]]; then
629 rm -rf /a/opt/hub-linux-amd64*
630 wget -P /a/opt $up
631 tar -C /a/opt -zxf /a/opt/$uptar
632 rm -f /a/opt/$uptar
633 s /a/opt/$updir/install
634 fi
635
636 # save token across computers
637 if [[ ! -L ~/.config/hub ]]; then
638 if [[ -e ~/.config/hub ]]; then
639 mv ~/.config/hub /p/c/subdir_files/.config/
640 fi
641 if [[ -e /p/c/subdir_files/.config/hub ]]; then
642 conflink
643 fi
644 fi
645 command hub "$@"
646 }
647
648 i() { git "$@"; }
649 # modified from ~/local/bin/git-completion.bash
650 # other completion commands are mostly taken from bash_completion package
651 complete -o bashdefault -o default -o nospace -F _git i 2>/dev/null \
652 || complete -o default -o nospace -F _git i
653
654 if ! type service &>/dev/null; then
655 service() {
656 echo actually running: systemctl $2 $1
657 systemctl $2 $1
658 }
659 fi
660
661
662
663 ic() {
664 # fast commit all
665 git commit -am "$*"
666 }
667
668
669
670 ifn() {
671 # insensitive find
672 find -L . -not \( -name .svn -prune -o -name .git -prune \
673 -o -name .hg -prune -o -name .editor-backups -prune \
674 -o -name .undo-tree-history -prune \) -iname "*$**" 2>/dev/null
675 }
676
677 ipdrop() {
678 s iptables -A INPUT -s $1 -j DROP
679 }
680
681
682 istext() {
683 grep -Il "" "$@" &>/dev/null
684 }
685
686 jtail() {
687 journalctl -n 10000 -f "$@"
688 }
689 jr() { journalctl "$@" ; }
690 jrf() { journalctl -f "$@" ; }
691
692 l() {
693 if [[ $PWD == /[iap] ]]; then
694 command ls -A --color=auto -I lost+found "$@"
695 else
696 command ls -A --color=auto "$@"
697 fi
698 }
699
700
701 lcn() { locate -i "*$**"; }
702
703 lg() { LC_COLLATE=C.UTF-8 ll --group-directories-first; }
704
705 lt() { ll -tr "$@"; }
706
707 lld() { ll -d "$@"; }
708
709 low() { # make filenames lowercase, remove bad chars
710 local f new
711 for f in "$@"; do
712 new="${f,,}" # downcase
713 new="${new//[^[:alnum:]._-]/_}" # sub bad chars
714 new="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
715 new="${new%"${new##*[[:alnum:]]}"}"
716 # remove bad underscores, like __ and _._
717 new=$(echo $new | sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
718 safe_rename "$f" "$new" || return 1
719 done
720 return 0
721 }
722
723 lower() { # make first letter of filenames lowercase.
724 local x
725 for x in "$@"; do
726 if [[ ${x::1} == [A-Z] ]]; then
727 y=$(tr '[:upper:]' '[:lower:]' <<<"${x::1}")"${x:1}"
728 safe_rename "$x" "$y" || return 1
729 fi
730 done
731 }
732
733
734 k() { # history search
735 grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history} | tail -n 80;
736 }
737
738 ks() { # history search
739 grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history} | uniq;
740 }
741
742
743 make-targets() {
744 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
745 make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
746 }
747
748 mkc() {
749 mkdir "$1"
750 c "$1"
751 }
752
753 mkct() {
754 mkc $(mktemp -d)
755 }
756
757 mkt() { # mkdir and touch file
758 local path="$1"
759 mkdir -p "$(dirname "$path")"
760 touch "$path"
761 }
762
763 # shellcheck disable=SC2032
764 mkdir() { command mkdir -p "$@"; }
765
766 nopanic() {
767 # shellcheck disable=SC2024
768 sudo tee -a /var/log/exim4/paniclog-archive </var/log/exim4/paniclog; sudo truncate -s0 /var/log/exim4/paniclog
769 }
770
771 p8() { ping 8.8.8.8; }
772 p6() { ping6 2001:4860:4860::8888; }
773
774 pkx() { # package extract
775 local pkg cached tmp f
776 c $(mktemp -d)
777 pkg=$1
778 # shellcheck disable=SC2012
779 cached=$(ls -t /var/cache/apt/archives/$pkg* | tail -n1 2>/dev/null)
780 if [[ $cached ]]; then
781 cp $cached .
782 else
783 aptitude download $pkg || return 1
784 fi
785 tmp=(*); f=${tmp[0]} # only 1 expected
786 ex $f
787 rm -f $f
788 }
789
790 # pgrep and kill
791 pk1() {
792 local pid
793 pid=($(pgrep -f "$*"))
794 case ${#pid[@]} in
795 1)
796 # shellcheck disable=SC2128
797 {
798 ps -F $pid
799 m kill $pid
800 }
801 ;;
802 0) echo "no pid found" ;;
803 *)
804 ps -F ${pid[@]}
805 ;;
806 esac
807 }
808
809 pubip() { curl -4s https://icanhazip.com; }
810 pubip6() { curl -6s https://icanhazip.com; }
811 whatismyip() { pubip; }
812
813 pwgen() {
814 # -m = min length
815 # -x = max length
816 # -t = print pronunciation
817 apg -m 14 -x 17 -t
818 for (( i=0; i<10; i++ )); do
819 shuf -n3 /usr/share/hunspell/en_US.dic | sed 's,/.*,,' | paste -sd . -
820
821 done
822 }
823
824 pwlong() {
825 # -M CLN = use Caps, Lowercase, Numbers
826 # -n 1 = 1 password
827 # -a 1 = use random instead of pronounceable algorithm
828 apg -m 50 -x 70 -n 1 -a 1 -M CLN
829 }
830
831
832 q() { # start / launch a program in the backround and redir output to null
833 "$@" &> /dev/null &
834 }
835
836 # shellcheck disable=SC2120
837 r() {
838 history -a # save history
839 exit ${1:0}
840 # i had this redir, not sure why
841 # exit "$@" 2>/dev/null
842 }
843
844 rl() {
845 # rsync, root is required to keep permissions right.
846 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
847 # --no-times --delete
848 # basically, make an exact copy, use checksums instead of file times to be more accurate
849 rsync -ahvic --delete "$@"
850 }
851 rld() {
852 # like rlu, but dont delete files on the target end which
853 # do not exist on the original end.
854 rsync -ahvic "$@"
855 }
856 complete -F _rsync -o nospace rld rl rlt
857
858 rlt() {
859 # rl without preserving modification time.
860 rsync -ahvic --delete --no-t "$@"
861 }
862
863 rlu() { # [OPTS] HOST PATH
864 # eg. rlu -opts frodo /testpath
865 # relative paths will expanded with readlink -f.
866 opts=("${@:1:$#-2}") # 1 to last -2
867 path="${*:$#}" # last
868 host="${*:$#-1:1}" # last -1
869 if [[ $path == .* ]]; then
870 path=$(readlink -f $path)
871 fi
872 # rync here uses checksum instead of time so we dont mess with
873 # unison relying on time as much. g is for group, same reason
874 # to keep up with unison.
875 s rsync -rlpchviog --relative "${opts[@]}" "$path" "root@$host:/";
876 }
877
878 rmstrips() {
879 ssh fencepost head -n 300 /gd/gnuorg/EventAndTravelInfo/rms-current-trips.txt | less
880 }
881
882 s() {
883 # background
884 # I use a function because otherwise we cant use in a script,
885 # cant assign to variable.
886 #
887 # note: gksudo is recommended for X apps because it does not set the
888 # home directory to the same, and thus apps writing to ~ fuck things up
889 # with root owned files.
890 #
891 if [[ $EUID != 0 || $1 == -* ]]; then
892 SUDOD="$PWD" sudo -i "$@"
893 else
894 "$@"
895 fi
896 }
897
898 safe_rename() { # warn and dont rename if file exists.
899 # mv -n exists, but it\'s silent
900 if [[ $# != 2 ]]; then
901 echo safe_rename error: $# args, need 2 >2
902 return 1
903 fi
904 if [[ $1 != "$2" ]]; then # yes, we want to silently ignore this
905 if [[ -e $2 || -L $2 ]]; then
906 echo "Cannot rename $1 to $2 as it already exists."
907 else
908 mv -vi "$1" "$2"
909 fi
910 fi
911 }
912
913
914 sb() { # sudo bash -c
915 # use sb instead of s is for sudo redirections,
916 # eg. sb 'echo "ok fine" > /etc/file'
917 local SUDOD="$PWD"
918 sudo -i bash -c "$@"
919 }
920 complete -F _root_command s sb
921
922
923 ser() {
924 local s; [[ $EUID != 0 ]] && s=s
925 if type -p systemctl &>/dev/null; then
926 $s systemctl $1 $2
927 else
928 $s service $2 $1
929 fi
930 }
931 # like restart, but do nothing if its not already started
932 srestart() {
933 local service=$1
934 if [[ $(s systemctl --no-pager show -p ActiveState $service ) == ActiveState=active ]]; then
935 systemctl restart $service
936 fi
937 }
938
939 setini() { # set a value in a .ini style file
940 key="$1" value="$2" section="$3" file="$4"
941 if [[ -s $file ]]; then
942 sed -ri -f - "$file" <<EOF
943 # remove existing keys
944 / *\[$section\]/,/^ *\[[^]]+\]/{/^\s*$key[[:space:]=]/d}
945 # add key
946 /^\s*\[$section\]/a $key=$value
947 # from section to eof, do nothing
948 /^\s*\[$section\]/,\$b
949 # on the last line, if we haven't found section yet, add section and key
950 \$a [$section]\\
951 $key=$value
952 EOF
953 else
954 cat >"$file" <<EOF
955 [$section]
956 $key=$value
957 EOF
958 fi
959 }
960
961 sgo() { # service go
962 service=$1
963 ser restart $service || return 1
964 if type -p systemctl &>/dev/null; then
965 ser enable $service
966 fi
967 }
968
969 sgu() {
970 systemctl list-unit-files | rg "$@"
971 }
972
973
974 sk() {
975 # 2086: unquoted $var
976 # 2046: unquoted $(cmd)
977 # 2068: Double quote array expansions to avoid re-splitting elements.
978 # 2119: Functions with optional args get bad warnings when none are passed.
979 # 2033: too many false positives for thing that will never work, passing shell function to find.
980 # i had -x as an arg, but debian testing(stretch) doesn\'t support it
981 shellcheck -x -e 2086,2046,2068,2119,2033 "$@"
982 # had this before. not sure what it is 2119
983 }
984
985
986 slog() {
987 # log with script. timing is $1.t and script is $1.s
988 # -l to save to ~/typescripts/
989 # -t to add a timestamp to the filenames
990 local logdir do_stamp arg_base
991 (( $# >= 1 )) || { echo "arguments wrong"; return 1; }
992 logdir="/a/dt/"
993 do_stamp=false
994 while getopts "lt" option
995 do
996 case $option in
997 l ) arg_base=$logdir ;;
998 t ) do_stamp=true ;;
999 esac
1000 done
1001 shift $((OPTIND - 1))
1002 arg_base+=$1
1003 [[ -e $logdir ]] || mkdir -p $logdir
1004 $do_stamp && arg_base+=$(date +%F.%T%z)
1005 script -t $arg_base.s 2> $arg_base.t
1006 }
1007 splay() { # script replay
1008 #logRoot="$HOME/typescripts/"
1009 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
1010 scriptreplay "$1.t" "$1.s"
1011 }
1012
1013 sr() {
1014 # sudo redo. be aware, this command may not work right on strange distros or earlier software
1015 if [[ $# == 0 ]]; then
1016 sudo -E bash -c -l "$(history -p '!!')"
1017 else
1018 echo this command redos last history item. no argument is accepted
1019 fi
1020 }
1021
1022 srm () {
1023 # with -ll, less secure but faster.
1024 command srm -ll "$@"
1025 }
1026
1027 srun() {
1028 scp $2 $1:/tmp
1029 ssh $1 /tmp/${2##*/} $(printf "%q\n" "${@:2}")
1030 }
1031
1032
1033 swap() {
1034 local tmp
1035 tmp=$(mktemp)
1036 mv $1 $tmp
1037 mv $2 $1
1038 mv $tmp $2
1039 }
1040
1041 tclock() { # terminal clock
1042 local x
1043 clear
1044 date +%l:%_M
1045 len=60
1046 # this goes to full width
1047 #len=${1:-$((COLUMNS -7))}
1048 x=1
1049 while true; do
1050 if (( x == len )); then
1051 end=true
1052 d="$(date +%l:%_M) "
1053 else
1054 end=false
1055 d=$(date +%l:%M:%_S)
1056 fi
1057 echo -en "\r"
1058 echo -n "$d"
1059 for ((i=0; i<x; i++)); do
1060 if (( i % 6 )); then
1061 echo -n _
1062 else
1063 echo -n .
1064 fi
1065 done
1066 if $end; then
1067 echo
1068 x=1
1069 else
1070 x=$((x+1))
1071 fi
1072 sleep 5
1073 done
1074 }
1075
1076
1077 te() {
1078 # test existence / exists
1079 local ret=0
1080 for x in "$@"; do
1081 [[ -e "$x" || -L "$x" ]] || ret=1
1082 done
1083 return $ret
1084 }
1085
1086
1087 tx() { # toggle set -x, and the prompt so it doesnt spam
1088 if [[ $- == *x* ]]; then
1089 set +x
1090 PROMPT_COMMAND=prompt-command
1091 # disabled due to issue on stretch, running ll we get error. something
1092 # about the DEBUG trap is broken
1093 # if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
1094 # trap 'settitle "$BASH_COMMAND"' DEBUG
1095 # fi
1096 else
1097 # normally, i would just execute these commands in the function.
1098 # however, DEBUG is not inherited, so we need to run it outside a function.
1099 # And we want to run set -x afterwards to avoid spam, so we cram everything
1100 # in here, and then it will run after this function is done.
1101 #PROMPT_COMMAND='trap DEBUG; unset PROMPT_COMMAND; PS1="\w \$ "; set -x'
1102
1103 unset PROMPT_COMMAND
1104 PS1="\w \$ "
1105 set -x
1106 fi
1107 }
1108
1109 psnetns() {
1110 # show all processes in the network namespace $1.
1111 # blank entries appear to be subprocesses/threads
1112 local x netns
1113 netns=$1
1114 ps -w | head -n 1
1115 s find -L /proc/[1-9]*/task/*/ns/net -samefile /run/netns/$netns | cut -d/ -f5 | \
1116 while read -r l; do
1117 x=$(ps -w --no-headers -p $l);
1118 if [[ $x ]]; then echo "$x"; else echo $l; fi;
1119 done
1120 }
1121
1122 m() { printf "%s\n" "$*"; "$@"; }
1123
1124
1125 virshrm() {
1126 for x in "$@"; do virsh destroy "$x"; virsh undefine "$x"; done
1127 }
1128
1129 vm-set-listen(){
1130 local t
1131 t=$(mktemp)
1132 local vm=$1
1133 local ip=$2
1134 s virsh dumpxml $vm | sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
1135 sed -r "s/listen='[^']+/listen='$ip/"> $t
1136 s virsh undefine $vm
1137 s virsh define $t
1138 }
1139
1140
1141 vmshare() {
1142 vm-set-listen $1 0.0.0.0
1143 }
1144
1145
1146 vmunshare() {
1147 vm-set-listen $1 127.0.0.1
1148 }
1149
1150 # * misc stuff
1151
1152
1153 # temporary variables to test colorization
1154 # some copied from gentoo /etc/bash/bashrc,
1155 use_color=false
1156 # dircolors --print-database uses its own built-in database
1157 # instead of using /etc/DIR_COLORS. Try to use the external file
1158 # first to take advantage of user additions.
1159 safe_term=${TERM//[^[:alnum:]]/?} # sanitize TERM
1160 match_lhs=""
1161 [[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)"
1162 [[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(</etc/DIR_COLORS)"
1163 [[ -z ${match_lhs} ]] \
1164 && type -P dircolors >/dev/null \
1165 && match_lhs=$(dircolors --print-database)
1166 # test if our $TERM is in the TERM values in dircolor
1167 [[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true
1168
1169
1170 if ${use_color} && [[ $- == *i* ]]; then
1171
1172 term_bold="$(tput bold)"
1173 term_red="$(tput setaf 1)"
1174 term_green="$(tput setaf 2)"
1175 term_yellow="$(tput setaf 3)"
1176 term_purple="$(tput setaf 5)"
1177 term_nocolor="$(tput sgr0)" # no font attributes
1178
1179 # unused so far. commented for shellcheck
1180 # term_underl="$(tput smul)"
1181 # term_blue="$(tput setaf 4)"
1182 # term_cyan="$(tput setaf 6)"
1183
1184 fi
1185 # Try to keep environment pollution down, EPA loves us.
1186 unset safe_term match_lhs use_color
1187
1188 # * prompt
1189
1190
1191 if [[ $- == *i* ]]; then
1192
1193 # this needs to come before next ps1 stuff
1194 # this stuff needs bash 4, feb 2009,
1195 # old enough to no longer condition on $BASH_VERSION anymore
1196 shopt -s autocd
1197 shopt -s dirspell
1198 PS1='\w'
1199 if [[ $- == *i* ]] && [[ ! $RLC_INSIDE_EMACS ]]; then
1200 PROMPT_DIRTRIM=2
1201 bind -m vi-command B:shell-backward-word
1202 bind -m vi-command W:shell-forward-word
1203 fi
1204
1205 if [[ $SSH_CLIENT || $SUDO_USER ]]; then
1206 PS1="\h $PS1"
1207 fi
1208
1209 # emacs terminal has problems if this runs slowly,
1210 # so I've thrown a bunch of things at the wall to speed it up.
1211 prompt-command() {
1212 local return=$? # this MUST COME FIRST
1213 local ps_char ps_color
1214 unset IFS
1215 history -a # save history
1216
1217 case $return in
1218 0) ps_color="$term_purple"
1219 ps_char='\$'
1220 ;;
1221 1) ps_color="$term_green"
1222 ps_char="$return \\$"
1223 ;;
1224 *) ps_color="$term_yellow"
1225 ps_char="$return \\$"
1226 ;;
1227 esac
1228 if [[ ! -O . ]]; then # not owner
1229 if [[ -w . ]]; then # writable
1230 ps_color="$term_bold$term_red"
1231 else
1232 ps_color="$term_bold$term_green"
1233 fi
1234 fi
1235
1236 # faster than sourceing the file im guessing
1237 if [[ -e /dev/shm/iank-status ]]; then
1238 eval $(< /dev/shm/iank-status)
1239 fi
1240 if [[ ! $SSH_CLIENT && $MAIL_HOST != "$HOSTNAME" ]]; then
1241 ps_char="@ $ps_char"
1242 fi
1243 PS1="${PS1%"${PS1#*[wW]}"} \[$ps_color\]$ps_char\[$term_nocolor\] "
1244 }
1245 PROMPT_COMMAND=prompt-command
1246
1247 settitle () {
1248 if [[ $TERM == screen* ]]; then
1249 local title_escape="\033]..2;"
1250 else
1251 local title_escape="\033]0;"
1252 fi
1253 if [[ $0 != prompt-command ]]; then
1254 echo -ne "$title_escape$USER@$HOSTNAME ${PWD/#$HOME/~} "
1255 printf "%s" "$*"
1256 echo -ne "\007"
1257 fi
1258 }
1259
1260 # for titlebar.
1261 # condition from the screen man page i think.
1262 # note: duplicated in tx()
1263 # disabled. see note in tx
1264 # if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
1265 # trap 'settitle "$BASH_COMMAND"' DEBUG
1266 # else
1267 # trap DEBUG
1268 # fi
1269
1270 fi
1271
1272 # * stuff that makes sense to be at the end
1273
1274
1275 # best practice
1276 unset IFS
1277
1278 # shellcheck disable=SC1090
1279 [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*
1280
1281
1282 # ensure no bad programs appending to this file will have an affect
1283 return 0