lots of updates
[distro-setup] / .bashrc
1 # to debug
2 #set -x
3 # redirect output to log file. this doesn't work. todo figure out why
4 #exec 1>>/a/tmp/bashlog
5 #exec 2>>/a/tmp/bashlog
6
7
8 # By default this file is sourced for ALL ssh commands. This is wonky.
9 # Normally, this file is not sourced when a script is run, but we can
10 # override that by having #!/bin/bash -l. I want something similar for ssh
11 # commands. when a local script runs an ssh command, this file should not be
12 # sourced by default, but we should be able to override that.
13 #
14 # so here we test for conditions of a script under ssh and return if so. To test
15 # for an overriding condition, we have a few options. one is to use an
16 # environment variable. env variables sent across ssh are strictly limited. ssh
17 # -t which sets $SSH_TTY, but within a script that won't work because tty
18 # allocation will fail. We could override an obscure unused LC_var, like
19 # telephone, but I don't want to run into some edge case where that messes
20 # things up. we could transfer a file which we could test for, but I can't think
21 # of a way to make that inherently limited to a single ssh command. I choose to
22 # set SendEnv and AcceptEnv ssh config vars to allow the environment variable
23 # BASH_LOGIN_SHELL to propagate across ssh.
24
25 # assume we want ssh commands to source this file if we are sourcing it,
26 # and we haven't specified otherwise already
27 [[ ! $BASH_LOGIN_SHELL ]] && export BASH_LOGIN_SHELL=true
28
29 # first conditions show that we are an ssh command without an interactive shell
30 if [[ $SSH_CONNECTION ]] \
31 && [[ $- == *c* ]] \
32 && [[ ! $SSH_TTY ]] \
33 && [[ $- != *i* ]] \
34 && [[ ! $BASH_LOGIN_SHELL == true ]]; then
35 return
36 else
37 source /etc/profile
38 fi
39
40
41
42 # note, to catch errors in functions but not outside, do:
43 # set -E -o pipefail
44 # trap return ERR
45 # trap 'trap ERR' RETURN
46
47
48
49 ############
50 # settings #
51 ############
52
53 CDPATH=.
54
55 # remove all aliases. aliases provided by the system tend to get in the way,
56 # for example, error happens if I try to define a function the same name as an alias
57 unalias -a
58
59 # remove gnome keyring warning messages
60 # there is probably a more proper way, but I didn't find any easily on google
61 # now using xfce+xmonad instead of vanilla xmonad, so disabling this
62 #unset GNOME_KEYRING_CONTROL
63
64 # use extra globing features.
65 shopt -s extglob
66 # include .files when globbing, but ignore files name . and ..
67 # setting this also sets dotglob
68 export GLOBIGNORE=*/.:*/..
69
70 # broken with bash_completion package. Saw a bug for this once. Don't anymore.
71 # still broken in wheezy
72 # still buggered in latest stable from the web, version 2.1
73 # perhaps its fixed in newer git version, which fails to make for me
74 # this note is from 6-2014.
75 # Also, enabling this before sourcing .bashrc makes PATH be empty.
76 #shopt -s nullglob
77
78 # make tab on an empty line do nothing
79 shopt -s no_empty_cmd_completion
80
81 # advanced completion
82 # http://bash-completion.alioth.debian.org/
83 # might be sourced by the system already, but I've noticed it not being sourced before
84 if ! type _init_completion &> /dev/null && [[ -r "/usr/share/bash-completion/bash_completion" ]]; then
85 . /usr/share/bash-completion/bash_completion
86 fi
87
88
89 # fix spelling errors for cd, only in interactive shell
90 shopt -s cdspell
91 # append history instead of overwritting it
92 shopt -s histappend
93 # for compatibility, per gentoo/debian bashrc
94 shopt -s checkwinsize
95 # attempt to save multiline single commands as single history entries.
96 shopt -s cmdhist
97 shopt -s globstar
98
99
100 # inside emacs fixes
101 if [[ $INSIDE_EMACS ]]; then
102 # EMACS is used by bash on startup, but we don't need it anymore.
103 # plus I hit a bug in a makefile which inherited it
104 unset EMACS
105 export INSIDE_EMACS
106 export PAGER=cat
107 export MANPAGER=cat
108 # scp completion does not work, but this doesn't fix it. todo, figure this out
109 complete -r scp &> /dev/null
110 # todo, remote file completion fails, figure out how to turn it off
111 fi
112
113
114 if [[ $- == *i* ]]; then
115 # for readline-complete.el
116 if [[ $INSIDE_EMACS ]]; then
117 # all for readline-complete.el
118 stty echo
119 bind 'set horizontal-scroll-mode on'
120 bind 'set print-completions-horizontally on'
121 bind '"\C-i": self-insert'
122 else
123 # arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
124 if [[ $TERM == "xterm" ]]; then
125 bind '"\e[1;5C": shell-forward-word' 2>/dev/null
126 bind '"\e[1;5D": shell-backward-word' 2>/dev/null
127 else
128 bind '"\eOc": shell-forward-word'
129 bind '"\eOd": shell-backward-word'
130 fi
131 # terminal keys: C-c, C-z. the rest defined by stty -a are, at least in
132 # gnome-terminal, overridden by bash, or disabled by the system
133 stty werase undef lnext undef stop undef start undef
134
135 fi
136
137 fi
138
139
140 # history number. History expansion is good.
141 PS4='$LINENO+ '
142 # history file size limit, set to unlimited.
143 # this needs to be different from the default because
144 # default HISTFILESIZE is 500 and could clobber our history
145 HISTFILESIZE=
146 # max commands 1 session can append/read from history
147 HISTSIZE=100000
148 # my own history size limit based on lines
149 HISTFILELINES=1000000
150 HISTFILE=$HOME/.bh
151 # the time format display when doing the history command
152 # also, setting this makes the history file record time
153 # of each command as seconds from the epoch
154 HISTTIMEFORMAT="%I:%M %p %m/%d "
155 # consecutive duplicate lines don't go in history
156 HISTCONTROL=ignoredups
157 # works in addition to HISTCONTROL to do more flexible things
158 # it could also do the same things as HISTCONTROL and thus replace it,
159 # but meh
160 HISTIGNORE='k *; *'
161
162 export BC_LINE_LENGTH=0
163
164
165 # note, if I use a machine I don't want files readable by all users, set
166 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
167
168 C_DEFAULT_DIR=/a
169
170
171 ###################
172 ## include files ###
173 ###################
174
175 for _x in /a/bin/distro-functions/src/* /a/bin/bash-programs-by-ian/repos/*/*-function; do
176 source "$_x"
177 done
178 unset _x
179 source /a/bin/semi-private # so I can share my bashrc
180 source $(dirname $(readlink -f $BASH_SOURCE))/path_add-function
181 path_add --ifexists --end /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
182 # todo, these need to be renamed to be less generic.
183 # sync overrode something else useful
184 #path_add $HOME/bin/bash-programs-by-ian/utils
185
186 [[ -e $HOME/mw_vars ]] && source $HOME/mw_vars
187
188 ###############
189 ### aliases ###
190 ###############
191
192 # disabled for now, but these are generally good
193 # if [[ $- == *i* ]]; then
194 # alias cp='cp -i'
195 # alias mv='mv -i'
196 # fi
197
198 # remove any default aliases for these
199 alias ls > /dev/null 2>&1 && unalias ls
200 alias ll > /dev/null 2>&1 && unalias ll
201 alias grep > /dev/null 2>&1 && unalias grep
202
203
204 mkdir() {
205 command mkdir -p "$@"
206 }
207
208
209 alias d='builtin bg'
210 complete -A stopped -P '"%' -S '"' d
211
212 alias hi='history'
213
214
215 # note: gksudo is recommended for X apps because it does not set the
216 # home directory to the same, and thus apps writing to ~ fuck things up
217 # with root owned files.
218
219 # background
220 # alias s='SUDOD="$PWD" sudo -i '
221 # because this is an alias, and the extra space at the end, it would allow
222 # aliases to be used with it. but aliases aren't used in scripts,
223 # better to eliminate inconsistencies. Plus, you can't do s=s; $s command
224 # with an alias, which I like to do in some functions
225 # extra space at the end allows aliases to work
226 s() {
227 if [[ $EUID != 0 || $1 == -* ]]; then
228 SUDOD="$PWD" sudo -i "$@"
229 else
230 "$@"
231 fi
232 }
233
234
235
236 if [[ $OS == Windows_NT ]]; then
237 alias ffs='cygstart "/c/Program Files (x86)/Mozilla Firefox/firefox.exe" -P scratch'
238 export DISPLAY=nt
239 alias j='command cygpath'
240 alias t='command cygstart'
241 alias cygstart='echo be quick, use the alias "t" instead :\)'
242 alias cygpath='echo be quick, use the alias "j" instead :\)'
243
244 fi
245
246
247
248
249
250 #####################
251 ### functions ####
252 #####################
253
254
255 sdf() {
256 c /sdx/test/sandbox/
257 }
258
259 debian_pick_mirror () {
260 # netselect-apt finds a fast mirror.
261 # but we need to replace the mirrors ourselves,
262 # because it doesn't do that. best it can do is
263 # output a basic sources file
264 # here we get the server it found, get the main server we use
265 # then substitute all instances of one for the other in the sources file
266 # and backup original to /etc/apt/sources.list-original.
267 # this is idempotent. the only way to identify debian sources is to
268 # note the original server, so we put it in a comment so we can
269 # identify it later.
270 local file=$(mktemp -d)/f # safe way to get file name without creating one
271 sudo netselect-apt -o "$file" || return 1
272 url=$(grep ^\\w $file | head -n1 | awk '{print $2}')
273 sudo cp -f /etc/apt/sources.list /etc/apt/sources.list-original
274
275 sudo sed -ri "/http.us.debian.org/ s@( *[^ #]+ +)[^ ]+([^#]+).*@\1$url\2# http.us.debian.org@" /etc/apt/sources.list
276 sudo apt-get update
277 }
278
279
280 a() {
281 beet "${@}"
282 }
283
284 dus() {
285 du -sh ${@:-*} | sort -h
286 }
287
288 t() {
289 local x
290 local -a args
291 if type -t trash-put >/dev/null; then
292 # skip args that don't exist, or else it's an err
293 for x in "$@"; do [[ ! -e $x ]] || args+=("$x"); done
294 [[ ! ${args[@]} ]] || trash-put "${args[@]}"
295 else
296 rm -rf "$@"
297 fi
298 }
299
300 ccat () { # config cat. see a config without extra lines.
301 grep '^\s*[^[:space:]#]' "$@"
302 }
303
304
305 if type ack-grep >/dev/null 2>&1; then
306 alias ack=ack-grep
307 fi
308
309 postconfin() {
310 local MAPFILE
311 mapfile -t
312 local s
313 [[ $EUID == 0 ]] || s=s
314 $s postconf -ev "${MAPFILE[@]}"
315 }
316
317
318 tclock() {
319 clear
320 date +%l:%_M
321 len=60
322 # this goes to full width
323 #len=${1:-$((COLUMNS -7))}
324 x=1
325 while true; do
326 if (( x == len )); then
327 end=true
328 d="$(date +%l:%_M) "
329 else
330 end=false
331 d=$(date +%l:%M:%_S)
332 fi
333 echo -en "\r"
334 echo -n "$d"
335 for ((i=0; i<x; i++)); do
336 if (( i % 6 )); then
337 echo -n _
338 else
339 echo -n .
340 fi
341 done
342 if $end; then
343 echo
344 x=1
345 else
346 x=$((x+1))
347 fi
348 sleep 5
349 done
350 }
351
352 gr() {
353 grep -iIP --color=auto "$@"
354 }
355
356 grr() {
357 if [[ ${#@} == 1 ]]; then
358 grep -riIP --color=auto "$@" .
359 else
360 grep -riIP --color=auto "$@"
361 fi
362 }
363
364 pub() {
365 rld /a/h/_site/ li:/var/www/iankelling.org/public_html
366 }
367
368 mkc() {
369 mkdir "$1"
370 c "$1"
371 }
372
373 bashrcpush () {
374 local startdir="$PWD"
375 cd ~
376 for x in "$@"; do
377 ssh $x mkdir -p bin/distro-functions/src
378 tar cz bin/bash-programs-by-ian bin/semi-private bin/distro-functions/src | ssh $x tar xz
379 done
380 cd $(mktemp -d)
381 command cp /a/c/repos/bash/!(.git) ~/.gitconfig .
382 for x in "$@"; do
383 tar cz * | ssh $x tar xz
384 done
385 cd "$startdir"
386 }
387
388 virshrm() {
389 for x in "$@"; do virsh destroy "$x"; virsh undefine "$x"; done
390 }
391
392 whatismyip() { curl ipecho.net/plain ; echo; }
393
394 # history search
395 k() { grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history} | tail -n 40; }
396
397 calc() { echo "scale=3; $*" | bc -l; }
398 # no having to type quotes, but also no command history:
399 clc() {
400 local x
401 read -r x
402 echo "scale=3; $x" | bc -l
403 }
404
405 # diff config files,
406 # setup for format of postfix
407 # option = stuff[,]
408 # [more stuff]
409 cdiff() {
410
411 local pastline
412 local unified="$(mktemp)"
413 local f1="$(mktemp)"
414 local f2="$(mktemp)"
415
416 _cdiff-prep "$1" "$f1"
417 _cdiff-prep "$2" "$f2"
418 cat "$f1" "$f2" | grep -Po '^[^=]+=' | sort | uniq > "$unified"
419 while IFS= read -r line; do
420 # the default bright red / blue doesn't work in emacs shell
421 dwdiff -cblue,red -A best -d " ," <(grep "^$line" "$f1" || echo ) <(grep "^$line" "$f2" || echo ) | colordiff
422 done < "$unified"
423 }
424
425 cim() {
426 git commit -m "$*"
427 }
428
429 cam() {
430 git commit -am "$*"
431 }
432
433 shellck() {
434 # 2086 = unquoted $var
435 # 2046 = unquoted $(cmd)
436 # i had -x as an arg, but debian testing(stretch) doesn't support it
437 shellcheck -e 2086,2046,2068,2006,2119 "$@"
438 }
439
440 # join options which are continued to multiples lines onto one line
441 _cdiff-prep() {
442 local first=true
443 grep -vE '^([ \t]*#|^[ \t]*$)' "$1" | while IFS= read -r line; do
444 # remove leading spaces/tabs. assumes extglob
445 if [[ $line == "[ ]*" ]]; then
446 line="${line##+( )}"
447 fi
448 if $first; then
449 pastline="$line"
450 first=false
451 elif [[ $line == *=* ]]; then
452 echo "$pastline" >> "$2"
453 pastline="$line"
454 else
455 pastline="$pastline $line"
456 fi
457 done
458 echo "$pastline" >> "$2"
459 }
460
461 # makes it so chown -R symlink affects the symlink and its target.
462 chown() {
463 if [[ $1 == -R ]]; then
464 shift
465 command chown -h "$@"
466 command chown "$@"
467 command chown -RH "$@"
468 else
469 command chown "$@"
470 fi
471 }
472
473
474
475 cgpl ()
476 {
477 if [[ $# == 0 ]]; then
478 cp /a/bin/data/COPYING .
479 else
480 cp /a/bin/data/COPYING "$@"
481 fi
482 }
483
484
485 dc() {
486 diff --strip-trailing-cr -w "$@" # diff content
487 }
488
489
490 dt() {
491 date "+%A, %B %d, %r" "$@"
492 }
493
494
495 e() { echo "$@"; }
496
497
498 envload() { # load environment from a previous: export > file
499 local file=${1:-$HOME/.${USER}_env}
500 eval "$(export | sed 's/^declare -x/export -n/')"
501 while IFS= read -r line; do
502 # declare -x makes variables local to a function
503 eval ${line/#declare -x/export}
504 done < "$file"
505 }
506
507
508
509 # havn't tested these:
510 #file cut copy and paste, like the text buffers :)
511 _fbufferinit() { # internal use by
512 ! [[ $my_f_tempdir ]] && my_f_tempdir=$(mktemp -d)
513 rm -rf "$my_f_tempdir"/*
514 }
515 fcp() { # file cp
516 _fbufferinit
517 cp "$@" "$my_f_tempdir"/
518 }
519 fct() { # file cut
520 _fbufferinit
521 mv "$@" "$my_f_tempdir"/
522 }
523 fpst() { # file paste
524 [[ $2 ]] && { echo too many arguments; return 1; }
525 target=${1:-.}
526 cp "$my_f_tempdir"/* "$target"
527 }
528
529
530 # find array. make an array of file names found by find into $x
531 # argument: find arguments
532 # return: find results in an array $x
533 fa() {
534 while read -rd ''; do
535 x+=("$REPLY");
536 done < <(find "$@" -print0);
537 }
538
539
540 git_empty_branch() { # start an empty git branch. carefull, it deletes untracked files.
541 [[ $# == 1 ]] || { echo 'need a branch name!'; return 1;}
542 local gitroot
543 gitroot || return 1 # function to set gitroot
544 builtin cd "$gitroot"
545 git symbolic-ref HEAD refs/heads/$1
546 rm .git/index
547 git clean -fdx
548 }
549
550
551 ff() {
552 if type -P firefox &>/dev/null; then
553 firefox "$@"
554 else
555 iceweasel "$@"
556 fi
557 }
558
559 fw() {
560 firefox -P default "$@" >/dev/null 2>&1
561 }
562
563 fn() {
564 firefox -P alt "$@" >/dev/null 2>&1
565 }
566
567
568
569
570
571 # horizontal row. used to break up output
572 hr() { printf "$(tput setaf 5)â–ˆ$(tput sgr0)%.0s" $(seq $COLUMNS); }
573
574
575 i() {
576 git "$@"
577 }
578 # modified from ~/local/bin/git-completion.bash
579 # other completion commands are mostly taken from bash_completion package
580 complete -o bashdefault -o default -o nospace -F _git i 2>/dev/null \
581 || complete -o default -o nospace -F _git i
582
583 # fast commit all
584 ic() {
585 git commit -am "$*"
586 }
587
588 # insensitive find
589 ifn () {
590 find -L . -iname "*$**" 2>/dev/null
591 }
592
593
594
595
596 l() {
597 if [[ $PWD == /[iap] ]]; then
598 command ls -A --color=auto -I lost+found "$@"
599 else
600 command ls -A --color=auto "$@"
601 fi
602 }
603
604
605
606 lld() { ll -d "$@"; }
607
608
609 low() { # make filenames all lowercase
610 local x y
611 for x in "$@"; do
612 y=$(tr "[A-Z]" "[a-z]" <<<"$x")
613 [[ $y != $x ]] && mv "$x" "$y"
614 done
615 }
616
617
618 lower() { # make first letter of filenames lowercase.
619 local x
620 for x in "$@"; do
621 if [[ ${x::1} == [A-Z] ]]; then
622 y=$(tr "[A-Z]" "[a-z]" <<<"${x::1}")"${x:1}"
623 safe_rename "$x" "$y"
624 fi
625 done
626 }
627
628 safe_rename() {
629 if [[ $# != 2 ]]; then
630 echo safe_rename error: $# args, need 2 >2
631 return 1
632 elif [[ $1 != $2 ]]; then
633 if [[ -e $2 ]]; then
634 echo Cannot rename "$1" to "$2" as it already exists.
635 else
636 mv "$1" "$2"
637 fi
638 fi
639 }
640
641 despace() {
642 local x y
643 for x in "$@"; do
644 y="${x// /_}"
645 safe_rename "$x" "$y"
646 done
647 }
648
649
650
651
652
653 # test existence / exists
654 te() {
655 local ret=0
656 for x in "$@"; do
657 [[ -e "$x" || -L "$x" ]] || ret=1
658 done
659 return $ret
660 }
661
662 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
663 make-targets() {
664 make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
665 }
666
667
668 # fix root file ownership for FILE argument.
669 # check if parent or grandparent is not root and if the dir of FILE is also
670 # owned by that user, and change ownership to that user
671 perm_fix() {
672 local parent
673 if [[ $EUID == 0 ]]; then
674 te "$1" || touch "$1"
675 if [[ $(stat -c "%u" "$1") == 0 ]] ; then
676 argdir=$(dirname "$1")
677 if [[ $(stat -c "%u" "$argdir") != 0 ]] ; then
678 if ! chown "--reference=$argdir" "$1"; then
679 echo failed to fix bad ownership file permissons
680 return 1
681 fi
682 fi
683 fi
684 fi
685 }
686
687 # see notes for usage
688 fsdiff () {
689 local missing=false
690 local dname="${PWD##*/}"
691 local m="/a/tmp/$dname-missing"
692 local d="/a/tmp/$dname-diff"
693 [[ -e $d ]] && rm "$d"
694 [[ -e $m ]] && rm "$m"
695 local msize=0
696 local fsfile
697 while read -r line; do
698 fsfile="$1${line#.}"
699 if [[ -e "$fsfile" ]]; then
700 md5diff "$line" "$fsfile" && tee -a "/a/tmp/$dname-diff" <<< "$fsfile $line"
701 else
702 missing=true
703 echo "$line" >> "$m"
704 msize=$((msize + 1))
705 fi
706 done < <(find -type f )
707 if $missing; then
708 echo "$m"
709 (( msize <= 100 )) && cat $m
710 fi
711 }
712
713 fsdiff-test() {
714 cd $(mktemp -d)
715 echo ok > a
716 echo nok > b
717 mkdir c
718 echo ok > c/d
719 local x=$(mktemp -d)
720 mkdir $x/c
721 echo different > $x/c/d
722 echo ok > $x/a
723 fsdiff $x
724 }
725 # expected output, with different tmp dirs
726 # /tmp/tmp.HDPbwMqdC9/c/d ./c/d
727 # /a/tmp/tmp.qLDkYxBYPM-missing
728 # ./b
729
730
731 # test whether missing files were renamed, generally for use with fsdiff
732 # $1 = fsdiff output file, $2 = directory to compare to. pwd = fsdiff dir
733 # echos non-renamed files
734 rename-test() {
735 local x y found
736 unset sums
737 for x in "$2"/*; do
738 { sums+=( "$(md5sum < "$x")" ) ; } 2>/dev/null
739 done
740 while read -r line; do
741 { missing_sum=$(md5sum < "$line") ; } 2>/dev/null
742 renamed=false
743 for x in "${sums[@]}"; do
744 if [[ $missing_sum == "$x" ]]; then
745 renamed=true
746 break
747 fi
748 done
749 $renamed || echo "$line"
750 done < "$1"
751 return 0
752 }
753
754
755 # get a random string
756 #par2rbase=$(head -c 50 /dev/urandom | tr -cd '[:alpha:]')
757 par2r() {
758 [[ $par2rbase ]] || { echo "set \$par2rbase first"; return 1; }
759 d="$PWD"
760 for x in **; do
761 if [[ -d $x ]]; then
762 cd "$x"
763 par2 c QDLDeDJ6z4.par2 *
764 cd "$d"
765 fi
766 done
767
768 }
769
770 md5diff() {
771 [[ $(md5sum < "$1") != $(md5sum < "$2") ]]
772 }
773
774
775 pfind() { #find *$1* in $PATH
776 [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
777 local pathArray
778 IFS=: pathArray=($PATH); unset IFS
779 find "${pathArray[@]}" -iname "*$1*"
780 }
781
782 # do pwd + some other info.
783 #pwd() { echo "$(ll -d "$PWD") $USER@$HOSTNAME $(date +%r)"; }
784
785 q() { # start / launch a program in the backround and redir output to null
786 "$@" &> /dev/null &
787 }
788
789 pwgen() {
790 apg -s -m 10 -x 14 -t
791 }
792
793 r() {
794 exit "$@" 2>/dev/null
795 }
796
797 # connect to vms made with virt-install
798 vspicy() {
799 spicy -p $(sudo virsh dumpxml "$1"|grep "<graphics.*type='spice'"|\
800 sed -r "s/.*port='([0-9]+).*/\1/")
801 }
802 rspicy() { # HOST DOMAIN
803 local port=$(ssh $1<<EOF
804 sudo virsh dumpxml $2|grep "<graphics.*type='spice'" | \
805 sed -rn "s/.*port='([0-9]+).*/\1/p"
806 EOF
807 )
808 if [[ $port ]]; then
809 spicy -h $1 -p $port
810 else
811 echo "error: no port found. check that the domain is running."
812 fi
813 }
814
815
816 # trash-restore lists everything that has been trashed at or below CWD
817 # This picks out files just in CWD, not subdirectories,
818 # which also match grep $1, usually use $1 for a time string
819 # which you get from running restore-trash once first
820 pick-trash() {
821 local name x ask
822 local nth=1
823 # last condition is to not ask again for ones we skipped
824 while name="$( echo | restore-trash | gr "$PWD/[^/]\+$" | gr "$1" )" \
825 && [[ $name ]] && (( $(wc -l <<<"$name") >= nth )); do
826 name="$(echo "$name" | head -n $nth | tail -n 1 )"
827 read -p "$name [Y/n] " ask
828 if [[ ! $ask || $ask == [Yy] ]]; then
829 x=$( echo "$name" | gr -o "^\s*[0-9]*" )
830 echo $x | restore-trash > /dev/null
831 elif [[ $ask == [Nn] ]]; then
832 nth=$((nth+1))
833 else
834 return
835 fi
836 done
837 }
838
839 # rsync, root is required to keep permissions right.
840 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
841 # --no-times --delete
842 # basically, make an exact copy, use checksums instead of file times to be more accurate
843 rl() { rsync -ahvic --delete "$@"; }
844 # don't delete files on the target end which do not exist on the original end:
845 rld() { rsync -ahvic "$@"; }
846 # useful for selectively sending dirs which have been synced with unison,
847 # where the path is the same on both hosts.
848 rlu() { # [OPTS] HOST PATH
849 # eg rlu -opts frodo testpath
850 o=o
851 opts=("${@:1:$#-2}") # 1 to last -2
852 path="${@:$#}"
853 host="${@:$#-1:1}"
854 if [[ $host == lj ]]; then
855 o=
856 fi
857 # rync options are without -g for group
858 s rsync -rlpthvi$o "${opts[@]}" "$path" "root@$host:$path";
859 }
860
861 lcn() { locate -i "*$**"; }
862
863 vpn() {
864 s systemctl start openvpn@client&
865 journalctl --unit=openvpn@client -f -n0
866 }
867 vpnoff() {
868 s systemctl stop openvpn@client
869 }
870
871
872 complete -F _rsync -o nospace rld rlt fl
873 # rl without preserving modification time. for some reason I had this as default before.
874 # perhaps that reason will come up again and I will document it.
875 rlt() { rsync -ahvic --delete --no-t "$@"; }
876
877 if ! type service &>/dev/null; then
878 service() {
879 echo actually running: systemctl $2 $1
880 systemctl $2 $1
881 }
882 fi
883
884 # use sb instead of s is for sudo redirections, eg. sb 'echo "ok fine" > /etc/file'
885 sb() {
886 local SUDOD="$PWD"
887 sudo -i bash -c "$@"
888 }
889 complete -F _root_command s sb
890
891 # use -ll, less secure but faster.
892 srm () {
893 command srm -ll "$@"
894 }
895
896 # sudo redo. be aware, this command may not work right on strange distros or earlier software
897 sr() {
898 if [[ $# == 0 ]]; then
899 sudo -E bash -c -l "$(history -p '!!')"
900 else
901 echo this command redos last history item. no argument is accepted
902 fi
903 }
904
905 rbpipe() { rbt post -o --diff-filename=- "$@"; }
906 rbp() { rbt post -o "$@"; }
907
908
909 # log with script. timing is $1.t and script is $1.s
910 # -l to save to ~/typescripts/
911 # -t to add a timestamp to the filenames
912 slog() {
913 local logdir do_stamp arg_base
914 (( $# >= 1 )) || { echo "arguments wrong"; return 1; }
915 logdir="/a/dt/"
916 do_stamp=false
917 while getopts "lt" option
918 do
919 case $option in
920 l ) arg_base=$logdir ;;
921 t ) do_stamp=true ;;
922 esac
923 done
924 shift $(($OPTIND - 1))
925 arg_base+=$1
926 [[ -e $logdir ]] || mkdir -p $logdir
927 $do_stamp && arg_base+=$(date +%F.%T%z)
928 script -t $arg_base.s 2> $arg_base.t
929 }
930 splay() { # script replay
931 #logRoot="$HOME/typescripts/"
932 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
933 scriptreplay "$1.t" "$1.s"
934 }
935
936
937 # like -e for functions. returns on error.
938 # at the end of the function, disable with:
939 # trap ERR
940 funce() {
941 trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
942 ${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\" }\$?=$?"
943 trap ERR
944 return' ERR
945 }
946
947 # timer in minutes
948 tm() {
949 (sleep $(calc "$@ * 60") && mpv --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
950 }
951
952 ser() {
953 local s; [[ $EUID != 0 ]] && s=sudo
954 if type -p systemctl &>/dev/null; then
955 $s systemctl $1 $2
956 else
957 $s service $2 $1
958 fi
959 }
960
961 sgo() { # service go
962 service=$1
963 ser start $service
964 if type -p systemctl &>/dev/null; then
965 ser enable $service
966 fi
967 }
968
969
970 vm-set-listen(){
971 local t=$(mktemp)
972 local vm=$1
973 local ip=$2
974 s virsh dumpxml $vm | sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
975 sed -r "s/listen='[^']+/listen='$ip/"> $t
976 s virsh undefine $vm
977 s virsh define $t
978 }
979
980
981 vmshare() {
982 vm-set-listen $1 0.0.0.0
983 }
984
985 vmunshare() {
986 vm-set-listen $1 127.0.0.1
987 }
988
989
990 tu() {
991 local s;
992 local dir="$(dirname "$1")"
993 if [[ -e $1 && ! -w $1 || ! -w $(dirname "$1") ]]; then
994 s=s;
995 fi
996 $s teeu "$@"
997 }
998 ts() { # start editing a new file
999 [[ $# != 1 ]] && echo "I need a filename." && return 1
1000 local quiet
1001 if [[ $- != *i* ]]; then
1002 quiet=true
1003 fi
1004 if [[ $1 == *.c ]]; then
1005 e '#include <stdio.h>' >"$1"
1006 e '#include <stdlib.h>' >>"$1"
1007 e 'int main(int argc, char * argv[]) {' >>"$1"
1008 e ' printf( "hello world\n");' >>"$1"
1009 e ' return 0;' >>"$1"
1010 e '}' >>"$1"
1011 e "${1%.c}: $1" > Makefile
1012 e " g++ -ggdb -std=gnu99 -o ${1%.c} $<" >> Makefile
1013 e "#!/bin/bash" >run.sh
1014 e "./${1%.c}" >>run.sh
1015 chmod +x run.sh
1016 elif [[ $1 == *.java ]]; then
1017 e "public class ${1%.*} {" >"$1"
1018 e ' public static void main(String[] args) {' >>"$1"
1019 e ' System.out.println("Hello, world!");' >>"$1"
1020 e ' }' >>"$1"
1021 e '}' >>"$1"
1022
1023 else
1024 echo "#!/bin/bash" > "$1"
1025 chmod +x "$1"
1026 fi
1027 [[ $quiet ]] || g "$1"
1028
1029 }
1030
1031 ediff() {
1032 [[ ${#@} == 2 ]] || { echo "error: ediff requires 2 arguments"; return 1; }
1033 emacs --eval "(ediff-files \"$1\" \"$2\")"
1034 }
1035
1036 tx() { # toggle set -x, and the prompt so it doesn't spam
1037 if [[ $- == *x* ]]; then
1038 set +x
1039 PROMPT_COMMAND=prompt_command
1040 else
1041 unset PROMPT_COMMAND
1042 PS1="\w \$ "
1043 set -x
1044 fi
1045 }
1046
1047
1048 dat() { # do all tee, for more complex scripts
1049 tee >(ssh frodo bash -l) >(bash -l) >(ssh x2 bash -l) >(ssh tp bash -l)
1050 }
1051 da() { # do all
1052 local host
1053 "$@"
1054 for host in x2 tp treetowl; do
1055 ssh $host "$@"
1056 done
1057
1058 }
1059
1060
1061 istext() {
1062 grep -Il "" "$@" &>/dev/null
1063 }
1064
1065 if [[ $OS == Windows_NT ]]; then
1066 # cygstart wrapper
1067 cs() {
1068 cygstart "$@" &
1069 }
1070 xp() {
1071 explorer.exe .
1072 }
1073 # launch
1074 o() {
1075 local x=(*$1*)
1076 (( ${#x[#]} > 1 )) && { echo "warning ${#x[#]} matches found"; sleep 1; }
1077 cygstart *$1* &
1078 }
1079 else
1080 o() {
1081 if type gvfs-open &> /dev/null ; then
1082 gvfs-open "$@"
1083 else
1084 xdg-open "$@"
1085 fi
1086 # another alternative is run-mailcap
1087 }
1088 fi
1089
1090 khfix() { # known hosts fix
1091 ssh-keygen -R $1
1092 local x=$(host $1)
1093 ssh-keygen -R ${x##* }
1094 ssh $1 :
1095 }
1096
1097
1098 # todo, update this
1099 complete -F _longopt la lower low rlt rld rl lld ts ll dircp ex fcp fct fpst gr
1100
1101
1102
1103 hl() { # history limit. Write extra history to archive file.
1104 local max_lines linecount tempfile prune_lines x
1105 local harchive="${HISTFILE}_archive"
1106 for x in "$HISTFILE" "$harchive"; do
1107 [[ -e $x ]] || { touch "$x" && echo "notice from hl(): creating $x"; }
1108 if [[ ! $x || ! -e $x || ! -w $x || $(stat -c "%u" "$x") != $EUID ]]; then
1109 echo "error in hl: history file \$x:$x no good"
1110 return 1
1111 fi
1112 done
1113 history -a # save history
1114 max_lines=$HISTFILELINES
1115 [[ $max_lines =~ ^[0-9]+$ ]] || { echo "error in hl: failed to get max line count"; return 1; }
1116 linecount=$(wc -l < $HISTFILE) # pipe so it doesn't output a filename
1117 [[ $linecount =~ ^[0-9]+$ ]] || { echo "error in hl: wc failed"; return 1; }
1118 if (($linecount > $max_lines)); then
1119 prune_lines=$(($linecount - $max_lines))
1120 head -n $prune_lines "$HISTFILE" >> "$harchive" \
1121 && sed -ie "1,${prune_lines}d" $HISTFILE
1122 fi
1123 }
1124
1125 ############## work stuff #############
1126
1127 rn() {
1128 pushd /sdx/test/sandbox/;
1129 ./setup.sh && ./run.sh
1130 popd
1131 }
1132
1133 vrm() {
1134 virsh destroy $1
1135 virsh undefine $1
1136 }
1137
1138
1139
1140
1141 ############# some other section
1142 if [[ $- == *i* ]]; then
1143 # commands to run when bash exits normally
1144 trap "hl; _smh" EXIT
1145 fi
1146
1147
1148 # temporary variables to test colorization
1149 # some copied from gentoo /etc/bash/bashrc,
1150 use_color=false
1151 # dircolors --print-database uses its own built-in database
1152 # instead of using /etc/DIR_COLORS. Try to use the external file
1153 # first to take advantage of user additions.
1154 safe_term=${TERM//[^[:alnum:]]/?} # sanitize TERM
1155 match_lhs=""
1156 [[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)"
1157 [[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(</etc/DIR_COLORS)"
1158 [[ -z ${match_lhs} ]] \
1159 && type -P dircolors >/dev/null \
1160 && match_lhs=$(dircolors --print-database)
1161 # test if our $TERM is in the TERM values in dircolor
1162 [[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true
1163
1164
1165 if ${use_color} && [[ $- == *i* ]]; then
1166
1167 if [[ $XTERM_VERSION == Cygwin* ]]; then
1168 get_term_color() {
1169 for x in "$@"; do
1170 case $x in
1171 underl) echo -n $'\E[4m' ;;
1172 bold) echo -n $'\E[1m' ;;
1173 red) echo -n $'\E[31m' ;;
1174 green) echo -n $'\E[32m' ;;
1175 blue) echo -n $'\E[34m' ;;
1176 cyan) echo -n $'\E[36m' ;;
1177 yellow) echo -n $'\E[33m' ;;
1178 purple) echo -n $'\E[35m' ;;
1179 nocolor) echo -n $'\E(B\E[m' ;;
1180 esac
1181 done
1182 }
1183
1184 else
1185 get_term_color() {
1186 for x in "$@"; do
1187 case $x in
1188 underl) echo -n $(tput smul) ;;
1189 bold) echo -n $(tput bold) ;;
1190 red) echo -n $(tput setaf 1) ;;
1191 green) echo -n $(tput setaf 2) ;;
1192 blue) echo -n $(tput setaf 4) ;;
1193 cyan) echo -n $(tput setaf 6) ;;
1194 yellow) echo -n $(tput setaf 3) ;;
1195 purple) echo -n $(tput setaf 5) ;;
1196 nocolor) echo -n $(tput sgr0) ;; # no font attributes
1197 esac
1198 done
1199 }
1200 fi
1201 else
1202 get_term_color() {
1203 :
1204 }
1205 fi
1206 # Try to keep environment pollution down, EPA loves us.
1207 unset safe_term match_lhs use_color
1208
1209
1210
1211
1212
1213
1214 ###############
1215 # prompt ######
1216 ###############
1217
1218
1219 if [[ $- == *i* ]]; then
1220 # git branch/status prompt function
1221 if [[ $OS != Windows_NT ]]; then
1222 GIT_PS1_SHOWDIRTYSTATE=true
1223 fi
1224 # arch source lopip show -fcation
1225 [[ -r /usr/share/git/git-prompt.sh ]] && source /usr/share/git/git-prompt.sh
1226 # fedora/debian source
1227 [[ -r /usr/share/git-core/contrib/completion/git-prompt.sh ]] && source /usr/share/git-core/contrib/completion/git-prompt.sh
1228
1229 # in case we didn't source git-prompt.sh
1230 if ! declare -f __git_ps1 > /dev/null; then
1231 __git_ps1() {
1232 :
1233 }
1234 fi
1235
1236 # this needs to come before next ps1 stuff
1237 # this stuff needs bash 4, feb 2009,
1238 # old enough to no longer condition on $BASH_VERSION anymore
1239 shopt -s autocd
1240 shopt -s dirspell
1241 PS1='\w'
1242 if [[ $- == *i* ]] && [[ ! $INSIDE_EMACS ]]; then
1243 PROMPT_DIRTRIM=2
1244 bind -m vi-command B:shell-backward-word
1245 bind -m vi-command W:shell-forward-word
1246 fi
1247
1248 if [[ $SSH_CLIENT ]]; then
1249 PS1="\h $PS1"
1250 fi
1251
1252 prompt_command() {
1253 local return=$? # this MUST COME FIRST
1254 local psc pst
1255 local ps_char ps_color
1256 unset IFS
1257 history -a # save history
1258 if [[ ! $DESKTOP_SESSION == xmonad && $TERM == *(screen*|xterm*|rxvt*) ]]; then
1259 # from the screen man page
1260 if [[ $TERM == screen* ]]; then
1261 local title_escape="\033]..2;"
1262 else
1263 local title_escape="\033]0;"
1264 fi
1265 echo -ne "$title_escape${PWD/#$HOME/~} $USER@$HOSTNAME\007"
1266 fi
1267
1268 case $return in
1269 0) ps_color="$(get_term_color blue)"
1270 ps_char='\$'
1271 ;;
1272 1) ps_color="$(get_term_color green)"
1273 ps_char="$return \\$"
1274 ;;
1275 *) ps_color="$(get_term_color yellow)"
1276 ps_char="$return \\$"
1277 ;;
1278 esac
1279 if [[ ! -O . ]]; then # not owner
1280 if [[ -w . ]]; then # writable
1281 ps_color="$(get_term_color bold red)"
1282 else
1283 ps_color="$(get_term_color bold green)"
1284 fi
1285 fi
1286 PS1="${PS1%"${PS1#*[wW]}"} \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
1287 # emacs completion doesn't like the git prompt atm, so disabling it.
1288 #PS1="${PS1%"${PS1#*[wW]}"}$(__git_ps1 ' (%s)') \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
1289 }
1290 PROMPT_COMMAND=prompt_command
1291 fi
1292
1293
1294
1295
1296
1297 ###########################################
1298 # stuff that makes sense to be at the end #
1299 ###########################################
1300 if [[ "$SUDOD" ]]; then
1301 cd "$SUDOD"
1302 unset SUDOD
1303 elif [[ -d /a ]] && [[ $PWD == $HOME ]] && [[ $- == *i* ]]; then
1304 cd /a
1305 fi
1306
1307
1308 # best practice
1309 unset IFS
1310
1311
1312 # if someone exported $SOE, catch errors
1313 if [[ $SOE ]]; then
1314 errcatch
1315 fi
1316
1317 # I'd prefer to have system-wide, plus user ruby, due to bug in it
1318 # https://github.com/rubygems/rubygems/pull/1002
1319 # further problems: installing multi-user ruby and user ruby,
1320 # you don't get multi-user ruby when you sudo to root, unless its sudo -i.
1321 # There a third hybrid form, which passenger error suggested I use,
1322 # but it didn't actually work.
1323
1324 # in cased I never need this
1325 # rvm for non-interactive shell: modified from https://rvm.io/rvm/basics
1326 #if [[ $(type -t rvm) == file && ! $(type -t ruby) ]]; then
1327 # source $(rvm 1.9.3 do rvm env --path)
1328 #fi
1329
1330 # based on warning from rvmsudo
1331 export rvmsudo_secure_path=1
1332
1333
1334 if [[ -s "/usr/local/rvm/scripts/rvm" ]]; then
1335 source "/usr/local/rvm/scripts/rvm"
1336 elif [[ -s $HOME/.rvm/scripts/rvm ]]; then
1337 source $HOME/.rvm/scripts/rvm
1338 fi
1339
1340 # https://wiki.archlinux.org/index.php/Xinitrc#Autostart_X_at_login
1341 # i added an extra condition as gentoo xorg guide says depending on
1342 # $DISPLAY is fragile.
1343 if [[ ! $DISPLAY && $XDG_VTNR == 1 ]] && shopt -q login_shell && isarch; then
1344 exec startx
1345 fi
1346 # ensure no bad programs appending to this file will have an affect
1347 return 0