├── 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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------