Last active
June 29, 2024 22:56
-
-
Save arthurl/3bef7cd3bfd9c14e8a02ee1536a43bf1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- mode: shell-script; -*- | |
# Enable colours in `ls` for Darwin | |
if [[ "${OS_NAME}" == Darwin ]]; then | |
export CLICOLOR=1 | |
fi | |
# Use nano as editor | |
export EDITOR=nano | |
alias gs="git status -sb" | |
alias gl="git log --oneline --decorate --graph" | |
export FZF_DEFAULT_OPTS="--reverse -m --bind 'ctrl-r:toggle-sort'" | |
#------------------------------------------------------------------------------# | |
### Bash ### | |
# not attempt to search the PATH for possible completions when completion is | |
# attempted on an empty line | |
shopt -s no_empty_cmd_completion # bash>=2.04 only | |
# use "M-." to cycle through menu completions | |
bind '"\e.": menu-complete' | |
# use "M-," to cycle through menu completions backwards | |
bind '"\e,": menu-complete-backward' | |
#------------------------------------------------------------------------------# | |
### Bash history config ### | |
# save all lines of a multiple-line command in the same history entry. This | |
# allows easy re-editing of multi-line commands. | |
shopt -s cmdhist | |
# reedit a history substitution line if it failed | |
shopt -s histreedit | |
# edit a recalled history line before executing | |
shopt -s histverify | |
# Huge history. Doesn't appear to slow things down, so why not? | |
export HISTSIZE=999999999 | |
export HISTFILESIZE=$HISTSIZE | |
# Avoid duplicate entries | |
export HISTCONTROL="ignorespace" #ignoreboth:ignorespace:erasedups | |
# see Emacs format-time-string function manual | |
export HISTTIMEFORMAT='%d %b %y %l:%M:%p » ' | |
#------------------------------------------------------------------------------# | |
### Internal utilities ### | |
[[ "$(uname -s | grep -c CYGWIN)" -eq 1 ]] && OS_NAME="CYGWIN" || OS_NAME=$(uname -s) | |
## pclip -- Cross platform clipboard function | |
function pclip() { | |
if [[ "${OS_NAME}" == CYGWIN ]]; then | |
putclip "$@"; | |
elif [[ "${OS_NAME}" == Darwin ]]; then | |
pbcopy "$@"; | |
else | |
if command -v xsel >/dev/null 2>&1; then | |
xsel -ib "$@"; | |
elif command -v xclip >/dev/null 2>&1; then | |
xclip -selection c "$@"; | |
else | |
echo "Neither xsel or xclip is installed! Dumping to stdout:" | |
echo "$@" | |
fi | |
fi | |
} | |
#------------------------------------------------------------------------------# | |
### Bash history utilities ### | |
py_save_script=" | |
from itertools import chain | |
import re | |
import sys | |
seperatorRe = re.compile(r'^#0#{78}$') | |
saveTokenRe = re.compile(r' #$') | |
timestampRe = re.compile(r'^#\d+$') | |
save = [] | |
histIter = chain(sys.stdin, [None]) | |
for line in histIter: | |
if line is not None and seperatorRe.match(line): | |
break | |
buffer = [] | |
for line in histIter: | |
if line is None or timestampRe.match(line): | |
if len(buffer) > 1: | |
buffer[-1], nRep = saveTokenRe.subn('', buffer[-1], count=1) | |
if nRep != 0: | |
save.extend(buffer) | |
buffer = [] | |
buffer.append(line) | |
for line in save: | |
print(line, end='') | |
" | |
function save_history() { | |
nix-shell -p python3 --run "python -c \"${py_save_script}\"" < ~/.bash_history >> ~/.bash_history.save | |
cp --reflink=always ~/.bash_history.save ~/.bash_history | |
echo '#0##############################################################################' >> ~/.bash_history | |
} | |
py_filter_script=" | |
import re | |
import sys | |
import collections | |
reCmp = re.compile(r'^ ( *\d+ \d{2} [A-Z][a-z]{2} \d{2} [ \d]\d:\d{2}:[apAP][mM]) » (.*)$') | |
count = collections.OrderedDict() | |
prefix = {} | |
for line in sys.stdin: | |
m = reCmp.match(line) | |
if m: | |
cmd = m.group(2).strip() | |
if cmd in count: | |
count[cmd] += 1 | |
else: | |
count[cmd] = 1 | |
prefix[cmd] = m.group(1) | |
for cmd, n in count.items(): | |
print('{:>4d}× '.format(n) + prefix[cmd] + ' » ' + cmd) | |
" | |
function h() { | |
save_history | |
# options to pass to fzf | |
local fzf_h_opts=("--no-hscroll" "--no-sort") | |
# history printout format is modified in my sensible bash config | |
local post_filter='s/^ *[0-9][0-9]*× *[0-9][0-9]* [0-9]\{2\} [A-Z][a-z]\{2\} [0-9]\{2\} [ 0-9][0-9]:[0-9]\{2\}:[apAP][mM] » \(.*\)$/\1/p' | |
# reverse history, pick up one line, remove new line characters and put it into clipboard | |
if [[ -z "$1" ]]; then | |
history | sed '1!G;h;$!d' | nix-shell -p python3 --run "python -c \"${py_filter_script}\"" | fzf "${fzf_h_opts[@]}" | sed -n "${post_filter}" | tr -d '\n' | pclip | |
else | |
history | rg "$1" | sed '1!G;h;$!d' | nix-shell -p python3 --run "python -c \"${py_filter_script}\"" | fzf "${fzf_h_opts[@]}" | sed -n "${post_filter}" | tr -d '\n' | pclip | |
fi | |
} | |
#------------------------------------------------------------------------------# | |
### Special file navigation utilities ### | |
# See: http://blog.binchen.org/posts/how-to-do-the-file-navigation-efficiently.html | |
function baseff() { | |
local fullpath="$*" | |
local filename="${fullpath##*/}" # remove "/" from the beginning | |
filename="${filename##*./}" # remove ".../" from the beginning | |
# Only the filename without path is needed | |
# filename should be reasonable | |
local cli | |
cli=$(find . \( -iwholename '*#' -o -iwholename '*/deployment/*' -o -iwholename '*/_doc/*' -o -iwholename '*/test/*' -o -iwholename '*/coverage/*' -o -iwholename '*/.gradle/*' -o -iwholename '*~' -o -iwholename '*.swp' -o -iwholename '*/dist/*' -o -iwholename '*.class' -o -iwholename '*.js.html' -o -iwholename '*.elc' -o -iwholename '*.pyc' -o -iwholename '*/bin/*' -o -iwholename '*/.config/*' -o -iwholename '*/vendor/*' -o -iwholename '*/bower_components/*' -o -iwholename '*/node_modules/*' -o -iwholename '*/.svn/*' -o -iwholename '*/.git/*' -o -iwholename '*/.gitback/*' -o -iwholename '*/.npm/*' -o -iwholename '*.sass-cache*' -o -iwholename '*/.hg/*' \) -prune -o -type f -iwholename '*'"${filename}"'*' -print | fzf) | |
# convert relative path to full path | |
echo "$(cd "$(dirname "$cli")" || exit; pwd)"/"$(basename "$cli")" | |
} | |
function ff() { | |
local cli | |
cli="$(baseff "$*")" | |
echo "${cli}" | |
echo -n "${cli}" | pclip | |
} | |
#------------------------------------------------------------------------------# | |
### My custom functions ### | |
## pgen -- Generates passwords | |
# | |
# 1st arg: optional. Length of password. Default 32. | |
# | |
# 2nd arg: optional. Specifies characters to include. By default includes all | |
# printable ASCII characters except spaces. See `man tr` for list of valid | |
# character ranges. | |
function pgen() { | |
local passwd_len="${1:-32}" | |
local new_passwd | |
if [[ -z "${2}" ]]; then | |
# Password character range not specified | |
new_passwd=$(LC_ALL=C < /dev/urandom tr -cd "[:print:]" | tr -d ' ' | head -c "${passwd_len}") | |
else | |
# Password character range specified | |
new_passwd=$(LC_ALL=C < /dev/urandom tr -cd "[:print:]" | tr -d ' ' | tr -cd "${2}" | head -c "${passwd_len}") | |
fi | |
printf '%s' "${new_passwd}" | pclip && echo "${new_passwd}" | |
} | |
## mkcmd_b64reconstruct -- Constructs a shell command that reconstructs a file. | |
## Useful for pasting into a serial terminal. | |
# | |
# 1st arg: Filename. | |
function mkcmd_b64reconstruct () { | |
if [[ -f "${1}" ]]; then | |
local b64data | |
b64data=$(gzip < "${1}" | base64) | |
filename=$(basename "${1}") | |
echo "echo \"${b64data}\" | base64 --decode | gzip --decompress > ${filename}" | pclip | |
else | |
echo "ERROR: No file specified." | |
fi | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- mode: shell-script; -*- | |
#------------------------------------------------------------------------------# | |
### My custom functions ### | |
function sshu() { | |
local remote="${1:-arthur@luyten.arthur.li}" | |
local error_cmd="display notification 'Connection error!' with title \"sshuttle ${remote}\"" | |
# Note: the bracket below are only for clarity, and are not required (i.e. | |
# semantically identical without). | |
(sudo true && sshuttle --dns -r "${remote}" 0/0) || osascript -e "${error_cmd}" | |
} | |
## rename_import_email -- Rename .eml files. | |
# | |
# args: Filenames. | |
function rename_import_email() { | |
local - # paranoia; in case the python activate script sets shell options | |
( | |
source "/home/arthur/.cache/pypoetry/virtualenvs/arthurlib-oQ_WqyeL-py3.10/bin/activate" # requires python-dateutil package | |
python "/home/arthur/Git/bashrc_portable/email_import.py" "${@}" | |
) | |
} | |
## ipynb2pdf -- Prints ipython notebooks as PDFs | |
# | |
# args: Filenames. | |
function ipynb2pdf() { | |
local - # paranoia; in case the python activate script sets shell options | |
( | |
source "/home/arthur/.cache/pypoetry/virtualenvs/arthurlib-oQ_WqyeL-py3.10/bin/activate" # requires python-dateutil package | |
python "/home/arthur/Git/bashrc_portable/ipynb2pdf.py" "${@}" | |
) | |
} | |
## monitor_vpn -- Pings 2 targets, one through VPN and one without | |
# | |
# arg 1: Approx interval in integeral seconds. | |
# arg 2: Number of pings. | |
# arg 3: Host with VPN. | |
# arg 4: Host without VPN. | |
function monitor_vpn() { | |
local interval="${1:-3}" | |
local target_vpn="${3:-se1.socks.azirevpn.net}" | |
local target_local="${4:-192.168.1.254}" | |
for i in $(seq -w 1 "${2:-9999}"); do | |
date "+${i} %H:%M:%S" | |
local left_pad=' ' | |
# run in subshell to remove monitor messages | |
( (x=$(ping -t "${interval}" -c1 "${target_vpn}" | sed '2q;d'); echo "${left_pad}+: ${x:-${target_vpn} FAILED}") & ) | |
( (x=$(ping -t "${interval}" -c1 "${target_local}" | sed '2q;d'); echo "${left_pad}-: ${x:-${target_local} FAILED}") & ) | |
sleep "${interval}.1" | |
done | |
} | |
#------------------------------------------------------------------------------# | |
### ReMarkable ### | |
## md2rm -- Compile markdown to PDF. | |
# | |
# args: Filenames. | |
function md2rm() { | |
for ff in "${@}"; do | |
pandoc -s -f markdown+tex_math_single_backslash+smart --template ~/Git/tex-template/template.tex -o "$ff.pdf" "$ff" | |
done | |
} | |
## send2rm -- Send file to ReMarkable tablet. | |
# | |
# args: Filenames. | |
function send2rm() { | |
for ff in "${@}"; do | |
local fullpath | |
fullpath=$(realpath "${ff}") | |
local filename | |
filename=$(basename "${ff}") | |
echo "Uploading: ${fullpath}" | |
curl 'http://10.11.99.1/upload' -H 'Origin: http://10.11.99.1' -H 'Accept: */*' -H 'Referer: http://10.11.99.1/' -H 'Connection: keep-alive' -F "file=@${fullpath};filename=${filename};type=application/pdf" | |
echo "" | |
done | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from sys import argv | |
from email.parser import BytesHeaderParser | |
from dateutil import parser | |
from datetime import datetime, timedelta | |
from os import utime | |
from pathlib import Path | |
from hashlib import md5 | |
if __name__ == '__main__': | |
for filename in argv[1:]: | |
try: | |
with open(filename, mode='rb') as fp: | |
bMsg = fp.read() | |
msgDateStr = BytesHeaderParser().parsebytes(bMsg)['Date'] | |
msgHash = md5(bMsg).hexdigest() | |
except FileNotFoundError: | |
print('File', filename, 'not found!') | |
else: | |
if msgDateStr: | |
# Set mtime to 'Date' header | |
msgSentTime = parser.parse(msgDateStr).timestamp() | |
utime(filename, (msgSentTime, msgSentTime)) | |
# Rename email file to a standard name | |
currentTimestamp = int((datetime.utcnow() - datetime(1970, 1, 1)) / timedelta(microseconds=1)) | |
(tSeconds, tMilliseconds) = divmod(currentTimestamp, 1000000) | |
p = Path(filename).resolve() # just in case file is not in current directory | |
p.rename(p.parent / '{:d}.{:06d}.import_{:s}'.format(tSeconds, tMilliseconds, msgHash)) | |
else: | |
print('Cannot find sent date in', filename) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment