├── Command-line Shortcut Tool ├── README.md ├── cmd └── scnshot.png ├── Convert Cygwin Path toWinPath ├── README.md ├── demo.png └── toWinPath ├── Cygwin rm Move to RecycleBin ├── README.md ├── img │ └── trash.png └── trash ├── Get Cygwin Setup Log ├── README.md ├── get_cygwin_log └── scnshot.png ├── Hex Color to 256 Color ├── README.md ├── hexcolor_to_256color.py └── screenshot.png ├── LICENSE ├── My Wrapper of C++ Compiler ├── README.md ├── mygcc └── scnshot.png ├── README.md ├── Rsync Over SSH ├── README.md ├── screenshot.png └── sshrsync ├── Search Keyword in Files ├── README.md ├── findinfiles.py └── screenshot.png ├── base64_decode.py ├── bash_timestamp.sh ├── calculate.py ├── hex2binary.c ├── hex2binary.py ├── myscreenshot ├── myvol ├── octal2UTF8.py ├── practice_password.py ├── ps_list_descendants.py ├── recyclebin ├── tee-safe - Tee with Prompting ├── README.md ├── scnshot.png ├── scnshot2.png └── tee-safe ├── waitfor └── zipencrypt /Command-line Shortcut Tool/README.md: -------------------------------------------------------------------------------- 1 | # Command-line Shortcut Tool 2 | 3 | A tool for calling command-line shortcuts with ease. 4 | 5 | ## Description 6 | 7 | The default way of creating a command-line shortcut is using `alias`. However, it has its shortcomings, for example it does not support positional parameters ($1, $2, ...). This tool is for creating and managing shortcuts that can execute arbitrary commands. 8 | 9 | ## Installation 10 | 11 | - Put this file under your `~/bin` folder and make it executable via `chmod +x cmd`. 12 | 13 | ## Screenshot 14 | 15 | 16 | -------------------------------------------------------------------------------- /Command-line Shortcut Tool/cmd: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Written by davidhcefx, 2019.11.7. 4 | # Format of $cmdlist: 5 | # SPACE SPACE KEY TAB (TAB)* COMMAND 6 | 7 | cmdlist=~/.cmdlist 8 | exe=/tmp/cmdtool.exe 9 | 10 | help() { 11 | if ! [ -e $cmdlist ]; then 12 | echo " edit nano $cmdlist" > $cmdlist 13 | fi 14 | echo "A tool for commandline shortcuts. Syntax: $(basename $0) [option]" 15 | echo "Options:" 16 | cat $cmdlist 17 | echo "" 18 | } 19 | 20 | 21 | if [ $# == 0 ]; then 22 | help 23 | exit 24 | fi 25 | 26 | key=$1; shift 27 | cmd=$(cat $cmdlist | sed -n "/^\ \ $key\t/{s/^\ \ $key//p}" | head -n 1) 28 | 29 | if [ "$cmd" == "" ]; then 30 | echo "Command not found!" 31 | help 32 | else 33 | echo "$cmd" > $exe 34 | bash $exe "$@" 35 | fi 36 | -------------------------------------------------------------------------------- /Command-line Shortcut Tool/scnshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Command-line Shortcut Tool/scnshot.png -------------------------------------------------------------------------------- /Convert Cygwin Path toWinPath/README.md: -------------------------------------------------------------------------------- 1 | # Convert Cygwin Path toWinPath 2 | 3 | ## Description 4 | 5 | Under *Cygwin*, paths such as `C:\Users` would appear as `/cygdrive/c/Users`, while paths that belong to *Unix* such as `/var/log` are actually `C:\cygwin64\var\log`. 6 | This script converts Cygwin paths to Windows-understandable paths. 7 | 8 | ## Installation 9 | 10 | 1. Put this file under your `~/bin` folder and make it executable via `chmod +x toWinPath`. 11 | 12 | 2. Now, you can run commands such as `explorer $(toWinPath .)` in order to open up `explorer.exe` under that folder. 13 | 14 | screenshot 15 | -------------------------------------------------------------------------------- /Convert Cygwin Path toWinPath/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Convert Cygwin Path toWinPath/demo.png -------------------------------------------------------------------------------- /Convert Cygwin Path toWinPath/toWinPath: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Convert Cygwin path to Windows path. Written by davidhcefx, 2019.10.17. 4 | # Description: 5 | # Under Cygwin, paths such as C:\Users would appear as /cygdrive/c/Users, 6 | # while paths that belong to Unix such as /var/log are actually C:\cygwin64\var\log. 7 | # Usage: 8 | # You can use this script like this: explorer "$(toWinPath .)", 9 | # in order to open up explorer.exe under that folder. 10 | 11 | if [[ $# == 0 ]]; then 12 | echo 'Syntax: toWinPath "path-to-file".' 13 | exit 14 | fi 15 | 16 | set -Eeuo pipefail 17 | path="$1" 18 | 19 | # Resolve symbolic link 20 | cd "$(dirname "$path")" # Go to its parent directory 21 | path="$(pwd -P)/$(basename "$path")" 22 | 23 | # Remove trailing slash 24 | path="${path%/}" 25 | 26 | # Replace cygdrive or add C:\cygwin64 27 | case "$path" in 28 | /cygdrive/*) 29 | path="${path#/cygdrive/}" 30 | path="${path/\//:\/}" 31 | ;; 32 | *) 33 | path="C:/cygwin64${path}" 34 | ;; 35 | esac 36 | 37 | # Change to backslashes 38 | echo "${path//\//\\}" 39 | -------------------------------------------------------------------------------- /Cygwin rm Move to RecycleBin/README.md: -------------------------------------------------------------------------------- 1 | # Trash - A rm replacement on Cygwin 2 | 3 | - Ever worried about accidentally deleting something and not being able to recover it? Here is a method to replace the native `rm` with a safer version of deleting files via moving it to the *Trash bin*. 4 | 5 | 6 | ## How to Install 7 | 8 | 1. Go to http://www.maddogsw.com/cmdutils/, download the zip, and then take out only **Recycle.exe**. 9 | 10 | 2. Download the script **toWinPath** from [here](https://github.com/davidhcefx/My-Bash-Scripts/tree/master/Convert%20Cygwin%20Path%20toWinPath). 11 | 12 | 3. Make the above two files *callable* and *runnable*, namely: 13 | 14 | 3-1) `chmod +x toWinPath`. 15 | 16 | 3-2) Add their paths to the PATH variable. For example, add `PATH=$PATH:~/bin` to your `~/.bashrc` and put them under the ~/bin folder. 17 | 18 | 4. Now, you can simply run `trash` to delete files safely, without worrying yourself accidentally deleting something. You might also prefer adding an alias like: `alias rm='trash'`. 19 | 20 | 21 | -------------------------------------------------------------------------------- /Cygwin rm Move to RecycleBin/img/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Cygwin rm Move to RecycleBin/img/trash.png -------------------------------------------------------------------------------- /Cygwin rm Move to RecycleBin/trash: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # A rm replacement on Cygwin, using Recycle.exe (http://www.maddogsw.com/cmdutils) and toWinPath 4 | # (https://github.com/davidhcefx/My-Bash-Scripts/tree/master/Convert%20Cygwin%20Path%20toWinPath). 5 | # Created by davidhcefx, 2019.11.8. 6 | 7 | file_list=() 8 | flags="-f" # force by default 9 | ret_val=0 10 | 11 | 12 | if [ $# -eq 0 ]; then 13 | Recycle.exe "/?" 14 | exit 15 | fi 16 | 17 | while getopts "hfiIrRdv" opt; do 18 | case $opt in 19 | h) Recycle.exe "/?" 20 | exit 21 | ;; 22 | i|I) flags="" 23 | ;; 24 | v) set -x 25 | ;; 26 | *) ;; # ignore other flags 27 | esac 28 | done 29 | shift $((OPTIND - 1)) 30 | 31 | for f in "$@"; do 32 | if [ -e "$f" ]; then 33 | file_list+=("$(toWinPath "$f")") 34 | else 35 | echo "trash: cannot remove '$f': No such file or directory" >&2 36 | ret_val=1 37 | fi 38 | done 39 | 40 | echo "Removing ${#file_list[@]} file(s)..." >&2 41 | Recycle.exe $flags "${file_list[@]}" || exit $? 42 | exit $ret_val 43 | -------------------------------------------------------------------------------- /Get Cygwin Setup Log/README.md: -------------------------------------------------------------------------------- 1 | # Get Cygwin Setup Log 2 | 3 | Extract the *Augmented Transaction List* from the setup log of Cygwin, which is a list showing which packages we installed in a clear way. 4 | 5 | ## Why? 6 | 7 | - Cygwin's [official installer](https://cygwin.com/install.html) is the **Package Manager** for Cygwin packages. However, it doesn't keep track which packages were installed manually and which are just dependencies. (the "Picked" filter won't show libraries) 8 | 9 | - By extracting the transaction list, we can figure out *when*, and ideally *why*, a package has been installed by ourselves. 10 | 11 | 12 | ## Screenshots 13 | 14 | 15 | -------------------------------------------------------------------------------- /Get Cygwin Setup Log/get_cygwin_log: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Extract all "Augmented Transaction List", written by davidhcefx, 2020.4.8. 4 | 5 | log=/var/log/setup.log 6 | 7 | cat $log | sed -n -e '/Augmented\ Transaction\ List/p' \ 8 | -e 's/^[0-9/]\+\ [0-9:]\+\ \+[0-9]\+\ \+\(install\|erase\)/\ \ \ \ \1/p' 9 | -------------------------------------------------------------------------------- /Get Cygwin Setup Log/scnshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Get Cygwin Setup Log/scnshot.png -------------------------------------------------------------------------------- /Hex Color to 256 Color/README.md: -------------------------------------------------------------------------------- 1 | # Hex Color to 256 Color 2 | 3 | Translate hex-colors to 256 color and display them right away *in your terminal*. 4 | 5 | 6 | ## Screenshot 7 | 8 | 9 | -------------------------------------------------------------------------------- /Hex Color to 256 Color/hexcolor_to_256color.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | from sys import argv 3 | from os.path import basename 4 | 5 | 6 | def color256(code: str) -> int: 7 | rgb = [int(code[i:i+2], 16) for i in range(0, len(code), 2)] 8 | ruler = [0, 95, 135, 175, 215, 255] # the scale used by 256 color 9 | ret_idx = 0 10 | for value in rgb: 11 | # find closest match on ruler 12 | for i, _ in enumerate(ruler): 13 | if ruler[i] <= value <= ruler[i + 1]: 14 | idx = i if (value - ruler[i] < ruler[i + 1] - value) else i + 1 15 | break 16 | ret_idx = ret_idx * len(ruler) + idx 17 | 18 | return ret_idx + 16 # (0, 0, 0) starts from 16 19 | 20 | 21 | def help_exit(): 22 | raise SystemExit('Example: {} a6e22e'.format(basename(argv[0]))) 23 | 24 | 25 | if __name__ == '__main__': 26 | if len(argv) == 1: 27 | help_exit() 28 | else: 29 | code = argv[1].strip('#') 30 | if len(code) != 6: 31 | help_exit() 32 | 33 | color = '\x1b[38;5;{}m'.format(color256(code)) 34 | reset = '\x1b[0m' 35 | head = color + '▁▂▃▄▅▆▇█' + '█' * 10 + reset 36 | tail = color + '█' * 10 + '█▇▆▅▄▃▂▁' + reset 37 | 38 | print(f'{head} {color}#{code}{reset} looks like this {tail}') 39 | -------------------------------------------------------------------------------- /Hex Color to 256 Color/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Hex Color to 256 Color/screenshot.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 davidhcefx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /My Wrapper of C++ Compiler/README.md: -------------------------------------------------------------------------------- 1 | # My Wrapper of C++ Compiler. 2 | 3 | - Having to compile, run and debug every time is a very cumbersome task. 4 | 5 | 6 | ## Features 7 | 8 | - `-Wall` and `-Wextra` enabled by default. (Easier to find bugs!) 9 | 10 | - Seperating of **Compiler** and **Linker**. (Sometimes it is an error of the linker rather than the compiler!) 11 | 12 | - Automatically guess the name of source file. (Quite often would there be more than one files starting with the same prefix, eg. `test.out` and `test.cpp`. This saves some keystrokes!) 13 | 14 | - On succeed, run the program directly. (Integrated workflow!) 15 | 16 | 17 | ## Installation 18 | 19 | - Simply put this file under your `~/bin` folder and grant it execution privilege. 20 | 21 | 22 | ## Screenshot 23 | 24 | screenshot 25 | -------------------------------------------------------------------------------- /My Wrapper of C++ Compiler/mygcc: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # My wrapper of C++ compiler. 4 | # You can create aliases like gcccc, gccccc, etc, all pointing to this file, in case you typed more than three 'c'. 5 | # Written by davidhcefx, edited in 2018.11.20 6 | 7 | CPP="c++ -Wall -Wextra" 8 | GRAY=$'\E[2;38m' 9 | RESET=$'\E[m' 10 | 11 | 12 | help() { 13 | echo "Syntax: $(basename $0) [file] (options)" 14 | } 15 | 16 | guess_name() { 17 | if [ -f "$1".cpp ]; then 18 | echo "$1".cpp 19 | elif [ -f "$1".c ]; then 20 | echo "$1".c 21 | else 22 | echo "$1" 23 | fi 24 | } 25 | 26 | main() { 27 | name="$1" 28 | shift 29 | if ! [ -f "$name" ]; then 30 | name=$(guess_name "${name%.}") # remove '.' if exists 31 | fi 32 | 33 | CPP="$CPP $@" # remaining options 34 | obj_file=/tmp/"${name%.*}".o 35 | out_name="${name%.*}".out 36 | 37 | echo "${GRAY}================== Compiler ==================${RESET}" 38 | $CPP -c "$name" -o "$obj_file" 39 | if [[ $? != 0 ]]; then 40 | exit 1 41 | fi 42 | 43 | echo "${GRAY}=================== Linker ===================${RESET}" 44 | $CPP "$obj_file" -o "$out_name" 45 | if [[ $? != 0 ]]; then 46 | exit 1 47 | fi 48 | 49 | printf "${GRAY}> Hit enter to run '$out_name' ${RESET}" 50 | read -n 1 ch 51 | 52 | if [ -z "$ch" ]; then 53 | ./"$out_name" 54 | else 55 | echo "" 56 | fi 57 | } 58 | 59 | 60 | if [[ $# == 0 ]]; then 61 | help 62 | else 63 | main "$@" 64 | fi 65 | -------------------------------------------------------------------------------- /My Wrapper of C++ Compiler/scnshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/My Wrapper of C++ Compiler/scnshot.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My Bash Scripts 2 | Bash scripts (mostly) and tools created for making the life easier. 3 | 4 | ## Usage 5 | Most of the files are intended to be placed in ~/bin, which is included in the PATH variable by default in most Unix systems. Make them executable by this command: `chmod +x `. 6 | -------------------------------------------------------------------------------- /Rsync Over SSH/README.md: -------------------------------------------------------------------------------- 1 | # Rsync Over SSH 2 | 3 | An interactive script to make file transfer with `rsync` + `ssh` easier. 4 | 5 | 6 | ## Introduction 7 | 8 | The usual way for `rsync` over `ssh` was: 9 | 10 | ```sh 11 | $ rsync -av -delete -e 'ssh -p 22' user@:/remote/path /local/path 12 | ``` 13 | 14 | Basing on that, this script provides additional features such as: 15 | 16 | - Config short aliases for each host addresses (eg. "lab"), as well as their ports. 17 | - Interactive host selection. 18 | - Non-interactive support, i.e. by passing program arguments. 19 | - Simplified usage, i.e. typing lesser to achieve the same thing. 20 | 21 | 22 | ## Usage 23 | 24 | ``` 25 | Syntax: sshrsync [-a alias] [-d (to|from)] [src_path]... [dst_path] 26 | The paths can be either relative or absolute. 27 | ``` 28 | 29 | Please edit the script and add your hosts before running it. By passing no argument, it would enter interactive mode. 30 | 31 | Screenshot. For interactive usage, simply type sshrsync. For Non-interactive, type eg. sshrync -a lab -d to ./local/path ./remote/path. 32 | -------------------------------------------------------------------------------- /Rsync Over SSH/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Rsync Over SSH/screenshot.png -------------------------------------------------------------------------------- /Rsync Over SSH/sshrsync: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Script for file transfer using rsync + ssh. Written by davidhcefx, 2021.4.2. 4 | 5 | ## Add host below; Format: 6 | hosts=( 7 | "local" "david@127.0.0.1" 22 8 | ) 9 | 10 | nb_column=3 # number of columns in hosts 11 | host= 12 | port= 13 | alias_name= 14 | direction= 15 | src_path= 16 | dst_path= 17 | 18 | 19 | function yellow() { 20 | printf "\e[33m$1\e[0m" 21 | } 22 | 23 | function list_targets() { 24 | for ((i = 0; i < ${#hosts[@]}; i += $nb_column)); do 25 | echo -e "$(yellow "[${hosts[i]}]")\t${hosts[i + 1]} (${hosts[i + 2]})" 26 | done 27 | echo "" 28 | } 29 | 30 | # return idx of alias name, or -1 if failed 31 | function find_idx_of_alias() { 32 | for ((i = 0; i < ${#hosts[@]}; i += $nb_column)); do 33 | if [[ ${hosts[i]} == "$1" ]]; then 34 | return $i 35 | fi 36 | done 37 | return 255 38 | } 39 | 40 | function select_target() { 41 | if [ -z "$alias_name" ]; then 42 | read -rep "$(yellow "Target: ")" alias_name 43 | fi 44 | find_idx_of_alias "$alias_name" 45 | idx=$? 46 | if (( idx >= ${#hosts[@]} )); then 47 | echo "Error: Alias name not found!" 48 | exit 1 49 | fi 50 | host="${hosts[idx + 1]}" 51 | port="${hosts[idx + 2]}" 52 | } 53 | 54 | # set direction from stdin if not set 55 | function set_direction() { 56 | if [ -z "$direction" ]; then 57 | read -rep "$(yellow "Direction: [to|from] ")" direction 58 | fi 59 | } 60 | 61 | function prompt_for_local_path() { 62 | read -rep "$(yellow "Local file: ")" local_path 63 | local_path=$(sed "s:^~:$HOME:" <<< "$local_path") # tilde expansion 64 | } 65 | 66 | function prompt_for_remote_path() { 67 | read -rep "$(yellow "Remote path (relative/absolute): ")" remote_path 68 | } 69 | 70 | # prefix each arg with ':' and store to $result 71 | function prefix_each_with_colon() { 72 | result=() 73 | for arg in "$@"; do 74 | result+=(":$arg") 75 | done 76 | } 77 | 78 | # set src_path and dst_path from stdin or args (with user@host prefix) 79 | function set_src_dst_paths() { 80 | if (( $# < 2 )); then 81 | case "$direction" in 82 | to) 83 | prompt_for_local_path 84 | if ! [ -e "$local_path" ]; then 85 | echo "Error: File not found!"; 86 | exit 1; 87 | fi 88 | prompt_for_remote_path 89 | src_path="$local_path" 90 | dst_path="$host:$remote_path" 91 | ;; 92 | from) 93 | prompt_for_remote_path 94 | prompt_for_local_path 95 | src_path="$host:$remote_path" 96 | dst_path="$local_path" 97 | ;; 98 | *) 99 | exit 1 100 | esac 101 | else 102 | # from args 103 | src_path=("${@:1:(($#-1))}") 104 | shift $(($# - 1)) 105 | dst_path="$1" 106 | 107 | case "$direction" in 108 | to) 109 | dst_path="$host:$dst_path" 110 | ;; 111 | from) 112 | prefix_each_with_colon "${src_path[@]}" 113 | src_path=("${result[@]}") 114 | src_path[0]="${host}${src_path[0]}" 115 | ;; 116 | *) 117 | exit 1 118 | esac 119 | fi 120 | # safety check 121 | if [[ $src_path == */ ]]; then 122 | echo -e "$(yellow Warning): Coping folder *contents* instead of the whole folder" \ 123 | "might be dangerous! (eg. Overwrite all contents under home)\n" 124 | fi 125 | } 126 | 127 | function main() { 128 | list_targets 129 | select_target 130 | set_direction 131 | set_src_dst_paths "$@" 132 | 133 | echo -ne "[ ${src_path[@]} ] >>>\t[ $dst_path ] ?" 134 | read -rep " " _ 135 | rsync -av --delete -e "ssh -p $port" "${src_path[@]}" "$dst_path" 136 | } 137 | 138 | # parse arguments if any 139 | while getopts "ha:d:" opt; do 140 | case $opt in 141 | h) echo "Syntax: $(basename $0) [-a alias] [-d (to|from)] [src_path]... [dst_path]" 142 | echo -e "\tThe paths can be either relative or absolute." 143 | exit 0 144 | ;; 145 | a) alias_name="$OPTARG" 146 | ;; 147 | d) direction="$OPTARG" 148 | ;; 149 | *) 150 | exit 1 151 | esac 152 | done 153 | shift $((OPTIND - 1)) 154 | main "$@" 155 | -------------------------------------------------------------------------------- /Search Keyword in Files/README.md: -------------------------------------------------------------------------------- 1 | # Search Keyword in Files 2 | 3 | Searching for cross references within a huge code base can be cumbersome. This is a simple and efficient tool to perform file-content search within a subdirectory. 4 | 5 | ## Usage 6 | 7 | ```csh 8 | Syntax: findinfiles.py (-i|-v|-h) 9 | -i: Case insensitive search. 10 | -v: Verbose. 11 | -h: Help. 12 | ``` 13 | 14 | ## Screenshot 15 | 16 | 17 | -------------------------------------------------------------------------------- /Search Keyword in Files/findinfiles.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # 3 | # Search for keywords in files reachable from current directory. 4 | # Written by davidhcefx, 2020.7.19. Originally in Bash. 5 | 6 | import readline # for better input handling 7 | import string 8 | from subprocess import run, PIPE 9 | from argparse import ArgumentParser, Namespace 10 | 11 | DEFAULT_EXT = 'h c cpp' 12 | 13 | 14 | def cyan(text: str) -> str: 15 | return '\33[36m' + text + '\33[0m' 16 | 17 | def green(text: str) -> str: 18 | return '\33[32m' + text + '\33[0m' 19 | 20 | def escape_regex(text: str) -> str: 21 | return text.translate({ord(p): '\\' + p for p in string.punctuation}) 22 | 23 | def main(args: Namespace): 24 | # configure extension 25 | ext = input('Extensions: [{}] '.format(DEFAULT_EXT)) or DEFAULT_EXT 26 | ext = list(map(lambda x: '\\.' + x.strip('.'), ext.split())) # remove dots if exists 27 | regex = '.*\\({}\\)'.format('\\|'.join(ext)) 28 | res = run(['find', '-L' if args.symlink else '-P', '.', '-type', 'f', '-regex', regex], 29 | stdout=PIPE, 30 | check=True) 31 | if len(res.stdout) == 0: 32 | raise SystemExit('It seems that no file match extension "{}"'.format(ext)) 33 | 34 | # configure search string 35 | search_str = input('Search string: ') 36 | grep_pattern = '{}{}'.format('(?i)' if args.icase else '', escape_regex(search_str)) 37 | print('Searching for "{}" ...'.format(search_str)) 38 | 39 | for name in res.stdout.decode('utf8').strip('\n').split('\n'): 40 | r = run(['grep', '--color=always', '-n', '-P', grep_pattern, name], stdout=PIPE) 41 | if len(r.stdout) > 0: 42 | print(cyan(name)) 43 | print(r.stdout.decode('utf8'), end='') 44 | 45 | 46 | if __name__ == '__main__': 47 | parser = ArgumentParser() 48 | parser.add_argument('-i', dest='icase', action='store_true', help='case insensitive search') 49 | parser.add_argument('-nL', dest='symlink', action='store_false', 50 | help='do not follow symbolic link when listing files') 51 | main(parser.parse_args()) 52 | 53 | 54 | 55 | ## Changelog 56 | # - 1/31: 57 | # - corrected path after #!, pylinted (6.7 -> 8.3), changed to argparse framework 58 | # - follow symlink by default, changable default-extension, disable regex for grep 59 | # - 2/1: 60 | # - grep pattern case-insensitively, prefer (X if C else Y) over (C and X or Y) 61 | # - reduce encode/decode usage 62 | # - 6/8: 63 | # - change extension representation to .h, show line number 64 | # - 6/11: 65 | # - simply use grep to do all the stuff 66 | # - 2/8: 67 | # - simplify what needed to be entered as extensions, correct `-regex` option usage. 68 | -------------------------------------------------------------------------------- /Search Keyword in Files/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/Search Keyword in Files/screenshot.png -------------------------------------------------------------------------------- /base64_decode.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # 3 | # Decode base64 manually. Written by davidhcefx, 2020.7.27. 4 | 5 | import readline 6 | from sys import stderr 7 | from string import ascii_uppercase, ascii_lowercase, digits 8 | 9 | CHARSET = ascii_uppercase + ascii_lowercase + digits + '+/' # length: 64 10 | TABLE = {ch: '{:0>6}'.format(bin(i)[2:]) for i, ch in enumerate(CHARSET)} 11 | 12 | 13 | def warn(msg: str): 14 | print('Warning: {}'.format(msg), file=stderr) 15 | 16 | def valid_check(binary: str, paddings: int): 17 | if (len(binary) + paddings * 6) % 24 != 0: 18 | warn('Input length incorrect!') 19 | return 20 | if paddings == 1: # 16 bits 21 | if binary[-2:] != '00': 22 | warn('Malformed encoding. (1)') 23 | elif paddings == 2: # 8 bits 24 | if binary[-4:] != '0000': 25 | warn('Malformed encoding. (2)') 26 | elif paddings >= 3: 27 | warn('Too many paddings!') 28 | 29 | def main(): 30 | s = input().strip() 31 | binary = ''.join(map(TABLE.get, s.strip('='))) 32 | # discard any remainders 33 | hex_code = list(map( 34 | lambda b: hex(int(b, 2)), 35 | filter( 36 | lambda b: len(b) == 8, 37 | (binary[i:i+8] for i in range(0, len(binary), 8))))) 38 | text = ''.join(map(lambda h: chr(int(h, 16)), hex_code)) 39 | 40 | print('hex:', hex_code) 41 | print('ascii:', text) 42 | valid_check(binary, s.count('=', -3)) 43 | 44 | 45 | if __name__ == '__main__': 46 | main() 47 | 48 | 49 | ### Examples: 50 | # - BA== (correct) 51 | # - BA (no padding) 52 | # - B/== (wrong encoding) 53 | 54 | ### Update log: 55 | # - 8/14 rewrite with functions, constant table lookup 56 | -------------------------------------------------------------------------------- /bash_timestamp.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Please set up to run this script upon user login. You may put this file somewhere else other than ~/bin. 4 | 5 | printf "\n[-- System Login: $USER@`uname -n`, $(date +%-m/%-d:%H) --]\n\n" >> ~/.bash_history 6 | -------------------------------------------------------------------------------- /calculate.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # Just a simple CLI math calculator. Written by davidhcefx, 2023.4.25. 3 | # 4 | from sys import argv 5 | from os.path import basename 6 | try: 7 | # list of additional modules that might be helpful 8 | import math # noqa:F401 9 | import numpy as np # noqa:F401 10 | except ModuleNotFoundError: 11 | pass 12 | 13 | 14 | def calc(expr: str) -> str: 15 | """Evaluate expr and return the result.""" 16 | return str(eval(expr)) 17 | 18 | 19 | if __name__ == '__main__': 20 | if len(argv) == 1: 21 | print('Syntax: {} "math-expression" ...'.format(basename(argv[0]))) 22 | raise SystemExit() 23 | 24 | for r in map(calc, argv[1:]): 25 | print(r) 26 | -------------------------------------------------------------------------------- /hex2binary.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #define BUFSIZE BUFSIZE_32K 7 | #define BUFSIZE_32K 32768 8 | #define STR(x) STR_(x) 9 | #define STR_(x) #x 10 | /** 11 | * Convert hex string from stdin to binary stdout 12 | * Written by davidhcefx, 2023.4.6 13 | */ 14 | 15 | uint8_t octet_to_uint(const char* hex) { 16 | char octet[3] = {0}; 17 | octet[0] = hex[0]; 18 | octet[1] = hex[1]; 19 | return (uint8_t)strtoul(octet, NULL, 16); 20 | } 21 | 22 | int main() { 23 | char input[BUFSIZE + 1]; 24 | uint8_t output[BUFSIZE / 2] = {0}; 25 | uint32_t i; 26 | 27 | fprintf(stderr, "Hex string input:\n"); 28 | if (scanf("%" STR(BUFSIZE) "s", input) <= 0) { 29 | perror("Error: scanf failed"); 30 | return -1; 31 | } 32 | for (i = 0; i < strlen(input); i += 2) { 33 | output[i / 2] = octet_to_uint(&input[i]); 34 | } 35 | write(1, output, strlen(input) / 2); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /hex2binary.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # Convert hex string from stdin to binary stdout, Written by davidhcefx, 2025.1.17 3 | from sys import stderr, stdout 4 | from os import write 5 | from math import ceil 6 | 7 | 8 | def hex2bin(hx: str) -> bytes: 9 | if hx.startswith('0x'): 10 | hx = hx[2:] 11 | size = ceil(len(hx) / 2) 12 | return int(hx, 16).to_bytes(size, 'big') 13 | 14 | 15 | if __name__ == '__main__': 16 | print('Hex string input:', file=stderr) 17 | hx = input('').strip() 18 | write(stdout.fileno(), hex2bin(hx)) 19 | -------------------------------------------------------------------------------- /myscreenshot: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # You can map a hotkey to this script, eg. Ctrl + PrtScn -> `myscreenshot` 4 | # Be sure to install 'imagemagick' from Ubuntu apt-get. 5 | 6 | name=Screenshot_$(date +%Y-%m-%d_%H%M%S) 7 | case $1 in 8 | scale) # Allow you to selected an area for taking a screenshot. 9 | import ~/Pictures/$name.png 10 | ;; 11 | edit) # Pop up image-editing app right away. 12 | import -window root ~/Pictures/$name.png 13 | # You can replace `gimp` with other image-editing apps. 14 | gimp ~/Pictures/$name.png 15 | ;; 16 | *) # Take screenshot silently. 17 | import -window root ~/Pictures/$name.png 18 | ;; 19 | esac 20 | -------------------------------------------------------------------------------- /myvol: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # This script is for adjusting the volume in a finer granularity. 4 | # You can map a hotkey with this script, eg. Shift + VolumeIncrement -> `myvol add`. 5 | # Be sufe to install 'alsa-utils' from Ubuntu apt-get. 6 | 7 | showInfo(){ 8 | echo "Aliases:" 9 | echo " myvol get = amixer -D pulse sget Master" 10 | echo " myvol set x = amixer -D pulse sset Master x%" 11 | echo " myvol add = amixer -D pulse sset Master 2%+" 12 | echo " myvol sub = amixer -D pulse sset Master 2%-" 13 | } 14 | 15 | if [ $# == 0 ]; then 16 | showInfo 17 | else 18 | case $1 in 19 | get) 20 | amixer -D pulse sget Master 21 | ;; 22 | set) 23 | amixer -D pulse sset Master $2% 24 | ;; 25 | add) 26 | amixer -D pulse sset Master 2%+ 27 | ;; 28 | sub) 29 | amixer -D pulse sset Master 2%- 30 | ;; 31 | *) 32 | showInfo 33 | ;; 34 | esac 35 | fi 36 | -------------------------------------------------------------------------------- /octal2UTF8.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | from sys import argv 3 | """ 4 | Convert octal-string to UTF8 string. Written by davidhcefx, 2023.10.27 5 | 6 | $ ./octal2UTF8.py '\147\144\142\347\204\241\346\263\225\351\241\257\347\244\272\347\232\204\344\270\255\346\226\207\345\255\227' 7 | gdb無法顯示的中文字 8 | """ 9 | 10 | def encode(utf8: str) -> str: 11 | bt = utf8.encode() 12 | return '\\' + '\\'.join(map(lambda b: oct(b)[2:], bt)) 13 | 14 | 15 | def decode(octalstr: str) -> str: 16 | bt = bytearray() 17 | bt.extend(map( 18 | lambda s: int(s, 8), 19 | filter(len, octalstr.split('\\')))) 20 | 21 | return bt.decode(encoding='utf8') 22 | 23 | 24 | if __name__ == '__main__': 25 | if len(argv) > 1: 26 | res = decode(argv[1]) 27 | else: 28 | res = decode(input('Octal string input (eg. \\xxx\\yyy): ')) 29 | 30 | print(res) 31 | -------------------------------------------------------------------------------- /practice_password.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # 3 | # Improve password typing skill by practicing it! Written by davidhcefx, 2020.12.11. 4 | from getpass import getpass 5 | from hashlib import sha256 6 | from argparse import ArgumentParser, Namespace 7 | 8 | LOG_FILE = '/home/davidhcefx/bin/.password_log' 9 | CORRECT = '7b791afaaf5909194c94 ' 10 | 11 | 12 | # (redacted sha256) || (masked length info) 13 | def my_hash(_pass: bytes) -> str: 14 | return sha256(_pass).hexdigest()[:-2] + str(len(_pass) & 0xfffffffc) 15 | 16 | def main(args: Namespace): 17 | while True: 18 | _hash = my_hash(getpass('Practice your password!\n> ').encode()) 19 | if args.nolog: 20 | print(_hash, end='\t') 21 | else: 22 | open(LOG_FILE, 'a').write(_hash + '\n') 23 | 24 | print('Correct!' if _hash == CORRECT else 'Opps!') 25 | if not args.infinite: 26 | break 27 | 28 | if __name__ == '__main__': 29 | parser = ArgumentParser(description='Sometimes we typed the wrong password, but there\'s' \ 30 | + ' no way to let ourselves learn from the mistake. This tool stores passwords in hash' \ 31 | + ' (\'{}\'), and can help debugging wrong passwords.'.format(LOG_FILE)) 32 | parser.add_argument('-n', '--nolog', help='Don\'t append to the log, just display it.', \ 33 | action='store_true') 34 | parser.add_argument('-i', '--infinite', help='Infinite loop mode.', action='store_true') 35 | main(parser.parse_args()) 36 | -------------------------------------------------------------------------------- /ps_list_descendants.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | from sys import argv 3 | from typing import Dict, List, Tuple 4 | from subprocess import run, PIPE 5 | """ 6 | Recursively find all descendants of a parent process. Written by davidhcefx, 2023.8.16 7 | 8 | $ bash -c "ps_list_descendants.py $$; echo; echo subshell:\$$" 9 | Descendants: 10 | PID PPID CMD 11 | 1737141 4181380 bash -c ps_list_descendants.py 4181380; echo; echo subshell:$$ 12 | 1737142 1737141 python3 /home/david/bin/ps_list_descendants.py 4181380 13 | 1737147 1737142 ps -Af 14 | 15 | subshell:1737141 16 | """ 17 | 18 | def get_ppid_table() -> Dict[str, List[Tuple[str, str]]]: 19 | table = dict() 20 | out = run(['ps', '-Af'], stdout=PIPE, check=True).stdout 21 | for line in out.decode().strip().split('\n'): 22 | arr = line.split() 23 | pid, ppid, cmd = arr[1], arr[2], ' '.join(arr[7:]) 24 | if ppid not in table: 25 | table[ppid] = [(pid, cmd)] 26 | else: 27 | table[ppid].append((pid, cmd)) 28 | 29 | return table 30 | 31 | 32 | def main(pid: str) -> None: 33 | print('Descendants:') 34 | print('{:12}{:12}{}'.format('PID', 'PPID', 'CMD')) 35 | table = get_ppid_table() 36 | queue = [pid] 37 | while queue: 38 | ppid = queue.pop() 39 | for pid, cmd in table.get(ppid, []): 40 | print('{:12}{:12}{}'.format(pid, ppid, cmd)) 41 | queue.append(pid) 42 | 43 | 44 | if __name__ == '__main__': 45 | if len(argv) > 1: 46 | main(argv[1]) 47 | else: 48 | main(input('Enter a PID: ')) 49 | -------------------------------------------------------------------------------- /recyclebin: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Utility function for listing the trash can, for there is no way to sort 4 | # the files by deletion date. Written by davidhcefx, 2017.12.04. 5 | 6 | help(){ 7 | echo "Syntax: " 8 | echo " recyclebin list - List all, sorted by deletion date." 9 | echo " recyclebin recover [name] - Recover a file to ~/." 10 | echo " recyclebin remove [2017-12] - Remove all files deleted in [date]. 11 | " 12 | } 13 | 14 | tmp=/tmp/recycle.tmp 15 | cd ~/.local/share/Trash/files 16 | case $1 in 17 | list) 18 | stat -c "%.19z - %n" * | sort -t'-' 19 | ;; 20 | recover) 21 | if (( $# < 2 )); then help; exit; fi 22 | 23 | mv $2 ~/ 24 | ;; 25 | remove) 26 | if (( $# < 2 )); then help; exit; fi 27 | 28 | stat -c "%.7z /%n" * | grep ^$2 > $tmp 29 | cat $tmp 30 | echo "" 31 | read -p "Are you sure you want to remove these files? [y/n] " -n 1 ch 32 | if [ $ch == y ]; then 33 | while read line; do 34 | rm -r "${line#*/}" 35 | done < $tmp 36 | fi 37 | rm $tmp 38 | ;; 39 | *) 40 | help 41 | ;; 42 | esac 43 | -------------------------------------------------------------------------------- /tee-safe - Tee with Prompting/README.md: -------------------------------------------------------------------------------- 1 | # tee-safe - Tee with Prompting 2 | 3 | - Ever having this catastrophic experience using `tee` to accidentally overwritten some files and couldn't fully recover them? To prevent it from happening again, you need this script! 4 | 5 | 6 | ## Installation 7 | 8 | 1. Put this file `tee-safe` under your `~/bin` folder. 9 | 10 | 2. a) If you are on Linux, please ensure that [Zenity](https://packages.ubuntu.com/search?keywords=zenity) has been installed. 11 | 12 | b) If you are on Windows, please install the tool [MessageBox](https://github.com/davidhcefx/Windows-MessageBox-for-Cmd) first. 13 | After that, uncomment [Lines 8-13](https://github.com/davidhcefx/My-Bash-Scripts/blob/0f35c73615332b07d0e409bf690b899fb09aad35/tee-safe%20-%20Tee%20with%20Prompting/tee-safe#L8-L13) and comment out [Line 14](https://github.com/davidhcefx/My-Bash-Scripts/blob/0f35c73615332b07d0e409bf690b899fb09aad35/tee-safe%20-%20Tee%20with%20Prompting/tee-safe#L14). 14 | 15 | 3. Finally, you can set up aliases like `alias tee='tee-safe'` to wrap up the original one. 16 | 17 | ## Linux 18 | 19 | 20 | 21 | ## Windows 22 | 23 | 24 | -------------------------------------------------------------------------------- /tee-safe - Tee with Prompting/scnshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/tee-safe - Tee with Prompting/scnshot.png -------------------------------------------------------------------------------- /tee-safe - Tee with Prompting/scnshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidhcefx/My-Bash-Scripts/306c4d22227f49a154d84608284d96b510bd15ab/tee-safe - Tee with Prompting/scnshot2.png -------------------------------------------------------------------------------- /tee-safe - Tee with Prompting/tee-safe: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Prompt when overwriting non-empty file with tee. Written by davidhcefx, 2020.3.31. 4 | 5 | prompt() { 6 | if [[ -e "$1" && -s "$1" ]]; then 7 | # Solution on Windows, see https://github.com/davidhcefx/Windows-MessageBox-for-Cmd 8 | #messagebox "tee" "Overwrite non-empty file '$1'?" yesno 9 | #if [[ $? == 6 ]]; then 10 | # return 0 11 | #else 12 | # return 1 13 | #fi 14 | zenity --question --width=300 --title="tee" --text="tee: Overwrite non-empty file \"$1\" ?" 2>/dev/null 15 | else 16 | return 0 17 | fi 18 | } 19 | 20 | if [[ $# == 0 ]]; then 21 | tee 22 | else 23 | for arg in "$@"; do 24 | case $arg in 25 | --help|--version) 26 | tee $arg 27 | echo "Safe version 'tee-safe' written by davidhcefx." 28 | exit 29 | ;; 30 | -a|--append) 31 | append=true 32 | opts="$opts $arg" 33 | shift 34 | ;; 35 | -*) opts="$opts $arg" 36 | shift 37 | ;; 38 | *) break 39 | ;; 40 | esac 41 | done 42 | 43 | # don't prompt when in append mode 44 | if [[ "$append" == "true" ]]; then 45 | tee $opts $* 46 | else 47 | prompt "$1" && tee $opts $* 48 | fi 49 | fi 50 | -------------------------------------------------------------------------------- /waitfor: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Wait for a proccess and take action when it ended. 4 | # Created by davidhcefx, 2019.6.27. 5 | 6 | # Modify the parameters below. 7 | interval=300 8 | action="shutdown now" 9 | 10 | 11 | help() { 12 | echo "Syntax: waitfor [pid]" 13 | } 14 | 15 | if [ $# == 0 ]; then help; exit; fi 16 | s=$(ps --pid $1 | grep $1) 17 | if [ "$s" == "" ]; then echo "pid $1 currently not running!"; exit; fi 18 | 19 | echo "Action: '$action'" 20 | echo "Waiting for..." 21 | ps --pid $1 22 | while true; do 23 | sleep $interval 24 | s=$(ps --pid $1 | grep $1) 25 | if [ "$s" == "" ]; then break; fi 26 | done 27 | echo "Taking action..." 28 | $action 29 | -------------------------------------------------------------------------------- /zipencrypt: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [[ $# == 0 ]]; then 4 | echo "Syntax: zipencrypt [file]" 5 | echo "Note that it would generate a file ended in .zip" 6 | exit 7 | fi 8 | 9 | case "$1" in 10 | *.zip) 11 | zip "$1.zip" -r -6 --encrypt "$1" 12 | ;; 13 | *.*) 14 | zip "${1%.*}.zip" -r -6 --encrypt "$1" 15 | ;; 16 | *) 17 | zip "$1.zip" -r -6 --encrypt "$1" 18 | ;; 19 | esac 20 | --------------------------------------------------------------------------------