├── README.md ├── bin ├── carbon-x1-v6.sh └── gk8s ├── libs ├── bashrc.txt ├── com.sh ├── docker.sh ├── ensure.sh ├── exif.sh ├── git.sh ├── image.sh ├── ps.sh ├── ps1.sh ├── raw2mbox.sh ├── url.sh └── xml.sh ├── others └── gitconfig └── sample-main.sh /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | It's a collection of my `Bash` utils and functions and stuff. 4 | It can be used for educational purpose, or any purpose. 5 | 6 | * [gk8s](bin/gk8s): A Bash script to manage multiple k8s clusters, 7 | especially helpful for scripting purpose. It also helps to avoid 8 | accidental deletion by locking dangerous actions by default. 9 | 10 | **WARNING**: Please use a simple version rewritten in `golang` instead: 11 | https://github.com/icy/gk8s 12 | * [git_push_to_all_remotes](libs/git.sh): Push your code to all remotes, 13 | but skipping the remote you don't have permissions, and/or skip any 14 | protected/personal branches. And a few convenient shortcuts for `git`. 15 | * [ps1.sh](libs/ps1.sh): Well, I have been using this PS for more than 10 years. 16 | 17 | ## Author 18 | 19 | Ky-Anh Huynh 20 | 21 | ## License 22 | 23 | MIT 24 | -------------------------------------------------------------------------------- /bin/carbon-x1-v6.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Purpose : Booting up ThinkPad Carbon X1-v6 4 | # Author : Mr. Internet 5 | # Date : 2019-09-02 6 | 7 | set -x 8 | 9 | # Disabling the memory card reader 10 | echo "2-3" | sudo tee /sys/bus/usb/drivers/usb/unbind || true 11 | 12 | # Why? Who cares; it just works. 13 | echo N | sudo tee /sys/module/overlay/parameters/metacopy 14 | -------------------------------------------------------------------------------- /bin/gk8s: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat <<'EOF' 4 | This tool is deprecated. 5 | Please use the Golang version of this script instead: 6 | https://github.com/icy/gk8s 7 | EOF 8 | 9 | exit 0 10 | 11 | # Date : 2019-09-06 12 | # Author : Ky-Anh Huynh 13 | # Purpose : wrapper for all k8s commands/clusters 14 | # Source : https://github.com/icy/bashy/blob/master/bin/gk8s 15 | # TODOs : 16 | # 17 | # - [ ] Should migrate to enviroment use instead? 18 | # 19 | # Ideas 20 | # 21 | # 1. Hook up $HOME variable 22 | # 2. Explicitly avoid kind of human mistakes when dealing with 23 | # multiple clusters and namespaces 24 | # 3. To use with/by non-human scripts ;) 25 | # 26 | # Usage 27 | # 28 | # 1. Initialize clusters' root 29 | # 30 | # $ mkdir ~/projects/gk8s/ 31 | # $ cd ~/projects/gk8s/ 32 | # $ touch .gk8s 33 | # 34 | # 2. Provision aws configurations 35 | # 36 | # $ ln -s ~/.aws .aws 37 | # 38 | # If you don't do this, the script tries to create symlink automatically. 39 | # 40 | # 3. Provision k8s configurations 41 | # 42 | # $ ln -s ~/.kube .kube 43 | # 44 | # 5. Invoke the command 45 | # 46 | # $ gk8s get pods # on the default namespace, 47 | # # on namespace specified in `.gk8s` 48 | # $ gk8s get pods @% # on all namespaces 49 | # 50 | # You can move around and execute your alias command instead 51 | # 52 | # $ cd /my/parent/home 53 | # $ gk8s :cluster-name get pods 54 | # 55 | # `kubectl` is injected when you are not specifed them as your first 56 | # argument. E.g, `gk8s get pods` is the same as `gk8s kubectl get pods`. 57 | # 58 | # Other usage 59 | # 60 | # 1. Move around the root: use `GK8S_HOME` instead of ~/projects/gk8s/. 61 | # You can specify this in `~/.config/gk8s`. 62 | # 2. To allow debugging, use `VERBOSE=2 gk8s <...>` 63 | # 64 | 65 | _ver="${BASH_VERSINFO[0]}" 66 | if [[ "$_ver" -lt 4 ]]; then 67 | echo >&2 ":: $0: Sorry, Bash >=4 is required." 68 | exit 1 69 | fi 70 | 71 | _log2() { 72 | [[ "${VERBOSE:-0}" -ge 2 ]] || return 0 73 | echo >&2 ":: ${*}" 74 | } 75 | 76 | _help() { 77 | echo >&2 ":: Please specify a command." 78 | } 79 | 80 | GK8S_HOME="${GK8S_HOME:-$HOME/projects/gk8s}"; 81 | export GK8S_HOME; readonly GK8S_HOME 82 | 83 | # Try to learn if the first argument is about the cluster name 84 | first_arg="${1:-}" 85 | if [[ "${first_arg:0:1}" == ":" ]]; then 86 | shift 87 | _CLUSTER="${first_arg:1}" 88 | if [[ -z "$_CLUSTER" ]]; then 89 | # FIXME: remove duplicate code... 90 | _CLUSTER="$(pwd)" 91 | NHOME="$(pwd -P)" 92 | _CLUSTER="${_CLUSTER##*/}" 93 | else 94 | ( cd "$GK8S_HOME/$_CLUSTER/" || exit ; ) 95 | NHOME="$GK8S_HOME/$_CLUSTER/" 96 | fi 97 | else 98 | _CLUSTER="$(pwd)" 99 | NHOME="$(pwd -P)" 100 | _CLUSTER="${_CLUSTER##*/}" 101 | fi 102 | 103 | export NHOME; readonly NHOME 104 | 105 | if [[ ! -f "$NHOME/.gk8s" ]]; then 106 | echo >&2 "... Missing .gk8s file in working directory '$NHOME'." 107 | if [[ -f "$NHOME/.k8s" ]]; then 108 | echo >&2 ".... found .k8s file. Rename this file to .gk8s would help." 109 | fi 110 | exit 1 111 | fi 112 | 113 | if [[ ! -f "$NHOME/.kube/config" ]]; then 114 | echo >&2 ":: kubectl config file not found: $NHOME/.kube/config" 115 | exit 1 116 | fi 117 | 118 | if [[ "$#" -eq 0 ]]; then 119 | set -- _help 120 | fi 121 | 122 | _ns="--namespace ${GK8S_NAMESPACE:-default}" 123 | 124 | case "${1:-}" in 125 | "kubectl"*|"helm"*|_help) 126 | ;; 127 | "--") 128 | shift; 129 | ;; 130 | *) 131 | set -- kubectl "$@" 132 | ;; 133 | esac 134 | 135 | # Insert the namespace configuration... 136 | _cmd1="$1"; 137 | case "$_cmd1" in 138 | kubectl|kubectl.*) 139 | _log2 "Original command: '$@'" 140 | shift; 141 | _cmd2="$1"; shift; 142 | case "$_cmd2" in 143 | "apply"|"diff"|"delete") 144 | set -- "$_cmd1" "$_cmd2" "$@" 145 | ;; 146 | *) 147 | set -- "$_cmd1" "$_cmd2" $_ns "$@" 148 | _log2 "Modified command: '$@'" 149 | ;; 150 | esac 151 | ;; 152 | esac 153 | 154 | export OHOME="$HOME"; readonly OHOME 155 | export HOME="$NHOME" 156 | 157 | _oargs=( "$@" ) 158 | _start=0 159 | while [[ "$_start" -le "${#_oargs}" ]]; do 160 | case "${_oargs[$_start]}" in 161 | "@%") 162 | _oargs["$_start"]="--all-namespaces" 163 | ;; 164 | esac 165 | (( _start ++ )) 166 | done 167 | 168 | set -- "${_oargs[@]}" 169 | 170 | # Print heading... 171 | _log2 ":: Cluster : $_CLUSTER, input namespace: $_ns" 172 | _log2 ":: WDir : $(pwd)" 173 | _log2 ":: Command : $@" 174 | _log2 ":: OHOME : $OHOME" 175 | _log2 ":: NHOME : $HOME" 176 | _log2 ":: GK8S_HOME : $GK8S_HOME" 177 | 178 | if [[ -f "$NHOME/.gk8s.lock" ]]; then 179 | echo >&2 ":: Lock file found $NHOME/.gk8s.lock. Action can not be processed." 180 | exit 1 181 | fi 182 | 183 | if grep -qsEe "[[:space:]]delete[[:space:]]" <<< $* ; then 184 | if [[ ! -f ".delete" ]]; then 185 | echo >&2 ":: Action delete was requested but confirmation file (.delete) not found" 186 | echo >&2 ":: Working directory: $(pwd)" 187 | exit 127 188 | fi 189 | _log2 ":: Removing confirmation file: $(pwd)/.delete" 190 | 1>&2 rm -fv .delete 191 | fi 192 | [[ "${VERBOSE:-0}" -ge 2 ]] && set -x 193 | # Create a symlink to ~/.aws if necessary 194 | if [[ ! -d "$NHOME/.aws" && -d "$OHOME/.aws" ]]; then 195 | 1>&2 ln -sv "$OHOME/.aws" "$NHOME/.aws" 196 | fi 197 | 198 | "${@}" 199 | -------------------------------------------------------------------------------- /libs/bashrc.txt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export HISTSIZE=2000000 4 | export HISTFILESIZE=20000000000 5 | export HISTCONTROL="ignoredups" 6 | export HISTCONTROL="ignoreboth" 7 | export HISTTIMEFORMAT="[%DT%T] " 8 | export HISTIGNORE="pwd:exit:clear:fg*:bg*:history*" 9 | 10 | shopt -s histappend 11 | shopt -s cmdhist 12 | shopt -s autocd 13 | shopt -s histreedit 14 | 15 | export PROMPT_COMMAND='history -a' 16 | export EDITOR="nano" 17 | export LANG=en_US.UTF-8 18 | 19 | export PATH=$HOME/bin:$HOME/bbin//:$HOME/bin/links:$PATH 20 | export PS1='\[\e[0;32m\]:: \[\e[0;37m\]You are \[\e[0;31m\]\u\[\e[0;37m\]@\[\e[0;31m\]\H\[\e[0;37m\] \[\e[0;37m\]\w\n\[\e[0;32m\]\$\[\e[0m\] ' 21 | 22 | alias grep='grep --color' 23 | alias ..='cd ..' 24 | alias ...='cd ../..' 25 | 26 | alias dmesg='dmesg -L -Tlwarn' 27 | alias l='ls --color=auto -F --indicator-style=classify' 28 | alias la='ls --color=auto -aF --indicator-style=classify' 29 | alias ll='ls --color=auto -lF --indicator-style=classify' 30 | alias lla='ls --color=auto -laF --indicator-style=classify' 31 | alias ls='ls --color=auto -F --indicator-style=classify' 32 | alias rm='rm -i' 33 | alias man='man -P /usr/bin/most' 34 | 35 | -() { cd -; } 36 | 37 | #if [[ -f $HOME/bin/gpg-agent.sh ]]; then 38 | # source $HOME/bin/gpg-agent.sh 39 | #fi 40 | #if [[ -f $HOME/bin/ssh-agent.sh ]]; then 41 | # source $HOME/bin/ssh-agent.sh 42 | #fi 43 | 44 | #if [[ -S "/run/user/$(id -u)/keyring/ssh" ]]; then 45 | # export SSH_AUTH_SOCK="/run/user/$(id -u)/keyring/ssh" 46 | #fi 47 | 48 | export DISPLAY=:0.0 49 | 50 | -------------------------------------------------------------------------------- /libs/com.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Purpose: Common stuff 4 | # Author : Anh K. Huynh 5 | # Date : 2018 6 | # License: MIT 7 | 8 | 9 | warn() { 10 | msg "$@" 1>&2 11 | } 12 | 13 | msg() { 14 | local _args="" 15 | if [[ "$1" == "-e" ]]; then 16 | _args="-e" 17 | shift 18 | fi 19 | echo >&2 $_args ":: $@" 20 | } 21 | -------------------------------------------------------------------------------- /libs/docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Purpose: Docker utils 4 | # Author : Anh K. Huynh 5 | # Date : 2015 May 04th 6 | # Ref. : Feature request at https://github.com/docker/docker/issues/12791 7 | 8 | # Return IP address of a docker container 9 | # Input : $1: Docker container ID / name 10 | # Output: The IP address 11 | docker_to_ip() { 12 | docker inspect --format='{{.NetworkSettings.IPAddress}}' $1 13 | } 14 | 15 | # PRINT OUT 16 | # the core rules for NAT-network generated by Docker 17 | docker_nat_core_rules() { 18 | cat <<-EOF 19 | iptables -t nat -N DOCKER \\ 20 | && iptables -N DOCKER \\ 21 | && { 22 | iptables -t nat -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER 23 | iptables -t nat -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER 24 | iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE 25 | 26 | iptables -A FORWARD -o docker0 -j DOCKER 27 | iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 28 | iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT 29 | iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT 30 | } \\ 31 | || true 32 | EOF 33 | } 34 | 35 | # Return iptables (NAT) rules for a running container 36 | # Input : Container ID/Name 37 | # Output: iptables commands 38 | # 39 | # FIXME: This script will expose a security hole. By default, 40 | # FIXME: Docker packets are filtered with DOCKER and we don't 41 | # FIXME: have any limitation on that. By using this script, 42 | # FIXME: the port may be accessible by the world. Please make sure 43 | # FIXME: to have another firewall layer. Don't use this script bindly 44 | # FIXME: when you are using Digital Ocean networks. 45 | # 46 | docker_container_to_nat() { 47 | local _ip= 48 | local _id="${1:-xxx}" 49 | 50 | _ip="$(docker_to_ip $_id)" 51 | [[ $? -eq 0 ]] || return $? 52 | 53 | 54 | # $ docker port test 55 | # 80/tcp -> 127.0.1.1:1234 56 | # 80/tcp -> 0.0.0.0:1235 57 | docker port $_id \ 58 | | grep /tcp \ 59 | | sed -e 's#[/tcp>: -]\+# #g' \ 60 | | awk \ 61 | -vIP=$_ip \ 62 | -vCONTAINER_ID=$_id \ 63 | '{ 64 | iport = $1 65 | eip = $2 66 | eport = $3 67 | 68 | printf("iptables -t nat -C POSTROUTING -s %s/32 -d %s/32 -p tcp -m tcp --dport %s -j MASQUERADE 2>/dev/null \\\n", IP, IP, iport); 69 | printf("|| iptables -t nat -A POSTROUTING -s %s/32 -d %s/32 -p tcp -m tcp --dport %s -j MASQUERADE\n", IP, IP, iport); 70 | 71 | printf("iptables -t nat -C DOCKER -d %s/32 ! -i docker0 -p tcp -m tcp --dport %s -j DNAT --to-destination %s:%s 2>/dev/null \\\n", eip, eport, IP, iport); 72 | printf("|| iptables -t nat -A DOCKER -d %s/32 ! -i docker0 -p tcp -m tcp --dport %s -j DNAT --to-destination %s:%s\n", eip, eport, IP, iport); 73 | 74 | printf("iptables -C DOCKER -d %s/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport %s -j ACCEPT 2>/dev/null \\\n", IP, iport); 75 | printf("|| iptables -A DOCKER -d %s/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport %s -j ACCEPT\n", IP, iport); 76 | }' 77 | } 78 | 79 | # Return iptables (NAT) rules for all running containers 80 | # Input : NONE 81 | # Output: all iptables rules for running container 82 | docker_containers_to_nat() { 83 | while read CONTAINER_ID; do 84 | echo >&2 ":: docker/firewall: Generating rule for $CONTAINER_ID..." 85 | docker_container_to_nat $CONTAINER_ID 86 | done < <(docker ps -q) 87 | } 88 | 89 | docker_images_clean() { 90 | docker rmi -f \ 91 | $(docker images \ 92 | | grep '^/dev/null)" 105 | fi 106 | if [[ -z "$_pid" || "$_pid" == 0 ]]; then 107 | echo 0 108 | return 109 | fi 110 | 111 | ps h -oetime "$_pid" \ 112 | | awk '{ match($0, /([0-9]+-)?(([0-9]+):)?([0-9]+):([0-9]+)/, m); printf("%d\n", (m[1]*24 + m[3])*60 + m[4] + m[5]/60); }' 113 | } 114 | 115 | # A simple fancy version of `docker ps` 116 | # TODO: Use Go template instead. 117 | docker_ps() { 118 | docker ps -a \ 119 | | awk \ 120 | '{ 121 | if (NR>1) { 122 | gsub(/.*\//, "", $2); 123 | 124 | where = match($NF, /_.*_/); 125 | if (where != 0) { 126 | gsub("_", " ", $NF); 127 | printf("%s %s %s ", $1, $2, $NF); 128 | } 129 | else { 130 | printf("%s %s %s - 0 ", $1, $2, $NF); 131 | } 132 | 133 | 134 | for (i=1; i&2 ":: $FUNCNAME: This script must be executed under user '$*'" 43 | exit 44 | } 45 | } 46 | 47 | # Ensure hostname (simple name) matching 48 | ensure_node() { 49 | __is_node $* \ 50 | || { 51 | echo >&2 ":: $FUNCNAME: This script must be executed under node '$*'" 52 | exit 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /libs/exif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Purpose: Deal with exif data 4 | # Author : Anh K. Huynh 5 | # Date : 2014 6 | # License: MIT 7 | 8 | # Clear all exif data from input files 9 | exif_clear() { 10 | local _f 11 | 12 | exiftool -ver >/dev/null 2>&1 \ 13 | || { 14 | echo >&2 ":: exiftoool not found" 15 | return 127 16 | } 17 | 18 | while [[ -n "$@" ]]; do 19 | _f="$1" 20 | [[ -f "$_f" ]] || { shift; continue; } 21 | exiftool -all= "$_f" >/dev/null \ 22 | && rm -f "${_f}_original" \ 23 | && echo "Clear all exif data for $_f" 24 | shift 25 | done 26 | } 27 | -------------------------------------------------------------------------------- /libs/git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Purpose: Push git commits / branches to all remotes (except some...) 4 | # Author : Anh K. Huynh 5 | # Date : 2013 6 | # License: MIT 7 | # History: This is part of my private Bash library ;) 8 | # I share this, because I talked about it on #archlinuxvn 9 | # Usage : 10 | # 11 | # touch .git/skip.origin.branch1 # skip origin/branch1 12 | # touch .git/skip.origin.branch2 # skip origin/branch2 13 | # touch .git/skip.upstream # don't push to upstream 14 | # touch .git/skip._.private # skip private branch 15 | # 16 | # git_push_to_all_remotes : branch3 branch4 # push current branch, branch3 and branch4 17 | # git_push_to_all_remotes : --force # for a remote update 18 | # 19 | git_push_to_all_remotes() { 20 | local _args= 21 | local _brs= 22 | local _f_tmp= 23 | local _d_tmp="$(git rev-parse --show-toplevel)" 24 | 25 | while (( $# )); do 26 | if [[ "${1:0:1}" == "-" ]]; then 27 | _args="$_args $1" 28 | shift 29 | else 30 | _brs="$_brs $1" 31 | fi 32 | shift 33 | done 34 | 35 | _brs="${_brs:-:}" 36 | for _br in $_brs; do 37 | [[ "$_br" != ":" ]] || _br="$(git rev-parse --abbrev-ref HEAD)" 38 | git remote \ 39 | | while read _remote; do 40 | for __file__ in \ 41 | "$_d_tmp/.$_remote" \ 42 | "$_d_tmp/.$_remote.$_br" \ 43 | "$_d_tmp/_.$_br" \ 44 | "$_d_tmp/.git/skip.$_remote" \ 45 | "$_d_tmp/.git/skip.$_remote.$_br" \ 46 | "$_d_tmp/.git/skip._.$_br" \ 47 | ; do 48 | if [[ -f "$__file__" ]]; then 49 | echo >&2 ":: .git/${__file__##*/}" 50 | continue 2 51 | fi 52 | done 53 | git st | grep -q "Your branch is up-to-date with '$_remote/$_br'." 54 | if [[ $? -eq 0 ]]; then 55 | echo >&2 ":: '$_remote/$_br' is up-to-date" 56 | else 57 | echo >&2 ":: Pushing to '$_remote/$_br'..." 58 | git push $_args "$_remote" "$_br" 59 | fi 60 | done 61 | done 62 | } 63 | 64 | # Use *our* verion of conflict files 65 | git_ours() { 66 | while (( $# )); do 67 | [[ -f "$1" ]] \ 68 | && git checkout --ours "$1" \ 69 | && git add "$1" 70 | shift 71 | done 72 | } 73 | 74 | # Use *their* verion of conflict files 75 | git_theirs() { 76 | while (( $# )); do 77 | [[ -f "$1" ]] \ 78 | && git checkout --theirs "$1" \ 79 | && git add "$1" 80 | shift 81 | done 82 | } 83 | 84 | # Show list of conflict files 85 | git_conflict() { 86 | git status -s $@ \ 87 | | grep AA 88 | } 89 | 90 | git_count_author_year_filter() { 91 | if [[ -z "${1:-}" ]]; then 92 | cat 93 | else 94 | grep ":${1}:" 95 | fi 96 | } 97 | 98 | # Print number of commits by user 99 | # $1: year number (2024, 2013) or empty 100 | git_count_author() { 101 | year_filter="${1:-}" 102 | TS="$(date +%s)" 103 | git log --date="format:%Y" --pretty=format:"%ae $TS :%cd:%s" \ 104 | | git_count_author_year_filter "$year_filter" \ 105 | | awk -F " $TS " '{a[$0]+=1; if (a[$0]==1) {printf("%s\n", $1);}}' \ 106 | | sort \ 107 | | uniq -c \ 108 | | sort -rn 109 | } 110 | -------------------------------------------------------------------------------- /libs/image.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Purpose: Dealing with images 4 | # Author : Anh K. Huynh 5 | # Date : 2018 6 | # License: MIT 7 | 8 | export __IMG_WATERMARK_FONT="/home/pi/.fonts/Chococooky.ttf" 9 | export __IMG_WATERMARK_STRING="Ky-Anh Huynh - CC BY-NC-ND 4.0" 10 | 11 | add_watermark_with_text() { 12 | local _f_input="$1" 13 | local _f_output="${2:-$1}" 14 | local _f_width="" 15 | 16 | if [[ -z "$_f_output" ]]; then 17 | warn "Please specify output file name (will be overwritten)" 18 | return 1 19 | fi 20 | 21 | if [[ -f "$_f_output" ]]; then 22 | warn "Output file does exist '$_f_output'. Please use FORCE=1 to continue" 23 | if [[ ! "$FORCE" == "1" ]]; then 24 | return 1 25 | fi 26 | fi 27 | 28 | local _banner_height="" 29 | local _banner_text_size="" 30 | 31 | if [ -f "$_f_input" ]; then 32 | _width="$(image_get_width "$_f_input")" 33 | 34 | if image_is_landscape "$_f_input"; then 35 | msg "$FUNCNAME: Image in landscape mode, widthxheight = $_width x $(image_get_height "$_f_input")" 36 | _banner_height="30" 37 | _banner_text_size="20" 38 | else 39 | _banner_height="30" 40 | _banner_text_size="20" 41 | fi 42 | 43 | msg "$FUNCNAME: Image width = $_width" 44 | convert \ 45 | -background "#0004" \ 46 | -fill white -gravity center \ 47 | -size ${_width}x${_banner_height} \ 48 | -font "$__IMG_WATERMARK_FONT" \ 49 | -pointsize ${_banner_text_size} \ 50 | label:"$__IMG_WATERMARK_STRING" \ 51 | +size "$_f_input" \ 52 | -gravity south \ 53 | +swap -composite \ 54 | "$_f_output" 55 | else 56 | msg "$FUNCNAME: File not found '$_f_input'" 57 | fi 58 | } 59 | 60 | # Get width of any image 61 | # FIXME: handle problem with bad exif data 62 | image_get_width() { 63 | local _f_image="$@" 64 | if [ ! -f "$_f_image" ]; then 65 | return 1 66 | fi 67 | identify -format %w "$_f_image" 68 | } 69 | 70 | image_get_height() { 71 | local _f_image="$@" 72 | if [ ! -f "$_f_image" ]; then 73 | return 1 74 | fi 75 | identify -format %h "$_f_image" 76 | } 77 | 78 | image_is_landscape() { 79 | local _f_image="$@" 80 | if [ ! -f "$_f_image" ]; then 81 | msg "$FUNCNAME: image doesn't exist $_f_image'" 82 | return 127 83 | fi 84 | local _w="$(image_get_width "$_f_image")" 85 | local _h="$(image_get_height "$_f_image")" 86 | [[ $_w -ge $_h ]] 87 | } 88 | -------------------------------------------------------------------------------- /libs/ps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Purpose: Collection of `ps` commands 4 | # Author : Anh K. Huynh 5 | # Date : 2014 6 | # License: MIT 7 | 8 | # See more at http://www.cyberciti.biz/tips/top-linux-monitoring-tools.html 9 | 10 | # simple listing 11 | ps_processes() { 12 | ps -A "$@" 13 | } 14 | 15 | # long listing 16 | ps_processes_long() { 17 | ps -Al "$@" 18 | } 19 | 20 | # extra full listing 21 | ps_processes_extra_long() { 22 | ps -AlF "$@" 23 | } 24 | 25 | # see all threads: LWP and NLWP 26 | ps_threads() { 27 | ps -AlFH "$@" 28 | } 29 | 30 | ps_threads_after_processes() { 31 | ps -AlLm "$@" 32 | } 33 | 34 | ps_my_processes() { 35 | ps -U $(whoami) -u $(whoami) u "$@" 36 | } 37 | 38 | ps_top_memory_consumers() { 39 | echo "USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND" 40 | ps auxf | sort -nr -k 4 | head -10 41 | } 42 | 43 | ps_top_cpu_consumers() { 44 | ps auxf | sort -nr -k 3 | head -10 45 | } 46 | -------------------------------------------------------------------------------- /libs/ps1.sh: -------------------------------------------------------------------------------- 1 | # Purpose: Provide Bash PS1 2 | # Author : Anh K. Huynh 3 | # Date : 2010 (v1), 2014 (v2) 4 | # Usage : source $0 5 | 6 | ps1_icy() { 7 | export PS1='\[\e[0;32m\]:: \[\e[0;37m\]You are \[\e[0;31m\]\u\[\e[0;37m\]@\[\e[0;31m\]\h\[\e[0;37m\] \[\e[0;37m\]\w\n\[\e[0;32m\]\$\[\e[0m\] ' 8 | } 9 | -------------------------------------------------------------------------------- /libs/raw2mbox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Purpose: Convert raw message (e.g, from Google Group) to mbox file 4 | # Author : Anh K. Huynh 5 | # Date : 2014 6 | # License: MIT 7 | 8 | # Received: from icy.bar (l00s3r.theslinux.org [199.180.254.75]) by mx.zohomail.com 9 | # with SMTPS id 1379761151703804.5943683273185; Sat, 21 Sep 2013 03:59:11 -0700 (PDT) 10 | # Date: Sat, 21 Sep 2013 17:59:07 +0700 11 | # From: "Anh K. Huynh" 12 | # To: archlinuxvn@googlegroups.com 13 | # 14 | raw2mbox() { 15 | awk ' 16 | BEGIN { 17 | _headers[0] = "" 18 | _count = 0 19 | _ship = 0 20 | 21 | _date="" 22 | _from="" 23 | } 24 | 25 | { 26 | if ( match($0, /^Date: (.*)$/, gs) ) { 27 | _ship ++; 28 | _date = gs[1]; 29 | _headers[++_count] = $0; 30 | } 31 | else if ( match($0, /^From:.*<([^<>]+)>$/, gs) ) { 32 | _ship ++; 33 | _from = gs[1]; 34 | _headers[++_count] = $0; 35 | } 36 | else if (_ship < 2) { 37 | _headers[++_count] = $0; 38 | } 39 | else if (_ship == 2) { 40 | _ship ++; 41 | _headers[++_count] = $0; 42 | 43 | printf("From %s %s\n", _from, _date); 44 | for (i = 1; i <= _count; i ++) { 45 | printf("%s\n", _headers[i]); 46 | } 47 | } 48 | else if (_ship > 2) { 49 | print $0; 50 | } 51 | } 52 | 53 | END { 54 | if (_ship == 2) { 55 | printf(":: Possibly bad format.\n") > "/dev/stderr"; 56 | } 57 | } 58 | ' 59 | } 60 | -------------------------------------------------------------------------------- /libs/url.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Purpose: URL processing 4 | # Author : Anh K. Huynh 5 | # Date : 2015 6 | # License: MIT 7 | 8 | # Usage 9 | # echo -n "http://example.com/" | url_encode 10 | url_encode() { 11 | perl -e ' 12 | use URI::Escape; 13 | while (<>) { 14 | printf("%s", uri_escape($_)); 15 | } 16 | ' 17 | } 18 | 19 | # Fetch all URLs from STDIN. Example 20 | # curl -sL duckduckgo.com |url_fetch 21 | url_fetch() { 22 | ruby -e 'STDIN.read.scan(%r{(https?://[a-z%0-9\-_\./]+)}i) { |m| puts m }' \ 23 | | sort -u 24 | } 25 | -------------------------------------------------------------------------------- /libs/xml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Purpose: Deal with XML files 4 | # Author : Anh K. Huynh 5 | # Date : 2014 6 | # License: MIT 7 | 8 | xml_pretty() { 9 | perl -e ' 10 | use XML::LibXML; 11 | use XML::LibXML::PrettyPrint; 12 | my $document = XML::LibXML->new->parse_file("-"); 13 | my $pp = XML::LibXML::PrettyPrint->new(indent_string => " "); 14 | $pp->pretty_print($document); # modified in-place 15 | print $document->toString; 16 | ' 17 | } 18 | -------------------------------------------------------------------------------- /others/gitconfig: -------------------------------------------------------------------------------- 1 | # License: MIT 2 | # AUthor : Ky-Anh Huynh 3 | 4 | [color] 5 | branch = auto 6 | diff = auto 7 | status = auto 8 | 9 | [alias] 10 | pu = push 11 | pl = pull 12 | ci = commit 13 | di = diff --color-words 14 | st = status -uno 15 | stt = status 16 | co = checkout 17 | br = branch 18 | lg = log -p 19 | logf = log --follow 20 | lol = log --graph --decorate --pretty=oneline --abbrev-commit 21 | lola = log --graph --decorate --pretty=oneline --abbrev-commit --all 22 | ls = ls-files 23 | mg = merge 24 | cb = rev-parse --abbrev-ref HEAD 25 | clog = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit 26 | slog = !git log --graph --pretty=format:'%h -%d %s (%cr) <%an>' --abbrev-commit | cat && echo 27 | 28 | fp = format-patch --ignore-space-change 29 | 30 | # TheSLinux utils 31 | stag = !git tag | grep -E "^$(git rev-parse --abbrev-ref HEAD)-[0-9.]+" 32 | sbr = !git branch | grep " $(git rev-parse --abbrev-ref HEAD)" 33 | 34 | [color "branch"] 35 | current = white 36 | local = magenta 37 | remote = cyan 38 | 39 | [color "diff"] 40 | meta = yellow bold 41 | frag = magenta bold 42 | old = red bold 43 | new = green bold 44 | 45 | [color "status"] 46 | added = yellow 47 | changed = green 48 | untracked = cyan 49 | 50 | [push] 51 | default = simple 52 | 53 | [commit] 54 | gpgsign = false 55 | 56 | [core] 57 | quotepath = false 58 | commitGraph = true 59 | 60 | [gc] 61 | writeCommitGraph = true 62 | 63 | [pull] 64 | ff = only 65 | -------------------------------------------------------------------------------- /sample-main.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for _ in libs/*.sh; do 4 | source "${_}" : \ 5 | || exit 1 6 | done 7 | 8 | [[ -z "$@" ]] || $@ 9 | --------------------------------------------------------------------------------