├── .gitattributes ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── COMMANDS.md ├── LICENSE.md ├── README.md ├── _config.yml ├── bash2json.js ├── build.go ├── build.js ├── images ├── banner.gif ├── for.gif ├── icon.png └── math.gif ├── make-binaries.sh ├── nsroot ├── README.md ├── archive │ ├── compress-tar-gz.json │ ├── compress-tar-xz.json │ ├── compress-zip.json │ ├── decompress-tar-gz.json │ ├── decompress-tar-xz.json │ └── decompress-unzip.json ├── array │ ├── all-elements.json │ ├── at-index.json │ ├── concat.json │ ├── contains.json │ ├── declare.json │ ├── delete-at.json │ ├── delete.json │ ├── filter.json │ ├── iterate.json │ ├── length.json │ ├── print.json │ ├── push.json │ ├── range.json │ ├── replace.json │ ├── reverse.json │ └── set-element-at.json ├── command │ ├── failure-check.json │ ├── hide-error.json │ ├── if-exists.json │ ├── nice.json │ ├── renice.json │ ├── run.json │ ├── substitution.json │ └── success-check.json ├── cryptography │ ├── base64-decode.json │ ├── base64-encode.json │ └── hash.json ├── date │ ├── date-now-short.json │ ├── day-of-month-current.json │ ├── day-of-week-current.json │ ├── day-of-year-current.json │ ├── local-short.json │ ├── month-name-current.json │ ├── month-number-current.json │ ├── utc-long.json │ └── year-current.json ├── event │ ├── on-ctrl-c.json │ └── on-exit.json ├── filesystem │ ├── directories-iterate.json │ ├── directory-create-nested.json │ ├── directory-create.json │ ├── directory-delete-nested.json │ ├── file-delete.json │ ├── file-read.json │ ├── file-search.json │ ├── file-write-multiline-sudo.json │ ├── file-write-multiline.json │ ├── file-write.json │ ├── files-iterate.json │ ├── find-files-or-directories.json │ ├── if-directory-exists.json │ ├── if-file-executable.json │ ├── if-file-exists-and-is-symbolic-link.json │ ├── if-file-exists.json │ ├── if-file-not-empty.json │ ├── if-file-readable.json │ ├── if-file-writeable.json │ ├── if-file1-newer-than-file2.json │ ├── if-file1-older-than-file2.json │ ├── if-files-are-equal.json │ ├── if-path-exists.json │ └── remove-files-older-newer-than.json ├── float │ ├── if-equal.json │ ├── if-greater-or-equal.json │ ├── if-greater.json │ ├── if-lesser-or-equal.json │ ├── if-lesser.json │ └── if-not-equal.json ├── fn-fx │ ├── fn-animate.json │ ├── fn-animation-pacman.json │ ├── fn-banner-color.json │ ├── fn-banner-simple.json │ ├── fn-import.json │ ├── fn-input-choice.json │ ├── fn-input-multi-choice.json │ ├── fn-math-average.json │ ├── fn-math-factorial.json │ ├── fn-math-fibonacci-series.json │ ├── fn-math-fibonacci.json │ ├── fn-math-product.json │ ├── fn-math-sum.json │ ├── fn-progress.json │ ├── fn-scan.json │ ├── fn-time-format-seconds.json │ ├── fn-urldecode.json │ ├── fn-urlencode.json │ ├── fn-version-compare.json │ ├── fx-animate-animate.json │ ├── fx-animate.json │ ├── fx-animation-pacman.json │ ├── fx-banner-color.json │ ├── fx-banner-simple.json │ ├── fx-import.json │ ├── fx-input-choice.json │ ├── fx-input-multi-choice.json │ ├── fx-math fibonacci.json │ ├── fx-math-average.json │ ├── fx-math-factorial.json │ ├── fx-math-fibonacci-series.json │ ├── fx-math-product.json │ ├── fx-math-sum.json │ ├── fx-progress.json │ ├── fx-scan.json │ ├── fx-time-format-seconds.json │ ├── fx-urldecode.json │ ├── fx-urlencode.json │ └── fx-version-compare.json ├── ftp │ ├── delete.json │ ├── download.json │ ├── list-of-files.json │ ├── rename.json │ └── upload.json ├── function │ ├── arguments-array.json │ ├── arguments-count.json │ ├── declare.json │ └── return-value.json ├── git │ ├── begin.json │ ├── branch-create.json │ ├── branch-delete-local.json │ ├── branch-delete-remote.json │ ├── branch-list.json │ ├── branch-push.json │ ├── branch-rename.json │ ├── changes-revert.json │ ├── clone-branch-https.json │ ├── clone-branch.json │ ├── clone-https.json │ ├── clone.json │ ├── commit-list-notpushed.json │ ├── commit-search.json │ ├── commit-undo.json │ ├── commit.json │ ├── config-list.json │ ├── config-set.json │ ├── patch-apply.json │ ├── patch-create.json │ ├── remote-list.json │ ├── remote-url-add-https.json │ ├── remote-url-add.json │ ├── remote-url-change-https.json │ ├── remote-url-change.json │ ├── tag-commit.json │ ├── tag-list.json │ ├── tag-remote-delete.json │ └── tag-remote-push.json ├── http │ ├── cookie.json │ ├── download.json │ ├── get.json │ ├── header.json │ ├── post-send-file.json │ └── post.json ├── input │ ├── password.json │ └── text.json ├── integer │ ├── if-equal.json │ ├── if-greater-or-equal.json │ ├── if-greater.json │ ├── if-lesser-or-equal.json │ ├── if-lesser.json │ └── if-not-equal.json ├── internal │ ├── for-i-j.json │ ├── for-i.json │ ├── for-in-collection.json │ ├── for-in-column.json │ ├── for-in-range.json │ ├── if-elif-else.json │ ├── iff-not.json │ ├── iff.json │ ├── infinite-loop.json │ ├── switch.json │ ├── until.json │ └── while.json ├── ip │ ├── array-of-local.json │ ├── info.json │ └── public-address.json ├── math │ ├── add.json │ ├── const-gamma.json │ ├── const-napier.json │ ├── const-omega.json │ ├── const-phi.json │ ├── const-pi.json │ ├── decrement.json │ ├── divide-equal.json │ ├── divide.json │ ├── expr.json │ ├── increment.json │ ├── let.json │ ├── minus-equal.json │ ├── modulus-equal.json │ ├── modulus.json │ ├── multiply-equal.json │ ├── multiply.json │ ├── plus-equal.json │ ├── power.json │ ├── precision.json │ ├── random-number.json │ ├── sqrt.json │ └── subtract.json ├── misc │ ├── am-I-not-root.json │ ├── am-I-root.json │ ├── animation-frame.json │ ├── arguments-parse.json │ ├── echo-text.json │ ├── echo-variable.json │ ├── exit.json │ ├── osis.json │ ├── region.json │ ├── shebang.json │ ├── sleep.json │ ├── stopwatch-elapsed.json │ ├── stopwatch-start.json │ ├── stopwatch-stop.json │ ├── summary.json │ └── timeout.json ├── output │ ├── color-black.json │ ├── color-blue.json │ ├── color-cyan.json │ ├── color-green.json │ ├── color-magenta.json │ ├── color-red.json │ ├── color-white.json │ ├── color-yellow.json │ ├── format-bold.json │ ├── format-dim.json │ ├── format-italic.json │ └── format-reverse.json ├── process │ ├── process-id.json │ ├── process-instances.json │ ├── process-kill.json │ ├── process-list.json │ └── process-name.json ├── string │ ├── concat.json │ ├── contains.json │ ├── first-index-substring.json │ ├── if-empty.json │ ├── if-equal.json │ ├── if-not-empty.json │ ├── if-not-equal.json │ ├── length.json │ ├── random.json │ ├── replace-all.json │ ├── replace-once.json │ ├── replace.json │ ├── reverse.json │ ├── substring-count.json │ ├── substring.json │ ├── to-lower.json │ ├── to-upper.json │ ├── trim-all.json │ ├── trim-left.json │ ├── trim-right.json │ └── trim.json ├── system │ ├── distro-codename.json │ ├── distro-name.json │ ├── distro-version.json │ ├── kernel-name.json │ ├── kernel-release.json │ ├── memory-info.json │ ├── processor-architecture.json │ ├── processor-count.json │ ├── processor-model.json │ ├── processor-type.json │ ├── service-manage.json │ ├── uptime-seconds.json │ └── uptime.json ├── time │ ├── epoch-seconds.json │ ├── local-time-current.json │ ├── now-local.json │ ├── time-now-utc.json │ └── time-utc.json └── variable │ ├── variable-assign.json │ ├── variable-default-value.json │ └── variable-read.json ├── package-lock.json ├── package.json ├── publish.sh ├── samples ├── animation │ ├── funny-cat.sh │ ├── pacman.sh │ ├── pendulum.sh │ └── wavy-shellman.sh └── backup-tool │ ├── README.md │ └── backup.sh ├── snippets └── snippets.json └── test.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | # .vscode/ 3 | bin/ 4 | *.vsix 5 | build 6 | build.exe 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "timonwong.shellcheck" 4 | ] 5 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "type": "node", 10 | "request": "launch", 11 | "name": "Build Node", 12 | "program": "${file}", 13 | "skipFiles": [ 14 | "/**" 15 | ] 16 | }, 17 | { 18 | "name": "Extension Test", 19 | "type": "extensionHost", 20 | "request": "launch", 21 | "runtimeExecutable": "${execPath}", 22 | "args": [ 23 | "--extensionDevelopmentPath=${workspaceFolder}" 24 | ] 25 | }, 26 | { 27 | "type": "bashdb", 28 | "request": "launch", 29 | "name": "Bash Debug", 30 | "program": "${file}" 31 | }, 32 | { 33 | "name": "Build Go", 34 | "type": "go", 35 | "request": "launch", 36 | "program": "${workspaceFolder}/build.go" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "alnum", 4 | "bashdb", 5 | "civis", 6 | "cnorm", 7 | "codename", 8 | "czvf", 9 | "distro", 10 | "docgen", 11 | "elif", 12 | "epub", 13 | "esac", 14 | "EUID", 15 | "exponentiate", 16 | "filepath", 17 | "gitignore", 18 | "Gitter", 19 | "gsub", 20 | "Haltarys", 21 | "icanhazip", 22 | "ifconfig", 23 | "imagemagick", 24 | "ipecho", 25 | "ipify", 26 | "ipinfo", 27 | "jsbrain", 28 | "KHTML", 29 | "lastrow", 30 | "libname", 31 | "localhost's", 32 | "lscpu", 33 | "Mascheroni", 34 | "maxdepth", 35 | "meminfo", 36 | "mobi", 37 | "multichoice", 38 | "multiline", 39 | "mylib", 40 | "notpushed", 41 | "nsroot", 42 | "osis", 43 | "ovsx", 44 | "Pacman", 45 | "pgrep", 46 | "pidof", 47 | "PIDs", 48 | "printf", 49 | "proto", 50 | "quickstart", 51 | "readarray", 52 | "Remisa", 53 | "renice", 54 | "retval", 55 | "RNFR", 56 | "RNTO", 57 | "rwin", 58 | "semvers", 59 | "setaf", 60 | "shasum", 61 | "shellcheck", 62 | "shellman", 63 | "SIGINT", 64 | "sitm", 65 | "somefile", 66 | "startrow", 67 | "stty", 68 | "systemctl", 69 | "tnum", 70 | "tolower", 71 | "tprintf", 72 | "tput", 73 | "treadarray", 74 | "ttput", 75 | "uarr", 76 | "uname", 77 | "urandom", 78 | "urldecode", 79 | "urlencode", 80 | "useremail", 81 | "wangyuew", 82 | "whatismyipaddress", 83 | "xargs", 84 | "xenial", 85 | "xzvf", 86 | "Yousefvand" 87 | ] 88 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "echo", 8 | "type": "shell", 9 | "command": "echo Hello" 10 | }, 11 | { 12 | "label": "build-test", 13 | "type": "shell", 14 | "command": "./build; cp -f /data/Code/shellman/snippets/snippets.json ~/.config/Code/User/snippets/shellscript.json" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | bin/** 3 | node_modules/** 4 | nsroot/** 5 | samples/** 6 | _config.yml 7 | .gitattributes 8 | .gitignore 9 | build* 10 | Dockerfile 11 | publish* 12 | test* 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 6.0.0 4 | 5 | - some improvements. 6 | - fixed #52 7 | - fixed #53 8 | 9 | ## 5.8.0 10 | 11 | - some improvements to become more standard. 12 | 13 | ## 5.7.0 14 | 15 | Fixes issue #48 16 | 17 | ## 5.6.1 18 | 19 | - Fixed #47 (thanks to Haltarys) 20 | 21 | ## 5.6.0 22 | 23 | - `shellcheck` compatible (99.99%). 24 | - Added `array print` snippet. 25 | 26 | ## 5.5.1 27 | 28 | - `build.go` now generates `COMMANDS.md` as well. 29 | - Fixed #34 (Argument parsing for switches may result in infinite loop). 30 | - Numerous bug fixed in `fn`s. 31 | 32 | ## 5.5.0 33 | 34 | - `process Name by ID`: Get process name by it's ID(s). 35 | - Fixed #28 (COMMANDS.md links not working). 36 | - Added animation examples for pendular mode. 37 | - Fixed #29 (thanks to @rwin-novo). 38 | - Fixed #30 and #32 (thanks to @jsbrain). 39 | - Code section removed from `COMMANDS.md`. 40 | 41 | ## 5.4.0 42 | 43 | - `exit code` for gracefully inform what error happened. 44 | - `array reverse` snippet to reverse array elements order added. 45 | - `hide command error` snippet to suppress command error (stderr), 46 | - `fn animation animate` now supports two mode: circular, pendular. 47 | - Binary build tool for major operating systems is available. 48 | 49 | ## 5.3.0 50 | 51 | - `iterate directories` snippet added. 52 | - `archive compress .zip` snippet added. 53 | - `archive decompress .zip` snippet added. 54 | - `system uptime seconds` snippet added. Seconds can be formatted by `fn time format seconds` snippet. 55 | - `remove old/new files/directories` snippet for removing files (-type f) or directories (-type d) if they are older (-mtime n) than n days or newer (+mtime n) than n days. 56 | - Comparison of floating point numbers: `float|double >`, `float|double >=`, `float|double <`, `float|double <=`, `float|double ==`, `float|double !=` added. 57 | 58 | ## 5.2.0 59 | 60 | - `echo array` snippet added. 61 | - `fn/fx math factorial` snippet added. 62 | - `fn/fx math fibonacci` snippet added. 63 | - `fn/fx math fibonacci series` snippet added. 64 | - `am I root` & `am I not root` snippets added. 65 | - `fn/fx input multichoice`: select more than one option. 66 | - `array contains`: check if the array contains an element". 67 | - `fn/fx time format seconds` snippet to format seconds into days/hours/minutes/seconds. 68 | 69 | Now there is a `input` namespace for getting user input: 70 | 71 | - `input text`: formerly `ask question`. 72 | - `input password`: don't show input on screen. 73 | - `fn/fx input choice`: formerly `fn/fx options`. 74 | 75 | ## 5.1.0 76 | 77 | - Smooth percentage for `fn progress`. 78 | - `echo text` and `echo variable` snippet added. 79 | - assign variable value via `variable assign` | `variable set` snippet. 80 | - `iterate files` snippet for iterating specific file extensions inside a path. 81 | - read/expand variable value via `var` | `variable read` | `variable expand` snippet. 82 | 83 | ## 5.0.0 84 | 85 | - Default command substitution to `$(command)`. 86 | - Default variable expansion to `"${variable}"`. 87 | - Unified TAB ordering. 88 | - Some functions and snippets enhanced. 89 | - Descriptive aliases added to some snippets. 90 | 91 | ## 4.10.0 92 | 93 | - `fn/fx urlencode`: Encode URL. 94 | - `fn/fx urldecode`: Decode URL. 95 | 96 | ## 4.9.0 97 | 98 | - `string random`: Random string from provided characters with desired length. 99 | 100 | ## 4.8.0 101 | 102 | - `loop infinite`: Infinite loop. 103 | 104 | ## 4.7.0 105 | 106 | - New `Shellman ebook` release. [Download](https://github.com/yousefvand/shellman-ebook) free ebook (pdf, epub, mobi) 107 | - Fixed issues [#20](https://github.com/yousefvand/shellman/issues/20) and [#21](https://github.com/yousefvand/shellman/issues/21). 108 | - directory 109 | - `directory delete nested` | `directory remove nested`: delete directory and all contents. 110 | - Updated `summary` snippet to include exit codes. 111 | - Added `loop` to `while` and `until` prefix. 112 | - Added samples: 113 | - Step by step [sample script](samples/backup-tool) tutorial. 114 | 115 | ## 4.6.0 116 | 117 | - Fixed issue [#19](https://github.com/yousefvand/shellman/issues/19) 118 | - Handle events `event ...` 119 | - `EXIT`: Run commands when script exits. 120 | - `CTRL+C`: Run commands when script is interrupted. 121 | - Animation 122 | - Define animation frame. 123 | - Call `animate` function. Check [sample animations](samples/animation/funny-cat.sh). 124 | - pacman animation for texts. Check [sample animations](samples/animation/pacman.sh). 125 | - String 126 | - Concat: Concatenate two strings. 127 | 128 | ## 4.5.0 129 | 130 | - Renice process and sub processes. [PR](https://github.com/yousefvand/shellman/pull/17) by [@jakiro2017](https://github.com/jakiro2017). 131 | - `archive...` 132 | - Compress/decompress file/directory to/from `.tar.xz` 133 | 134 | ## 4.4.0 135 | 136 | - Fixed issue [#13](https://github.com/yousefvand/shellman/issues/13). Thanks to [@wangyuew](https://github.com/wangyuew). 137 | - `fn/fx [version | semver] compare` 138 | - Compare two [semver](https://semver.org) versions. Function compares first argument with the second and returns `>`, `<` or `=`. 139 | - `string substring [count | frequency]`: Frequency of a substring in a string. You may need character escaping, for example if string is `1.2.3` and substring is `.` then substring should be `\.` when passed to command. 140 | 141 | ## 4.3.1 142 | 143 | - Fixed issue [#10](https://github.com/yousefvand/shellman/issues/10) in math namespace. 144 | - Some enhancements in math namespace. 145 | 146 | ## 4.3.0 147 | 148 | - Process commands `process ...` 149 | - `list`: List all processes. 150 | - `id`: Get process ID by name. 151 | - `kill`: Kill process by name. 152 | - `cmd renice`: Change running process priority. 153 | 154 | ## 4.2.0 155 | 156 | - Shellman ebook available for [download](https://github.com/yousefvand/shellman-ebook). 157 | - `math fn/fx` 158 | - `sum`: Calculate sum of given integers. 159 | - `product`: Calculate product of given integers. 160 | - `average`: Calculate average of given integers. 161 | 162 | ## 4.1.1 163 | 164 | - Minor bugs fixed. 165 | - `let` added for arithmetic operations. 166 | 167 | ## 4.0.0 168 | 169 | - fixed issues [#3](https://github.com/yousefvand/shellman/issues/3), [#4](https://github.com/yousefvand/shellman/issues/4), [#5](https://github.com/yousefvand/shellman/issues/5), [#6](https://github.com/yousefvand/shellman/issues/6), [#7](https://github.com/yousefvand/shellman/issues/7) 170 | 171 | ## 3.6.1 172 | 173 | - Escape dollar sign 174 | 175 | ## 3.6.0 176 | 177 | - `system...` 178 | - System information (CPU, Memory, Kernel...) 179 | 180 | ## 3.5.0 181 | 182 | - `git...` 183 | - `git` commands. 184 | 185 | ## 3.4.0 186 | 187 | - `ftp...` 188 | - `list`: Get the list of files on the ftp server at specific path. 189 | - `download`: Download specified file from ftp server. 190 | - `upload`: Upload specified file to ftp server. 191 | - `delete`: Delete specified file from ftp server. 192 | - `rename`: Rename specified file/directory on ftp server. 193 | 194 | ## 3.3.0 195 | 196 | - `http...` 197 | - `GET`: Send http GET request using curl. 198 | - `POST`: Send data with http POST, using curl. 199 | - `POST file`: Send file via http POST, using curl. 200 | - `PUT`: Send data with http PUT, using curl 201 | - `DELETE`: Send http DELETE request using curl. 202 | - `cookie`: Send http request with cookies, using curl. 203 | - `header`: Send http request with protocol/custom header, using curl. 204 | - `download`: Download from url and save to `/path/to/file`, using curl. 205 | 206 | ## 3.2.0 207 | 208 | - `fn/fx progress` 209 | - Dummy progress bar with custom message 210 | 211 | ## 3.1.0 212 | 213 | - `nice` 214 | - Execute a command with desired privilege 215 | - `archive...` 216 | - Compress/decompress file/directory (currently .tar.gz supported. More soon) 217 | - `crypto...` 218 | - base64 encode / decode 219 | - Calculate hash of string using md5, sha, sha1, sha224, sha256, sha384 and sha512 algorithms 220 | 221 | ## 3.0.0 222 | 223 | - `array...` 224 | - Array namespace 225 | 226 | ## 2.3.1 227 | 228 | - `date...`, `time...` 229 | - Date and time commands 230 | - `file find` 231 | - Find files and directories by name (wildcard supported) 232 | - `file search` 233 | - Search inside all files for a text (i.e. word) 234 | 235 | ## 2.2.1 236 | 237 | - Argument parsing 238 | - parse command line arguments (flags/switches) 239 | - bug fixed (int less than) 240 | 241 | ## 2.1.0 242 | 243 | - Documentation improved. 244 | - `import` function (fn/fx) added. 245 | - Import functions from other shell script files. 246 | - `options` function (fn/fx) added. 247 | - Provide a list of options to user. 248 | 249 | ## 2.0.0 250 | 251 | - `math...` 252 | - math constants (π, e, Ω, ...) 253 | - math operations (with precision). 254 | 255 | ## 1.5.1 256 | 257 | - `string substring` typo fixed. 258 | 259 | ## 1.5.0 260 | 261 | - `region`: A region for specific purpose (functions, variables...) 262 | - string manipulation 263 | - `string length`: length of string in characters. 264 | - `string trim`: remove leading and trailing white space(s). 265 | - `string trim left`: remove leading white space(s). 266 | - `string trim right`: remove trailing white space(s). 267 | - `string trim all`: remove all white space(s). 268 | - `string replace`: find all occurrences of a substrings and replace them. 269 | - `string reverse`: reverse string characters. 270 | - `string toLower`: convert string to lowercase. 271 | - `string toUpper`: convert string to uppercase. 272 | - `string substring`: part of the string from offset with [length] characters. 273 | - `string contains`: check whether string contains substring. 274 | - `string indexOf`: first index of substring in string. 275 | 276 | ## 1.4.0 277 | 278 | - `assign array`: assign elements to an array. 279 | - `ip ...` 280 | - `ips`: array of local IPs. 281 | - `ip info`: public ip information (country, city...). 282 | - `ip public`: public ip address. 283 | - `random number`: generate random integer x such as min < x < max. 284 | - `service manage`: manage service operations via `systemctl`. 285 | - `timeout`: run command within a time frame. Cancel command if not finished within x seconds. 286 | - `fn/fx` 287 | - `scan`: scan host's port range and find open ports (tcp/udp). 288 | 289 | ## 1.3.0 290 | 291 | - stopwatch 292 | - `fn...` / `fx...` whole function and usage insertion. 293 | - `fn banner simple`: insert function to print a banner. 294 | - `fn banner color`: insert function to print a color banner. 295 | 296 | ## 1.2.0 297 | 298 | - Check last command success/failure via `cmd...` 299 | - Directory operations via `directory...` 300 | 301 | ## 1.1.2 302 | 303 | - Fixed vscode marketplace header color contrast 304 | 305 | ## 1.1.1 306 | 307 | - Minor bugs fixed 308 | - Write colorful text 309 | - Write formatted text (bold, italic, dim, reverse) 310 | - Snippet documentation added 311 | 312 | ## 0.0.1 313 | 314 | Initial release 315 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018 Remisa Yousefvand 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shellman 2 | 3 | [![GitHub release](https://img.shields.io/github/release/yousefvand/shellman.svg?style=plastic)](https://github.com/yousefvand/shellman/releases) 4 | [![GitHub license](https://img.shields.io/github/license/yousefvand/shellman.svg?style=plastic)](https://github.com/yousefvand/shellman/blob/master/LICENSE.md) 5 | [![GitHub stars](https://img.shields.io/github/stars/yousefvand/shellman.svg?style=plastic)](https://github.com/yousefvand/shellman/stargazers) 6 | [![GitHub issues](https://img.shields.io/github/forks/yousefvand/shellman.svg?style=plastic)](https://github.com/yousefvand/shellman/forks) 7 | [![GitHub issues](https://img.shields.io/github/issues/yousefvand/shellman.svg?style=plastic)](https://github.com/yousefvand/shellman/issues) 8 | 9 | ## Shell scripting snippet 10 | 11 | Learn easy Shell Scripting with `Shellman`, examples included. [Download](https://github.com/yousefvand/shellman-ebook) free ebook (pdf, epub). Reading the `Basics` part of the book is strongly recommended if you are new to `Shell Scripting`. Finally first edition of the ebook published. 12 | 13 | Read [Shellman story on medium](https://medium.com/@remisa.yousefvand/shellman-reborn-f2cc948ce3fc) (3 min read). 14 | 15 | ![for in range](images/for.gif) 16 | 17 | ![math square root](images/math.gif) 18 | 19 | ![fn/fx: simple banner](images/banner.gif) 20 | 21 | Instead of language specific syntax, here `Shell Scripting`, `Shellman` focuses on programming concepts. These concepts are grouped under `namespaces`. For example `string` namespace to name a few contains: 22 | 23 | - concat 24 | - length 25 | - reverse 26 | - toLower 27 | - toUpper 28 | - trim 29 | - ... 30 | 31 | to activate desired `snippet` you need to type `string` and select desired `snippet` from listed `snippets` i.e. `string reverse`. Using TAB key fill needed info and you are done. 32 | 33 | Sometimes doing a job takes more than a `snippet`. `Shellman` has ready to use functions to put into your code if you type `fn`, and selected function usage is available after typing `fx`. For example the function to create a banner with desired text can be accessed via `fn banner simple` and to call this function the `snippet` is `fx banner simple`. Pass required parameters and a banner with your text would be printed. 34 | 35 | ### List of [commands](COMMANDS.md) 36 | 37 | ### [Full release Notes](CHANGELOG.md) 38 | 39 | ### [Contribution](nsroot/README.md) 40 | 41 | ### Latest release Notes 42 | 43 | ## 6.0.0 44 | 45 | - `shellcheck` compatible (99.99%) 46 | - Fixes issue #52, #53 47 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-merlot -------------------------------------------------------------------------------- /bash2json.js: -------------------------------------------------------------------------------- 1 | // Convert Shellscript function into JSON array 2 | const fs = require("fs"); 3 | 4 | // Constants 5 | inputFile = "./test.sh"; 6 | outputFile = "./test.json"; 7 | 8 | const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); 9 | 10 | pipe( 11 | // Read input file 12 | () => { 13 | try { 14 | return fs.readFileSync(inputFile, "utf8"); 15 | } catch (err) { 16 | `Cannot read input file: ${inputFile}. Error: ${err}`; 17 | } 18 | }, 19 | // lines 20 | string => string.split("\n"), 21 | lines => 22 | // replacing leading spaces (tab size = 2: "\s\s") with "\t" 23 | lines.map(line => line.replace(/^\s+/, m => m.replace(/\s\s/g, "\t"))), 24 | // escaping \ 25 | lines => lines.map(line => line.replace("\\", "\\")), 26 | // escaping $ 27 | lines => lines.map(line => line.replace(/\$/g, "\\$")), 28 | // to JSON 29 | o => JSON.stringify(o, null, 2), 30 | // write output file 31 | data => { 32 | try { 33 | fs.writeFileSync(outputFile, data); 34 | } catch (error) { 35 | `Cannot write output file: ${outputFile}. Error: ${err}`; 36 | } 37 | } 38 | )(); 39 | -------------------------------------------------------------------------------- /build.go: -------------------------------------------------------------------------------- 1 | /* 2 | Tool for compiling small snippets in `nsroot` directory into a single snippet file 3 | at `snippets/snippets` and generating `COMMANDS.md`. 4 | 5 | To compile to binary (you need golang installed): 6 | $ go build build.go 7 | 8 | To run without build (you need golang installed): 9 | $ go run build.go 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "bytes" 16 | "encoding/json" 17 | "fmt" 18 | "io/ioutil" 19 | "os" 20 | "path/filepath" 21 | "strings" 22 | ) 23 | 24 | const Version = "1.1.0" // 2021-05-02 25 | const root = "nsroot" 26 | const tabSpace = " " 27 | 28 | type Snippet struct { 29 | Prefix interface{} `json:"prefix"` 30 | Body interface{} `json:"body"` 31 | Description string `json:"description"` 32 | } 33 | 34 | func main() { 35 | 36 | if len(os.Args) > 1 { // No argument accepted 37 | fmt.Printf(`Shellman build tool v%v 38 | 39 | This tool doesn't accept any argument. Run it from project root directory. 40 | It concatenates 'nsroot' snippets to 'snippets/snippets.json' and generates 'COMMANDS.md'. 41 | `, Version) 42 | os.Exit(1) 43 | } 44 | 45 | cwd, errGetCurrentDirectory := os.Getwd() 46 | if errGetCurrentDirectory != nil { 47 | fmt.Printf("Cannot get current directory due to error: %v\n", errGetCurrentDirectory) 48 | panic(errGetCurrentDirectory) 49 | } 50 | snippetFilePath := filepath.Join(cwd, "snippets", "snippets.json") 51 | snippetFilePath = filepath.FromSlash(snippetFilePath) // OS agnostic path 52 | docFilePath := filepath.Join(cwd, "COMMANDS.md") 53 | docFilePath = filepath.FromSlash(docFilePath) // OS agnostic path 54 | 55 | docTitleBuilder := strings.Builder{} 56 | docBodyBuilder := strings.Builder{} 57 | 58 | docTitleBuilder.WriteString("# Commands\n\n") 59 | 60 | changeToRootErr := os.Chdir(root) 61 | if changeToRootErr != nil { 62 | fmt.Println("Run this program from Shellman root directory") 63 | panic(changeToRootErr) 64 | } 65 | 66 | folders := getFolders(".") 67 | snippets := map[string]Snippet{} 68 | 69 | for _, folder := range folders { 70 | docTitleBuilder.WriteString("### " + folder + "\n\n") 71 | files := getFiles(folder) 72 | 73 | errChangeToChildDir := os.Chdir(folder) 74 | if errChangeToChildDir != nil { 75 | fmt.Printf("Cannot change path to child directory: %s due to error: %v\n", 76 | folder, errChangeToChildDir) 77 | panic(errChangeToChildDir) 78 | } 79 | 80 | for _, file := range files { 81 | 82 | snippetName := folder + "." + strings.TrimSuffix(file, ".json") 83 | jsonFileBytes, err := ioutil.ReadFile(file) 84 | if err != nil { 85 | fmt.Printf("Cannot read file: %s due to error: %v\n", file, err) 86 | panic(err) 87 | } 88 | 89 | var snippet Snippet 90 | json.Unmarshal(jsonFileBytes, &snippet) 91 | namedSnippet := map[string]Snippet{ 92 | snippetName: snippet, 93 | } 94 | 95 | for k, v := range namedSnippet { 96 | snippets[k] = v 97 | 98 | prefixes := []string{} 99 | docTitleBuilder.WriteString(" - [") 100 | docBodyBuilder.WriteString("## ") 101 | 102 | switch v.Prefix.(type) { 103 | case []interface{}: // snippet with multiple prefixes 104 | for _, prefix := range v.Prefix.([]interface{}) { 105 | p := prefix.(string) 106 | prefixes = append(prefixes, p) 107 | } 108 | link := strings.Join(prefixes[:], " , ") 109 | docTitleBuilder.WriteString(link) 110 | docTitleBuilder.WriteString("](#") 111 | 112 | docBodyBuilder.WriteString(link + "\n\n") 113 | docBodyBuilder.WriteString(v.Description + "[↑](#" + folder + ")\n\n") 114 | 115 | link = strings.ReplaceAll(link, " ", "-") 116 | docTitleBuilder.WriteString(link) 117 | case interface{}: // snippet with single prefix 118 | p := v.Prefix.(string) 119 | docTitleBuilder.WriteString(p) 120 | docTitleBuilder.WriteString("](#") 121 | 122 | docBodyBuilder.WriteString(p + "\n\n") 123 | docBodyBuilder.WriteString(v.Description + "[↑](#" + folder + ")\n\n") 124 | 125 | link := strings.ReplaceAll(p, " ", "-") 126 | docTitleBuilder.WriteString(link) 127 | default: 128 | panic("Unknown prefix type") 129 | } 130 | 131 | docTitleBuilder.WriteString(")\n\n") 132 | } 133 | } 134 | errChangeToParentDir := os.Chdir("..") 135 | if errChangeToParentDir != nil { 136 | fmt.Printf("Cannot change path to parent directory: %s due to error: %v\n", 137 | folder, errChangeToParentDir) 138 | panic(errChangeToParentDir) 139 | } 140 | } 141 | 142 | // write snippet file 143 | var buf bytes.Buffer 144 | jsonEncoder := json.NewEncoder(&buf) 145 | jsonEncoder.SetEscapeHTML(false) 146 | jsonEncoder.SetIndent("", tabSpace) 147 | jsonEncoder.Encode(snippets) 148 | snippetFileErr := ioutil.WriteFile(snippetFilePath, buf.Bytes(), 0755) 149 | if snippetFileErr != nil { 150 | fmt.Printf("Cannot write snippet file: %v due to error: %v\n", 151 | snippetFilePath, snippetFileErr) 152 | panic(snippetFileErr) 153 | } 154 | 155 | // Write documentation file 156 | docFile, docFileErr := os.Create(docFilePath) 157 | if docFileErr != nil { 158 | fmt.Printf("Cannot create documentation file: %v due to error: %v\n", 159 | docFilePath, docFileErr) 160 | panic(docFileErr) 161 | } 162 | defer docFile.Close() 163 | documentation := docTitleBuilder.String() + docBodyBuilder.String() 164 | _, docFileWriteErr := docFile.WriteString(documentation) 165 | if docFileWriteErr != nil { 166 | fmt.Printf("Cannot write documentation file: %v due to error: %v\n", 167 | docFilePath, docFileWriteErr) 168 | panic(docFileWriteErr) 169 | } 170 | 171 | } 172 | 173 | func getFolders(root string) []string { 174 | var folders []string 175 | files, err := ioutil.ReadDir(root) 176 | if err != nil { 177 | fmt.Printf("Cannot read "+root+" directories: %v due to error: %v\n", root, err) 178 | panic(err) 179 | } 180 | for _, fileInfo := range files { 181 | if fileInfo.IsDir() { 182 | folders = append(folders, fileInfo.Name()) 183 | } 184 | } 185 | return folders 186 | } 187 | 188 | func getFiles(dir string) []string { 189 | files, err := ioutil.ReadDir(dir) 190 | if err != nil { 191 | fmt.Printf("Cannot read directory: %v due to error: %v\n", dir, err) 192 | panic(err) 193 | } 194 | 195 | var result []string 196 | for _, fileInfo := range files { 197 | if fileInfo.IsDir() { 198 | fmt.Printf("Namespace directories should not contain nested directories: '%v' contains '%v'\n", dir, fileInfo.Name()) 199 | panic("Found nested directories inside namespace directory") 200 | } 201 | result = append(result, fileInfo.Name()) 202 | } 203 | return result 204 | } 205 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | // NOTE: This script doesn't work on Windows. 2 | // This script doesn't work for unknown reason. Use Go tool. 3 | 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | 7 | // pipe :: [Function] -> Function 8 | const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); 9 | 10 | // directoryFiles :: String -> [String] 11 | function directoryFiles(dir, result = []) { 12 | fs.readdirSync(dir).forEach(item => 13 | fs.statSync(path.join(dir, item)).isDirectory() 14 | ? directoryFiles(path.join(dir, item), result) 15 | : result.push(path.join(dir, item)) 16 | ); 17 | return result; 18 | } 19 | 20 | // objectToString :: Object -> String 21 | const objectToString = o => JSON.stringify(o, null, 2); 22 | 23 | // jsonFiles :: [String] -> [String] 24 | const jsonFiles = filesArray => 25 | filesArray.filter(f => f.trim().toLowerCase().slice(-5) === ".json"); 26 | 27 | // filesContent :: [String] -> [[String, Object]] 28 | const filesContent = files => 29 | files.map(f => { 30 | try { 31 | return { 32 | key: path.parse(f).dir.split("/")[1] + "." + path.parse(f).name, 33 | value: JSON.parse(fs.readFileSync(f, "utf-8")), 34 | }; 35 | } catch (err) { 36 | throw Error(`Cannot process json file: ${f}\nError: ${err}`); 37 | } 38 | }); 39 | 40 | // arrayToObject :: [[String, Object]] -> Object 41 | const arrayToObject = arrayOfObjects => 42 | arrayOfObjects.reduce((acc, c) => { 43 | acc[c.key] = c.value; 44 | return acc; 45 | }, {}); 46 | 47 | // write :: String -> IO 48 | const write = str => 49 | fs.writeFileSync(path.join("snippets", "snippets.json"), str); 50 | 51 | const app = pipe( 52 | directoryFiles, 53 | jsonFiles, 54 | filesContent, 55 | arrayToObject, 56 | objectToString, 57 | write 58 | ); 59 | 60 | // Entry Point 61 | app("./nsroot"); // Generate snippet file 62 | 63 | // Generate Documentation file 64 | // TODO: Numerous minor parsing bugs to fix! 65 | const snippets = JSON.parse( 66 | fs.readFileSync(path.join(__dirname, "./snippets/snippets.json"), "utf-8") 67 | ); 68 | 69 | const nameSpaces = [ 70 | ...new Set(Object.keys(snippets).map(snippet => snippet.split(".")[0])), 71 | ].sort(); 72 | 73 | structure = new Map(); 74 | 75 | for (const ns of nameSpaces) { 76 | members = Object.keys(snippets).filter( 77 | snippet => snippet.split(".")[0] === ns 78 | ); 79 | structure.set(ns, members.sort()); 80 | } 81 | 82 | const toc = ["# Commands"]; 83 | const content = []; 84 | 85 | for (const [ns, members] of structure.entries()) { 86 | toc.push(`### ${ns}`); 87 | for (const member of members) { 88 | let entry = Object.entries(snippets).filter(entry => entry[0] === member); 89 | const snippet = entry[0]; 90 | const snippetName = snippet[0]; 91 | const prefix = snippet[1].prefix; 92 | const body = snippet[1].body; 93 | const description = snippet[1].description; 94 | 95 | // fs.writeFileSync( 96 | // path.join(__dirname, "ZZZ.json"), 97 | // JSON.stringify(entry[0], null, 2) 98 | // ); 99 | 100 | const title = Array.isArray(prefix) ? prefix.join(" , ") : prefix; 101 | const link = title.split(" ").join("-"); 102 | toc.push(` - [${title}](#${link})`); 103 | content.push(`## ${title}`); 104 | content.push(`${description} [↑](#Commands)`); 105 | 106 | // const rgx = /\$\{\d+:(.*?)\}/g; 107 | // let out = "```bash\n"; 108 | // let code = ""; 109 | // let y; 110 | 111 | // if (Array.isArray(body)) { 112 | // let tmp = body.map(line => line.split("\n").join("")); 113 | // tmp = tmp.map(line => line.split("\t").join(" ")); 114 | // tmp = tmp.map(line => unescape(line).split("\\$").join("$")); 115 | // tmp = tmp.map(line => line.split(/\\\$/g).join("$")); 116 | // tmp = tmp.map(line => line.split(/\\\}/g).join("}")); 117 | // tmp = tmp.map(line => { 118 | // let y; 119 | // while ((y = rgx.exec(line)) !== null) { 120 | // line = line.split(y[0]).join(y[1]); 121 | // } 122 | // return line; 123 | // }); 124 | // code = tmp.join("\n"); 125 | // } else { 126 | // // single line body 127 | // code = unescape(body).split("\\$").join("$"); 128 | // code = code.split(/\\\$/g).join("$"); 129 | // code = code.split("\n").join(""); 130 | // while ((y = rgx.exec(code)) !== null) { 131 | // code = code.split(y[0]).join(y[1]); 132 | // } 133 | // code = code.split(/\\\}/g).join("}"); 134 | // } 135 | // out += code.trim(); 136 | // out += "\n```\n\n"; 137 | // content.push(out); 138 | } 139 | } 140 | 141 | fs.writeFileSync( 142 | path.join(__dirname, "COMMANDS.md"), 143 | toc.concat(content).join("\n\n") 144 | ); 145 | -------------------------------------------------------------------------------- /images/banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousefvand/shellman/150dcd4491162eeea1f632be01f4253b037b06c9/images/banner.gif -------------------------------------------------------------------------------- /images/for.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousefvand/shellman/150dcd4491162eeea1f632be01f4253b037b06c9/images/for.gif -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousefvand/shellman/150dcd4491162eeea1f632be01f4253b037b06c9/images/icon.png -------------------------------------------------------------------------------- /images/math.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousefvand/shellman/150dcd4491162eeea1f632be01f4253b037b06c9/images/math.gif -------------------------------------------------------------------------------- /make-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 4 | 5 | # Usage: multiChoice "header message" resultArray "comma separated options" "comma separated default values" 6 | # Credit: https://serverfault.com/a/949806 7 | # TODO: 1) Refactoring to return result array 2) Get input options as array 8 | function multiChoice { 9 | echo "${1}"; shift 10 | echo $(tput dim)-"Change Option: [up/down], Change Selection: [space], Done: [ENTER]" $(tput sgr0) 11 | # little helpers for terminal print control and key input 12 | ESC=$( printf "\033") 13 | cursor_blink_on() { printf "$ESC[?25h"; } 14 | cursor_blink_off() { printf "$ESC[?25l"; } 15 | cursor_to() { printf "$ESC[$1;${2:-1}H"; } 16 | print_inactive() { printf "$2 $1 "; } 17 | print_active() { printf "$2 $ESC[7m $1 $ESC[27m"; } 18 | get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; } 19 | key_input() { 20 | local key 21 | IFS= read -rsn1 key 2>/dev/null >&2 22 | if [[ $key = "" ]]; then echo enter; fi; 23 | if [[ $key = $'\x20' ]]; then echo space; fi; 24 | if [[ $key = $'\x1b' ]]; then 25 | read -rsn2 key 26 | if [[ $key = [A ]]; then echo up; fi; 27 | if [[ $key = [B ]]; then echo down; fi; 28 | fi 29 | } 30 | toggle_option() { 31 | local arr_name=$1 32 | eval "local arr=(\"\${${arr_name}[@]}\")" 33 | local option=$2 34 | if [[ ${arr[option]} == 1 ]]; then 35 | arr[option]=0 36 | else 37 | arr[option]=1 38 | fi 39 | eval $arr_name='("${arr[@]}")' 40 | } 41 | 42 | local retval=$1 43 | local options 44 | local defaults 45 | 46 | IFS=';' read -r -a options <<< "$2" 47 | if [[ -z $3 ]]; then 48 | defaults=() 49 | else 50 | IFS=';' read -r -a defaults <<< "$3" 51 | fi 52 | 53 | local selected=() 54 | 55 | for ((i=0; i<${#options[@]}; i++)); do 56 | selected+=("${defaults[i]}") 57 | printf "\n" 58 | done 59 | 60 | # determine current screen position for overwriting the options 61 | local lastrow=$(get_cursor_row) 62 | local startrow=$(($lastrow - ${#options[@]})) 63 | 64 | # ensure cursor and input echoing back on upon a ctrl+c during read -s 65 | trap "cursor_blink_on; stty echo; printf '\n'; exit" 2 66 | cursor_blink_off 67 | 68 | local active=0 69 | while true; do 70 | # print options by overwriting the last lines 71 | local idx=0 72 | for option in "${options[@]}"; do 73 | local prefix="[ ]" 74 | if [[ ${selected[idx]} == 1 ]]; then 75 | prefix="[x]" 76 | fi 77 | 78 | cursor_to $(($startrow + $idx)) 79 | if [ $idx -eq $active ]; then 80 | print_active "$option" "$prefix" 81 | else 82 | print_inactive "$option" "$prefix" 83 | fi 84 | ((idx++)) 85 | done 86 | 87 | # user key control 88 | case $(key_input) in 89 | space) toggle_option selected $active;; 90 | enter) break;; 91 | up) ((active--)); 92 | if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi;; 93 | down) ((active++)); 94 | if [ $active -ge ${#options[@]} ]; then active=0; fi;; 95 | esac 96 | done 97 | 98 | # cursor position back to normal 99 | cursor_to $lastrow 100 | printf "\n" 101 | cursor_blink_on 102 | 103 | indices=() 104 | for((i=0;i<${#selected[@]};i++)); do 105 | if ((${selected[i]} == 1)); then 106 | indices+=(${i}) 107 | fi 108 | done 109 | 110 | # eval $retval='("${selected[@]}")' 111 | eval $retval='("${indices[@]}")' 112 | } 113 | 114 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 115 | 116 | # >>>>>>>>>>>>>>>>>>>>>>>> Operating Systems >>>>>>>>>>>>>>>>>>>>>>>> 117 | # Windows 118 | windows_amd64='GOOS=windows GOARCH=amd64 go build -o bin/build-windows-amd64.exe build.go' 119 | windows_i386='GOOS=windows GOARCH=386 go build -o bin/build-windows-i386.exe build.go' 120 | 121 | # macOS 122 | mac_amd64='GOOS=darwin GOARCH=amd64 go build -o bin/build-darwin-amd64 build.go' 123 | mac_arm64='GOOS=darwin GOARCH=arm64 go build -o bin/build-darwin-arm64 build.go' 124 | #-# GOOS=darwin GOARCH=386 go build -o bin/build-darwin-386 build.go 125 | #-# GOOS=darwin GOARCH=arm go build -o bin/build-darwin-arm build.go 126 | 127 | # Linux 128 | linux_amd64='GOOS=linux GOARCH=amd64 go build -o bin/build-linux-amd64 build.go' 129 | linux_i386='GOOS=linux GOARCH=386 go build -o bin/build-linux-386 build.go' 130 | linux_arm='GOOS=linux GOARCH=arm go build -o bin/build-linux-arm build.go' 131 | linux_arm64='GOOS=linux GOARCH=arm64 go build -o bin/build-linux-arm64 build.go' 132 | linux_ppc64='GOOS=linux GOARCH=ppc64 go build -o bin/build-linux-ppc64 build.go' 133 | linux_ppc64le='GOOS=linux GOARCH=ppc64le go build -o bin/build-linux-ppc64le build.go' 134 | linux_mips='GOOS=linux GOARCH=mips go build -o bin/build-linux-mips build.go' 135 | linux_mipsle='GOOS=linux GOARCH=mipsle go build -o bin/build-linux-mipsle build.go' 136 | linux_mips64='GOOS=linux GOARCH=mips64 go build -o bin/build-linux-mips64 build.go' 137 | linux_mips64le='GOOS=linux GOARCH=mips64le go build -o bin/build-linux-mips64le build.go' 138 | 139 | # Dragonfly 140 | dragonfly_amd64='GOOS=dragonfly GOARCH=amd64 go build -o bin/build-dragonfly-amd64 build.go' 141 | 142 | # FreeBSD 143 | freebsd_amd64='GOOS=freebsd GOARCH=arm64 go build -o bin/build-freebsd-amd64 build.go' 144 | freebsd_i386='GOOS=freebsd GOARCH=386 go build -o bin/build-freebsd-386 build.go' 145 | freebsd_arm='GOOS=freebsd GOARCH=arm64 go build -o bin/build-freebsd-arm build.go' 146 | 147 | # NetBSD 148 | netbsd_amd64='GOOS=netbsd GOARCH=amd64 go build -o bin/build-netbsd-amd64 build.go' 149 | netbsd_i386='GOOS=netbsd GOARCH=386 go build -o bin/build-netbsd-386 build.go' 150 | netbsd_arm='GOOS=netbsd GOARCH=arm go build -o bin/build-netbsd-arm build.go' 151 | 152 | # OpenBSD 153 | openbsd_amd64='GOOS=openbsd GOARCH=amd64 go build -o bin/build-openbsd-amd64 build.go' 154 | openbsd_i386='GOOS=openbsd GOARCH=386 go build -o bin/build-openbsd-386 build.go' 155 | openbsd_arm='GOOS=openbsd GOARCH=arm go build -o bin/build-openbsd-arm build.go' 156 | 157 | # Plan9 158 | plan9_amd64='GOOS=plan9 GOARCH=amd64 go build -o bin/build-plan9-amd64 build.go' 159 | plan9_i386='GOOS=plan9 GOARCH=386 go build -o bin/build-plan9-386 build.go' 160 | 161 | # Solaris 162 | # GOOS=solaris GOARCH=amd64 go build -o bin/build-openbsd-amd64 build.go 163 | 164 | # <<<<<<<<<<<<<<<<<<<<<<<< Operating Systems <<<<<<<<<<<<<<<<<<<<<<<< 165 | 166 | rm -rf ./bin 2>/dev/null 167 | mkdir ./bin 168 | 169 | # Usage: multiChoice "header message" resultArray "comma separated options" "comma separated default values" 170 | multiChoice "Compile build tool for:" result "linux-amd64;windows-amd64;mac-amd64" "1;1;1" 171 | 172 | if [[ "${result[@]}" =~ '0' ]]; then 173 | eval "${linux_amd64}" 174 | echo $(tput setaf 4)"Compiling build tool for Linux....."$(tput sgr0) 175 | 176 | fi 177 | 178 | if [[ "${result[@]}" =~ '1' ]]; then 179 | eval "${windows_amd64}" 180 | echo $(tput setaf 3)"Compiling build tool for Windows..."$(tput sgr0) 181 | 182 | fi 183 | 184 | if [[ "${result[@]}" =~ '2' ]]; then 185 | eval "${mac_amd64}" 186 | echo $(tput setaf 5)"Compiling build tool for MacOS....."$(tput sgr0) 187 | 188 | fi 189 | -------------------------------------------------------------------------------- /nsroot/README.md: -------------------------------------------------------------------------------- 1 | # Namespaces 2 | 3 | `nsroot` is the root folder of namespaces and each namespace has its own folder inside it. 4 | 5 | Only one depth level is allowed (to maintain readability). 6 | 7 | Add new folder for a new namespace. 8 | 9 | Put snippet inside appropriate folder and run go build tool from root of the project: 10 | 11 | ```bash 12 | ./build 13 | ``` 14 | 15 | or in windows: 16 | 17 | ```bash 18 | />build.exe 19 | 20 | to build go build tool from source: 21 | 22 | ```bash 23 | # Compile executable 24 | go build build.go 25 | 26 | # Run without build 27 | go run build.go 28 | ``` 29 | 30 | or download the appropriate one based on your operating system from project latest release page. This tool doesn't accept arguments and must be executed from project root folder. 31 | 32 | Bump version in `package.json` file according to [SemVer](https://semver.org). 33 | 34 | ## Caution 35 | 36 | - Directory structure depth is fixed to one. 37 | - Don't use dot (`.`) in file and directory names. 38 | 39 | ## Testing changes 40 | 41 | First disable `Shellman` extension. Your compiled version if snippets is in file `snippets/snippets`. Copy all of contents of this file. In vscode from main menu go to: 42 | 43 | ``` 44 | File -> Preferences -> User Snippets 45 | ``` 46 | 47 | Select `shellscript.json` from drop down menu. Delete all file contents and paste `snippets/snippets` content there and save the file. 48 | 49 | Now you can use `test.sh` at project root to test your changes. 50 | 51 | If you are using `vscode` there is a task named `build-test` that will do all above tasks. -------------------------------------------------------------------------------- /nsroot/archive/compress-tar-gz.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "archive compress tar.gz", 4 | "archive tar.gz" 5 | ], 6 | "body": "tar -czvf ${1|/path/to/archive, \"${pathToArchive}\"|}.tar.gz ${2|/path/to/directory-or-file, \"${pathToDirectoryOrFile}\"|}\n", 7 | "description": "compress file/folder to a .tar.gz file" 8 | } -------------------------------------------------------------------------------- /nsroot/archive/compress-tar-xz.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "archive compress tar.xz", 4 | "archive tar.xz" 5 | ], 6 | "body": "tar -cJf ${1|/path/to/archive, \"${pathToArchive}\"|}.tar.xz ${2|/path/to/directory-or-file, \"${pathToDirectoryOrFile}\"|}\n", 7 | "description": "compress file/folder to a .tar.xz file" 8 | } -------------------------------------------------------------------------------- /nsroot/archive/compress-zip.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "archive compress .zip", 4 | "archive zip" 5 | ], 6 | "body": "zip -rq ${1|/path/to/archive, \"${pathToArchive}\"|}.zip ${2|/path/to/directory-or-file,\"${pathToDirectoryOrFile}\"|}\n", 7 | "description": "compress file/folder to a .zip file" 8 | } -------------------------------------------------------------------------------- /nsroot/archive/decompress-tar-gz.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "archive decompress tar.gz", 4 | "decompress tar.gz" 5 | ], 6 | "body": "tar -C ${1|/extract/to/path, \"${extractToPath}\"|} -xzvf ${2|/path/to/archive, \"${pathToArchive}\"|}.tar.gz\n", 7 | "description": "decompress a .tar.gz file to specified path" 8 | } -------------------------------------------------------------------------------- /nsroot/archive/decompress-tar-xz.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "archive decompress tar.xz", 4 | "decompress tar.xz" 5 | ], 6 | "body": "tar -C ${1|/extract/to/path, \"${extractToPath}\"|} -xf ${2|/path/to/archive, \"${pathToArchive}\"|}.tar.xz\n", 7 | "description": "decompress a .tar.xz file to specified path" 8 | } -------------------------------------------------------------------------------- /nsroot/archive/decompress-unzip.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "archive decompress .zip", 4 | "archive unzip" 5 | ], 6 | "body": "unzip -q ${1|/path/to/archive, \"${pathToArchive}\"|}.zip -d ${2|/extract/to/path,\"${extractToPath}\"|}", 7 | "description": "decompress a .zip file to specified path" 8 | } -------------------------------------------------------------------------------- /nsroot/array/all-elements.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array all", 3 | "body": "${0:echo }\"${${1:myArray}[@]}\"", 4 | "description": "access all array elements" 5 | } -------------------------------------------------------------------------------- /nsroot/array/at-index.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array at index", 3 | "body": "${0:echo }\"${${1:myArray}[${2:index}]}\"", 4 | "description": "retrieve element from array at specified index (zero based)" 5 | } -------------------------------------------------------------------------------- /nsroot/array/concat.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array concat", 3 | "body": "${1:newArray}=(\"${${2:array1}[@]}\" \"${${3:array2}[@]}\")\n", 4 | "description": "concatenate two arrays" 5 | } -------------------------------------------------------------------------------- /nsroot/array/contains.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array contains", 3 | "body": [ 4 | "if [[ \"\\${${1:myArray}[*]}\" =~ ${2|'element',\"${value}\"|} ]]; then", 5 | "\techo 'array contains element'", 6 | "fi\n" 7 | ], 8 | "description": "check if the array contains an element" 9 | } -------------------------------------------------------------------------------- /nsroot/array/declare.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array declare", 3 | "body": [ 4 | "${1:myArray}=(", 5 | "\t'${2:constant}'", 6 | "\t\"${3:${variable\\}}\"", 7 | "\t'${4:another constant}'", 8 | ")\n" 9 | ], 10 | "description": "declare an array" 11 | } -------------------------------------------------------------------------------- /nsroot/array/delete-at.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array delete at", 3 | "body": "unset \"${1:myArray}[${2:index}]\"\n", 4 | "description": "delete element at index from array" 5 | } -------------------------------------------------------------------------------- /nsroot/array/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array delete", 3 | "body": "unset ${1:myArray}\n", 4 | "description": "delete entire array" 5 | } -------------------------------------------------------------------------------- /nsroot/array/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array filter", 3 | "body": "readarray -t ${1:filtered} < <(for i in \"${${2:myArray}[@]}\" ; do echo \"\\${i\\}\"; done | grep ${3|',\"|}${4:pattern}${3})\n", 4 | "description": "filter elements of an array based on given grep pattern" 5 | } -------------------------------------------------------------------------------- /nsroot/array/iterate.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "array iterate", 4 | "array forEach" 5 | ], 6 | "body": [ 7 | "for item in \"${${1:myArray}[@]}\"; do", 8 | "\t${2:echo \"\\${item\\}\"}", 9 | "done\n" 10 | ], 11 | "description": "iterate array elements" 12 | } -------------------------------------------------------------------------------- /nsroot/array/length.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array length", 3 | "body": "${1:length}=${#${2:myArray}[@]}\n", 4 | "description": "length of an array" 5 | } -------------------------------------------------------------------------------- /nsroot/array/print.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "array print", 4 | "echo array" 5 | ], 6 | "body": "echo \"\\${${1:myArray}[@]}\"\n", 7 | "description": "iterate array elements" 8 | } -------------------------------------------------------------------------------- /nsroot/array/push.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "array push", 4 | "array add" 5 | ], 6 | "body": "${1:myArray}+=('${2:newItem}')\n", 7 | "description": "push new item to the end of array" 8 | } -------------------------------------------------------------------------------- /nsroot/array/range.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "array slice", 4 | "array range" 5 | ], 6 | "body": "${1:newArray}=\"${${2:myArray}[*]:${3:fromIndex}:${4:n}}\"\n", 7 | "description": "n elements of an array from specified index (zero based)" 8 | } -------------------------------------------------------------------------------- /nsroot/array/replace.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array replace", 3 | "body": "${1:newArray}=${${2:myArray}[*]//${3:find}/${4:replace}}\n", 4 | "description": "find and replace elements in array using regex" 5 | } -------------------------------------------------------------------------------- /nsroot/array/reverse.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array reverse", 3 | "body": [ 4 | "for((i=\\${#${1:myArray}[@]}-1;i>=0;i--)); do", 5 | "\t${2:reversed}+=(\"\\${${1:myArray}[i]}\")", 6 | "done", 7 | "", 8 | "unset \"${1:myArray}\" # optional", 9 | "echo \"\\${${2:reversed}[@]}\"", 10 | "" 11 | ], 12 | "description": "reverse order of array elements" 13 | } -------------------------------------------------------------------------------- /nsroot/array/set-element-at.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "array set element", 3 | "body": "${1:myArray}[${2:index}]=\"${3:value}\"\n", 4 | "description": "set array element at specified index" 5 | } -------------------------------------------------------------------------------- /nsroot/command/failure-check.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "command failure check", 4 | "cmd failure check" 5 | ], 6 | "body": [ 7 | "if ! ${1:command} >/dev/null 2>&1; then", 8 | "\techo \"failed\"", 9 | "else", 10 | "\techo \"succeed\"", 11 | "fi\n" 12 | ], 13 | "description": "check if last command failed" 14 | } -------------------------------------------------------------------------------- /nsroot/command/hide-error.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "hide command error", 4 | "don't show command error" 5 | ], 6 | "body": "${1:command} 2> /dev/null\n", 7 | "description": "If a command fails don't show error (suppress stderr)" 8 | } -------------------------------------------------------------------------------- /nsroot/command/if-exists.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if command exists", 4 | "if cmd exists" 5 | ], 6 | "body": [ 7 | "if [ \"\\$(command -v ${1:command})\" ]; then", 8 | "\t${2:echo \"command \\\"${1:command}\\\" exists on system\"}", 9 | "fi\n" 10 | ], 11 | "description": "check if command exists" 12 | } -------------------------------------------------------------------------------- /nsroot/command/nice.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "command nice", 4 | "cmd nice" 5 | ], 6 | "body": "sudo nice -n ${1|-20,-15,-10,-5,0,5,10,15,19|} ${2:command}\n", 7 | "description": "run command with desired privilege. n: -20 (highest priority) to 19 (lowest priority)" 8 | } -------------------------------------------------------------------------------- /nsroot/command/renice.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "command renice", 4 | "cmd renice" 5 | ], 6 | "body": "for p in \\$(pidof \"${1:processName}\"); do sudo renice -n ${2|-20,-15,-10,-5,0,5,10,15,19|} -p \"\\$p\"; done\n", 7 | "description": "change running process priority. n: -20 (highest priority) to 19 (lowest priority)" 8 | } -------------------------------------------------------------------------------- /nsroot/command/run.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "command", 4 | "cmd", 5 | "command substitution", 6 | "cmd substitution" 7 | ], 8 | "body": "${1:result}=\"$(${2:command})\"\n", 9 | "description": "run command (command substitution)" 10 | } -------------------------------------------------------------------------------- /nsroot/command/substitution.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "command substitution" 4 | ], 5 | "body": "${1:result}=\"$(${2:command})\"\n", 6 | "description": "run command (command substitution)" 7 | } -------------------------------------------------------------------------------- /nsroot/command/success-check.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "command success check", 4 | "cmd success check" 5 | ], 6 | "body": [ 7 | "if ${1:command} >/dev/null 2>&1; then", 8 | "\techo \"succeed\"", 9 | "else", 10 | "\techo \"failed\"", 11 | "fi\n" 12 | ], 13 | "description": "check if last command succeed" 14 | } -------------------------------------------------------------------------------- /nsroot/cryptography/base64-decode.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "crypto base64 decode", 3 | "body": "${1:base64Decoded}=\\$(echo -n \"${2|stringToDecode,${variableToDecode}|}\" | base64 -d)\n", 4 | "description": "decode variable from base64" 5 | } -------------------------------------------------------------------------------- /nsroot/cryptography/base64-encode.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "crypto base64 encode", 3 | "body": "${1:base64Encoded}=\\$(echo -n \"${2|stringToEncode,${variableToEncode}|}\" | base64)\n", 4 | "description": "encode variable to base64" 5 | } -------------------------------------------------------------------------------- /nsroot/cryptography/hash.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "crypto hash", 3 | "body": "${1:hash}=\\$(echo -n \"\\$${2:variableToHash}\" | ${3|md5sum,shasum,sha1sum,sha224sum,sha256sum,sha384sum,sha512sum|} | cut -f1 -d ' ')\n", 4 | "description": "compute hash of variable (md5, sha, sha1, sha224, sha256, sha384, sha512)" 5 | } -------------------------------------------------------------------------------- /nsroot/date/date-now-short.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now short", 3 | "body": "${1:dateShort}=\\$(date -I) ${0:# format: yyyy/mm/dd}\n", 4 | "description": "yyyy/mm/dd" 5 | } -------------------------------------------------------------------------------- /nsroot/date/day-of-month-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now dayOfMonth", 3 | "body": "${1:dayOfMonth}=\\$(date +%d)\n", 4 | "description": "current day of month (1..31)" 5 | } -------------------------------------------------------------------------------- /nsroot/date/day-of-week-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now dayOfWeek", 3 | "body": "${1:dayOfWeek}=\\$(date +%${2|A,a|})\n", 4 | "description": "current day of week name (A: full, a: abbreviated)" 5 | } -------------------------------------------------------------------------------- /nsroot/date/day-of-year-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now dayOfYear", 3 | "body": "${1:dayOfYear}=\\$(date +%j)\n", 4 | "description": "current day of year (1..366)" 5 | } -------------------------------------------------------------------------------- /nsroot/date/local-short.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now short", 3 | "body": "${1:dateShort}=\\$(date -I) ${0:# format: yyyy/mm/dd}\n", 4 | "description": "yyyy/mm/dd" 5 | } -------------------------------------------------------------------------------- /nsroot/date/month-name-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now monthName", 3 | "body": "${1:monthName}=\\$(date +%${2|B,b|})\n", 4 | "description": "current month name (B: full, b: abbreviated)" 5 | } -------------------------------------------------------------------------------- /nsroot/date/month-number-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now monthNumber", 3 | "body": "${1:monthNumber}=\\$(date +%m)\n", 4 | "description": "current month number (1..12)" 5 | } -------------------------------------------------------------------------------- /nsroot/date/utc-long.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now UTC", 3 | "body": "${1:dateUTC}=\\$(date -u)\n", 4 | "description": "coordinated Universal Time" 5 | } -------------------------------------------------------------------------------- /nsroot/date/year-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "date now year", 3 | "body": "${1:year}=\\$(date +%${2|Y,y|})\n", 4 | "description": "current Year (Y: full, y: last two digits)" 5 | } -------------------------------------------------------------------------------- /nsroot/event/on-ctrl-c.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "event CTRL+C", 4 | "event terminated" 5 | ], 6 | "body": [ 7 | "# CTRL+C event handler", 8 | "function on_ctrl_c() {", 9 | "\techo # Set cursor to the next line of '^C'", 10 | "\ttput cnorm # show cursor. You need this if animation is used.", 11 | "\t${1:# i.e. clean-up code here}", 12 | "\texit 1 # Don't remove. Use a number (1-255) for error code.", 13 | "}", 14 | "", 15 | "# Put this line at the beginning of your script (after functions used by event handlers).", 16 | "# Register CTRL+C event handler", 17 | "trap on_ctrl_c SIGINT" 18 | ], 19 | "description": "register a function (handler) to run on script termination (CTRL+C)" 20 | } -------------------------------------------------------------------------------- /nsroot/event/on-exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "event EXIT", 3 | "body": [ 4 | "# Exit event handler", 5 | "function on_exit() {", 6 | "\ttput cnorm # Show cursor. You need this if animation is used.", 7 | "\t${1:# i.e. clean-up code here}", 8 | "\texit 0 # Exit gracefully.", 9 | "}", 10 | "", 11 | "# Put this line at the beginning of your script (after functions used by event handlers).", 12 | "# Register exit event handler.", 13 | "trap on_exit EXIT" 14 | ], 15 | "description": "register a function (handler) to run on script exit" 16 | } -------------------------------------------------------------------------------- /nsroot/filesystem/directories-iterate.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "iterate directories", 3 | "body": [ 4 | "# Make sure path ends with /", 5 | "for directory in ${1| /'path/to/directory'/,\"${pathToDirectory}\"|}*; do", 6 | "\tif [[ -d \"\\${directory}\" && ! -L \"\\${directory}\" ]]; then", 7 | "\t\t${2:echo \"\\${directory\\}\"}", 8 | "\tfi", 9 | "done\n" 10 | ], 11 | "description": "write to a file" 12 | } -------------------------------------------------------------------------------- /nsroot/filesystem/directory-create-nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "directory create nested", 3 | "body": "mkdir -p ${1|\"parent dir/child dir\",\"${pathToCreate}\"|}\n", 4 | "description": "create nested directories" 5 | } -------------------------------------------------------------------------------- /nsroot/filesystem/directory-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "directory create", 3 | "body": "mkdir \"${1:directory name}\"\n", 4 | "description": "create directory" 5 | } -------------------------------------------------------------------------------- /nsroot/filesystem/directory-delete-nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "directory delete nested", 4 | "directory remove nested" 5 | ], 6 | "body": "rm -rf ${1|/path/to/directory,${pathToDirectory}|}\n", 7 | "description": "delete directory and all contents!" 8 | } -------------------------------------------------------------------------------- /nsroot/filesystem/file-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "file delete", 4 | "file remove" 5 | ], 6 | "body": "rm -f ${1|/path/to/file,${pathToFile}|}\n", 7 | "description": "delete file(s)" 8 | } -------------------------------------------------------------------------------- /nsroot/filesystem/file-read.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "file read", 3 | "body": [ 4 | "while IFS= read -r line; do", 5 | "\techo \"\\$line\"", 6 | "done < ${1|/path/ro/file,\"${inputFile}\"|}\n" 7 | ], 8 | "description": "read a file" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/file-search.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "file search", 4 | "search in files", 5 | "find in files" 6 | ], 7 | "body": "${1:result}=\\$(find \"${2|/path/to/search,${pathToSearch}|}\" -maxdepth ${3|1,2,3,4,5,6,7,8,9|} -type f -exec grep \"${4:criteria}\" {} +)\n", 8 | "description": "find files which contain search criteria in given path and below" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/file-write-multiline-sudo.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "file write multiline sudo", 3 | "body": [ 4 | "cat << EOF | sudo tee \"${1|/path/to/file,${filePath}|}\" >/dev/null", 5 | "${2:first line\nsecond line\n...}", 6 | "EOF\n" 7 | ], 8 | "description": "write multiple lines into file when sudo permission is required" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/file-write-multiline.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "file write multiline", 3 | "body": [ 4 | "cat >\"${1|/path/to/file,${filePath}|}\" < \"${2|/path/to/file,${pathToFile}|}\"", 5 | "echo # Empty line", 6 | "for ${3:line} in \"\\${${4:lines}[@]}\"; do", 7 | "\techo \"\\${${3:line}\\}\" >> \"${2}\" # >> means append", 8 | "done\n" 9 | ], 10 | "description": "write to a file" 11 | } -------------------------------------------------------------------------------- /nsroot/filesystem/files-iterate.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "iterate files", 3 | "body": [ 4 | "# Searching in the current directory", 5 | "for file in ${1|*,*.{jpg\\,png}|}; do", 6 | "\t${2:echo \"\\${file\\}\"}", 7 | "done\n" 8 | ], 9 | "description": "write to a file" 10 | } -------------------------------------------------------------------------------- /nsroot/filesystem/find-files-or-directories.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "find file", 4 | "find directory" 5 | ], 6 | "body": "readarray -t resultArray < <(find \"/path/to/search\" -maxdepth 1 -type f -name \"criteria\")\n", 7 | "description": "find files (-type f) or directories (-type d) by name or pattern (*.jpg)" 8 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-directory-exists.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if directory exists", 3 | "body": [ 4 | "if [ -d \"${1|/path/to/directory,${pathToDirectory}|}\" ]; then", 5 | "\t${2:echo \"directory \\\"${1}\\\" exists\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if a directory exists" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file-executable.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file executable", 3 | "body": [ 4 | "if [ -x \"${1|/path/to/file,${filePath}|}\" ]; then", 5 | "\t${2:echo \"file \\\"${1}\\\" is executable\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if file is executable" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file-exists-and-is-symbolic-link.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file link", 3 | "body": [ 4 | "if [ -h \"${1|/path/to/file,${filePath}|}\" ]; then", 5 | "\t${2:echo \"Path \\\"${1}\\\" is a symbolic link\"}", 6 | "fi\n" 7 | ], 8 | "description": "if given path is a symbolic link" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file-exists.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file exists", 3 | "body": [ 4 | "if [ -f \"${1|/path/to/file,${filePath}|}\" ]; then", 5 | "\t${2:echo \"File \\\"${1}\\\" exists\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if the file exists" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file-not-empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file not empty", 3 | "body": [ 4 | "if [ -s \"${1|/path/to/file,${filePath}|}\" ]; then", 5 | "\t${2:echo \"File \\\"${1}\\\" is not empty\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if file size is greater than zero" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file-readable.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file readable", 3 | "body": [ 4 | "if [ -r \"${1|/path/to/file,${filePath}|}\" ]; then", 5 | "\t${2:echo \"File \\\"${1}\\\" is readable\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if file readable" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file-writeable.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file writeable", 3 | "body": [ 4 | "if [ -w \"${1|/path/to/file,${filePath}|}\" ]; then", 5 | "\t${2:echo \"File \\\"${1}\\\" is writeable\"}", 6 | "fi\n" 7 | ], 8 | "description": "if file writeable" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file1-newer-than-file2.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file newer", 3 | "body": [ 4 | "if [ \"${1|/path/to/file1,${filePath1}|}\" -nt \"${2|/path/to/file2,${filePath2}|}\" ]; then", 5 | "\t${3:echo \"Path \\\"${1}\\\" is newer than path \\\"${2}\\\"\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if file1 is newer than file2" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-file1-older-than-file2.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file older", 3 | "body": [ 4 | "if [ \"${1|/path/to/file1,${filePath1}|}\" -ot \"${2|/path/to/file2,${filePath2}|}\" ]; then", 5 | "\t${3:echo \"Path \\\"${1}\\\" is older than path \\\"${2}\\\"\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if file1 is older than file2" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-files-are-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if file =", 3 | "body": [ 4 | "if cmp -s \"${1|/path/to/file1,${filePath1}|}\" \"${2|/path/to/file2,${filePath2}|}\"; then", 5 | "\t${3:echo \"File \\\"${1}\\\" is equal to file \\\"${2}\\\"\"}", 6 | "fi\n" 7 | ], 8 | "description": "check if files are equal" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/if-path-exists.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if path exists", 3 | "body": [ 4 | "if [ -e \"${1|/path/to/something,${pathToSomething}|}\" ]; then", 5 | "\t${2:echo \"Path \\\"${1}\\\" exists\"}", 6 | "fi\n" 7 | ], 8 | "description": "if path exists (file, directory, link...)" 9 | } -------------------------------------------------------------------------------- /nsroot/filesystem/remove-files-older-newer-than.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "remove old/new files/directories", 3 | "body": "find \"${1|/path/to/directory/,${pathToDirectory}|}\"* -type ${2|f,d|} -mtime ${3|-,+|}${4:days} -delete\n", 4 | "description": "find and remove files(f)/directories(d) older(+)/newer(-) than x days" 5 | } -------------------------------------------------------------------------------- /nsroot/float/if-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if float =", 4 | "if double =" 5 | ], 6 | "body": [ 7 | "if (( $(echo \"\\${${1:num1}} == \\${${2:num2}}\" | bc -l) )); then", 8 | "\t${3:echo \"equal\"}", 9 | "fi\n" 10 | ], 11 | "description": "if numbers are equal" 12 | } -------------------------------------------------------------------------------- /nsroot/float/if-greater-or-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if float >=", 4 | "if double >=" 5 | ], 6 | "body": [ 7 | "if (( $(echo \"\\${${1:num1}} >= \\${${2:num2}}\" | bc -l) )); then", 8 | "\t${3:echo \"greater or equal\"}", 9 | "fi\n" 10 | ], 11 | "description": "if num1 is greater to num2" 12 | } -------------------------------------------------------------------------------- /nsroot/float/if-greater.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if float >", 4 | "if double >" 5 | ], 6 | "body": [ 7 | "if (( $(echo \"\\${${1:num1}} > \\${${2:num2}}\" | bc -l) )); then", 8 | "\t${3:echo \"greater\"}", 9 | "fi\n" 10 | ], 11 | "description": "if num2 is greater than num2" 12 | } -------------------------------------------------------------------------------- /nsroot/float/if-lesser-or-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if float <=", 4 | "if double <=" 5 | ], 6 | "body": [ 7 | "if (( $(echo \"\\${${1:num1}} <= \\${${2:num2}}\" | bc -l) )); then", 8 | "\t${3:echo \"lesser or equal\"}", 9 | "fi\n" 10 | ], 11 | "description": "if num1 is lesser or equal to num2" 12 | } -------------------------------------------------------------------------------- /nsroot/float/if-lesser.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if float <", 4 | "if double <" 5 | ], 6 | "body": [ 7 | "if (( $(echo \"\\${${1:num1}} < \\${${2:num2}}\" | bc -l) )); then", 8 | "\t${3:echo \"lesser\"}", 9 | "fi\n" 10 | ], 11 | "description": "if num1 is lesser than num2" 12 | } -------------------------------------------------------------------------------- /nsroot/float/if-not-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if float !=", 4 | "if double !=" 5 | ], 6 | "body": [ 7 | "if (( $(echo \"\\${${1:num1}} != \\${${2:num2}}\" | bc -l) )); then", 8 | "\t${3:echo \"not equal\"}", 9 | "fi\n" 10 | ], 11 | "description": "if numbers are not equal" 12 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-animate.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn animation animate", 3 | "body": [ 4 | "# Usage: animate framesArray interval", 5 | "function animate () {", 6 | "\tlocal frames=(\"\\$@\")", 7 | "", 8 | "\t((lastIndex=\\${#frames[@]} - 1))", 9 | "\tlocal mode=\\${frames[lastIndex]}", 10 | "\tunset \"frames[lastIndex]\"", 11 | "", 12 | "\t((lastIndex=\\${#frames[@]} - 1))", 13 | "\tlocal interval=\\${frames[lastIndex]}", 14 | "\tunset \"frames[lastIndex]\"", 15 | "", 16 | "\t# Comment out next two lines if you are using CTRL+C event handler.", 17 | "${1:\ttrap 'tput cnorm; echo' EXIT\n\ttrap 'exit 127' HUP INT TERM}\n", 18 | "\ttput civis # hide cursor", 19 | "\ttput sc # save cursor position", 20 | "", 21 | "\ttput civis # hide cursor", 22 | "\ttput sc # save cursor position", 23 | "", 24 | "\tindex=0", 25 | "\tmax=\"\\${#frames[@]}\"", 26 | "\tindices=()", 27 | "\tdirection=\"forward\"", 28 | "\treadarray -t forwardIndices < <(seq 0 1 \"\\${max}\")", 29 | "\treadarray -t backwardIndices < <(seq \"\\${max}\" -1 0)", 30 | "", 31 | "\twhile true; do", 32 | "\t\tif [ \"\\${mode}\" = \"circular\" ]; then", 33 | "\t\t\tdirection=\"forward\"", 34 | "\t\telif [ \"\\${mode}\" = \"pendular\" ]; then", 35 | "\t\t\tif (( index >= max )); then", 36 | "\t\t\t\tdirection=\"backward\"", 37 | "\t\t\telif (( index <= 0 )); then", 38 | "\t\t\t\tdirection=\"forward\"", 39 | "\t\t\tfi", 40 | "\t\telse", 41 | "\t\t\techo \"Wrong mode! Valid modes: circular, pendular\"", 42 | "\t\t\texit 255", 43 | "\t\tfi", 44 | "", 45 | "\t\tif [ \"\\${direction}\" = \"forward\" ]; then", 46 | "\t\t\tindices=( \"\\${forwardIndices[@]}\" )", 47 | "\t\telse", 48 | "\t\t\tindices=( \"\\${backwardIndices[@]}\" )", 49 | "\t\tfi", 50 | "\t\t", 51 | "", 52 | "\t\tfor index in \"\\${indices[@]}\"; do", 53 | "\t\t\ttput rc # restore cursor position", 54 | "\t\t\techo \"\\${frames[\\$index]}\"", 55 | "\t\t\tsleep \"\\${interval}\"", 56 | "\t\tdone", 57 | "\tdone", 58 | "}\n" 59 | ], 60 | "description": "animate frames of animation with interval seconds between frames circular and pendular" 61 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-animation-pacman.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn animation pacman", 3 | "body": [ 4 | "# Usage: pacMan inputString interval pad", 5 | "# Example: pacman \"Hello World\" 0.5 \"*\"", 6 | "function pacMan () {", 7 | "\tlocal string=\"\\${1\\}\"", 8 | "\tlocal interval=\"\\${2\\}\"", 9 | "\t: \"\\${interval:=0.2}\"", 10 | "\tlocal pad=\"\\${3\\}\"", 11 | "\t: \"\\${pad:=.}\"", 12 | "\tlocal length=\\${#string}", 13 | "\tlocal padding=\"\"", 14 | "", 15 | "\t# Comment out next two lines if you are using CTRL+C event handler.", 16 | "${1:\ttrap 'tput cnorm; echo' EXIT\n\ttrap 'exit 127' HUP INT TERM}\n", 17 | "\ttput civis # hide cursor", 18 | "\ttput sc # save cursor position", 19 | "", 20 | "\tfor((i=0;i<=length;i++)); do", 21 | "\t\ttput rc", 22 | "\t\techo \"\\${padding\\}c\\${string:i:length}\"", 23 | "\t\tsleep \"\\$interval\"", 24 | "\t\ttput rc", 25 | "\t\techo \"\\${padding\\}C\\${string:i:length}\"", 26 | "\t\tsleep \"\\${interval\\}\"", 27 | "\t\tpadding+=\"\\${pad\\}\"", 28 | "\tdone", 29 | "", 30 | "\ttput cnorm", 31 | "\ttput rc", 32 | "\techo \"\\${padding\\}\"", 33 | "}\n${0:}" 34 | ], 35 | "description": "pacMan animation (eating input text)" 36 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-banner-color.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn banner color", 3 | "body": [ 4 | "# Usage: bannerColor \"my title\" \"red\" \"*\"", 5 | "function bannerColor() {", 6 | "\tcase \\${2\\} in", 7 | "\t\tblack) color=0", 8 | "\t\t;;", 9 | "\t\tred) color=1", 10 | "\t\t;;", 11 | "\t\tgreen) color=2", 12 | "\t\t;;", 13 | "\t\tyellow) color=3", 14 | "\t\t;;", 15 | "\t\tblue) color=4", 16 | "\t\t;;", 17 | "\t\tmagenta) color=5", 18 | "\t\t;;", 19 | "\t\tcyan) color=6", 20 | "\t\t;;", 21 | "\t\twhite) color=7", 22 | "\t\t;;", 23 | "\t\t*) echo \"color is not set\"; exit 1", 24 | "\t\t;;", 25 | "\tesac\n", 26 | "\tlocal msg=\"\\${3\\} \\${1\\} \\${3\\}\"", 27 | "\tlocal edge", 28 | "\tedge=\\${msg\/\/?\/\\$3\\}", 29 | "\ttput setaf \\${color\\}", 30 | "\ttput bold", 31 | "\techo \"\\${edge}\"", 32 | "\techo \"\\${msg\\}\"", 33 | "\techo \"\\${edge}\"", 34 | "\ttput sgr 0", 35 | "\techo", 36 | "}\n" 37 | ], 38 | "description": "print a color banner." 39 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-banner-simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn banner simple", 3 | "body": [ 4 | "# Usage: bannerSimple \"my title\" \"*\"", 5 | "function bannerSimple() {", 6 | "\tlocal msg=\"\\${2\\} \\${1\\} \\${2\\}\"", 7 | "\tlocal edge", 8 | "\tedge=\\${msg\/\/?\/\\$2\\}", 9 | "\techo \"\\${edge\\}\"", 10 | "\techo \"\\$(tput bold)\\${msg\\}\\$(tput sgr0)\"", 11 | "\techo \"\\${edge\\}\"", 12 | "\techo", 13 | "}\n" 14 | ], 15 | "description": "function: print a banner with provided title and surrounding character" 16 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn import", 3 | "body": [ 4 | "# Usage: import \"mylib\"", 5 | "function import() {", 6 | "\tlocal file=\"./${1:lib}/\\${1\\}.sh\"", 7 | "\tif [ -f \"\\${file\\}\" ]; then", 8 | "\t\tsource \"\\${file\\}\"", 9 | "\telse", 10 | "\t\techo \"Error: Cannot find library at: \\${file\\}\"", 11 | "\t\texit 1", 12 | "\tfi", 13 | "}\n" 14 | ], 15 | "description": "import functions from other shellscript files" 16 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-input-choice.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "fn options", 4 | "fn input choice" 5 | ], 6 | "body": [ 7 | "# Usage: options=(\"one\" \"two\" \"three\"); inputChoice \"Choose:\" 1 \"\\${options[@]}\"; choice=\\$?; echo \"\\${options[\\$choice]}\"", 8 | "function inputChoice() {", 9 | "\techo \"\\${1\\}\"; shift", 10 | "\techo \"\\$(tput dim)\"\"- Change option: [up/down], Select: [ENTER]\" \"\\$(tput sgr0)\"", 11 | "\tlocal selected=\"\\${1\\}\"; shift\n", 12 | "\tESC=\\$(echo -e \"\\033\")", 13 | "\tcursor_blink_on() { tput cnorm; }", 14 | "\tcursor_blink_off() { tput civis; }", 15 | "\tcursor_to() { tput cup \\$((\\$1-1)); }", 16 | "\tprint_option() { echo \"\\$(tput sgr0)\" \"\\$1\" \"\\$(tput sgr0)\"; }", 17 | "\tprint_selected() { echo \"\\$(tput rev)\" \"\\$1\" \"\\$(tput sgr0)\"; }", 18 | "\tget_cursor_row() { IFS=';' read -rsdR -p \\$'\\E[6n' ROW COL; echo \"\\${ROW#*[}\"; }", 19 | "\tkey_input() { read -rs -n3 key 2>/dev/null >&2; [[ \\$key = \\${ESC}[A ]] && echo up; [[ \\$key = \\${ESC}[B ]] && echo down; [[ \\$key = \"\" ]] && echo enter; }\n", 20 | "\tfor opt; do echo; done\n", 21 | "\tlocal lastrow", 22 | "\tlastrow=\\$(get_cursor_row)", 23 | "\tlocal startrow=\\$((lastrow - \\$#))", 24 | "\ttrap \"cursor_blink_on; echo; echo; exit\" 2", 25 | "\tcursor_blink_off\n", 26 | "\t: selected:=0\n", 27 | "\twhile true; do", 28 | "\t\tlocal idx=0", 29 | "\t\tfor opt; do", 30 | "\t\t\tcursor_to \\$((startrow + idx))", 31 | "\t\t\tif [ \\${idx\\} -eq \"\\${selected\\}\" ]; then", 32 | "\t\t\t\tprint_selected \"\\${opt\\}\"", 33 | "\t\t\telse", 34 | "\t\t\t\tprint_option \"\\${opt\\}\"", 35 | "\t\t\tfi", 36 | "\t\t\t((idx++))", 37 | "\t\tdone\n", 38 | "\t\tcase \\$(key_input) in", 39 | "\t\t\tenter) break;;", 40 | "\t\t\tup) ((selected--)); [ \"\\${selected}\" -lt 0 ] && selected=\\$((\\$# - 1));;", 41 | "\t\t\tdown) ((selected++)); [ \"\\${selected}\" -ge \\$# ] && selected=0;;", 42 | "\t\tesac", 43 | "\tdone\n", 44 | "\tcursor_to \"\\${lastrow\\}\"", 45 | "\tcursor_blink_on", 46 | "\techo\n", 47 | "\treturn \"\\${selected\\}\"", 48 | "}\n" 49 | ], 50 | "description": "provides a list of choices to user and returns the index of selected choice" 51 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-input-multi-choice.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "fn checkbox", 4 | "fn input multichoice" 5 | ], 6 | "body": [ 7 | "# Usage: multiChoice \"header message\" resultArray \"comma separated options\" \"comma separated default values\"", 8 | "# Credit: https://serverfault.com/a/949806", 9 | "function multiChoice {", 10 | "\techo \"\\${1\\}\"; shift", 11 | "\techo \"\\$(tput dim)\"\"- Change Option: [up/down], Change Selection: [space], Done: [ENTER]\" \"\\$(tput sgr0)\"", 12 | "\t# little helpers for terminal print control and key input", 13 | "\tESC=\\$( printf \"\\033\")", 14 | "\tcursor_blink_on() { printf \"%s\" \"\\${ESC}[?25h\"; }", 15 | "\tcursor_blink_off() { printf \"%s\" \"\\${ESC}[?25l\"; }", 16 | "\tcursor_to() { printf \"%s\" \"\\${ESC}[\\$1;\\${2:-1}H\"; }", 17 | "\tprint_inactive() { printf \"%s %s \" \"\\$2\" \"\\$1\"; }", 18 | "\tprint_active() { printf \"%s \\${ESC}[7m \\$1 \\${ESC}[27m\" \"\\$2\"; }", 19 | "\tget_cursor_row() { IFS=';' read -rsdR -p \\$'\\E[6n' ROW COL; echo \"\\${ROW#*[}\"; }", 20 | "\tkey_input() {", 21 | "\t\tlocal key", 22 | "\t\tIFS= read -rsn1 key 2>/dev/null >&2", 23 | "\t\tif [[ \\$key = \"\" ]]; then echo enter; fi;", 24 | "\t\tif [[ \\$key = \\$'\\x20' ]]; then echo space; fi;", 25 | "\t\tif [[ \\$key = \\$'\\x1b' ]]; then", 26 | "\t\t\tread -rsn2 key", 27 | "\t\t\tif [[ \\$key = [A ]]; then echo up; fi;", 28 | "\t\t\tif [[ \\$key = [B ]]; then echo down; fi;", 29 | "\t\tfi", 30 | "\t}", 31 | "\ttoggle_option() {", 32 | "\t\tlocal arr_name=\\$1", 33 | "\t\teval \"local arr=(\\\"\\\\${\\${arr_name}[@]}\\\")\"", 34 | "\t\tlocal option=\\$2", 35 | "\t\tif [[ \\${arr[option]} == 1 ]]; then", 36 | "\t\t\tarr[option]=0", 37 | "\t\telse", 38 | "\t\t\tarr[option]=1", 39 | "\t\tfi", 40 | "\t\teval \"\\$arr_name\"='(\"\\${arr[@]}\")'", 41 | "\t}", 42 | "", 43 | "\tlocal retval=\\$1", 44 | "\tlocal options", 45 | "\tlocal defaults", 46 | "", 47 | "\tIFS=';' read -r -a options <<< \"\\$2\"", 48 | "\tif [[ -z \\$3 ]]; then", 49 | "\t\tdefaults=()", 50 | "\telse", 51 | "\t\tIFS=';' read -r -a defaults <<< \"\\$3\"", 52 | "\tfi", 53 | "", 54 | "\tlocal selected=()", 55 | "", 56 | "\tfor ((i=0; i<\\${#options[@]}; i++)); do", 57 | "\t\tselected+=(\"\\${defaults[i]}\")", 58 | "\t\tprintf \"\\n\"", 59 | "\tdone", 60 | "", 61 | "\t# determine current screen position for overwriting the options", 62 | "\tlocal lastrow", 63 | "\tlastrow=\\$(get_cursor_row)", 64 | "\tlocal startrow=\\$((lastrow - \\${#options[@]}))", 65 | "", 66 | "\t# ensure cursor and input echoing back on upon a ctrl+c during read -s", 67 | "\ttrap \"cursor_blink_on; stty echo; printf '\\n'; exit\" 2", 68 | "\tcursor_blink_off", 69 | "", 70 | "\tlocal active=0", 71 | "\twhile true; do", 72 | "\t\t# print options by overwriting the last lines", 73 | "\t\tlocal idx=0", 74 | "\t\tfor option in \"\\${options[@]}\"; do", 75 | "\t\t\tlocal prefix=\"[ ]\"", 76 | "\t\t\tif [[ \\${selected[idx]} == 1 ]]; then", 77 | "\t\t\t\tprefix=\"[x]\"", 78 | "\t\t\tfi", 79 | "", 80 | "\t\t\tcursor_to \\$((startrow + idx))", 81 | "\t\t\tif [ \\$idx -eq \\$active ]; then", 82 | "\t\t\t\tprint_active \"\\$option\" \"\\$prefix\"", 83 | "\t\t\telse", 84 | "\t\t\t\tprint_inactive \"\\$option\" \"\\$prefix\"", 85 | "\t\t\tfi", 86 | "\t\t\t((idx++))", 87 | "\t\tdone", 88 | "", 89 | "\t\t# user key control", 90 | "\t\tcase \\$(key_input) in", 91 | "\t\t\tspace) toggle_option selected \\$active;;", 92 | "\t\t\tenter) break;;", 93 | "\t\t\tup) ((active--));", 94 | "\t\t\t\tif [ \\$active -lt 0 ]; then active=\\$((\\${#options[@]} - 1)); fi;;", 95 | "\t\t\tdown) ((active++));", 96 | "\t\t\t\tif [ \"\\$active\" -ge \\${#options[@]} ]; then active=0; fi;;", 97 | "\t\tesac", 98 | "\tdone", 99 | "", 100 | "\t# cursor position back to normal", 101 | "\tcursor_to \"\\$lastrow\"", 102 | "\tprintf \"\\n\"", 103 | "\tcursor_blink_on", 104 | "", 105 | "\tindices=()", 106 | "\tfor((i=0;i<\\${#selected[@]};i++)); do", 107 | "\t\tif ((selected[i] == 1)); then", 108 | "\t\t\tindices+=(\"\\${i}\")", 109 | "\t\tfi", 110 | "\tdone", 111 | "", 112 | "\t# eval \\$retval='(\"\\${selected[@]}\")'", 113 | "\teval \"\\$retval\"='(\"\\${indices[@]}\")'", 114 | "}\n" 115 | ], 116 | "description": "provides a list of choices to user and returns the index of selected choices" 117 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-math-average.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn math average", 3 | "body": [ 4 | "# Usage: average int1 int2 ...", 5 | "function average () {", 6 | "\tlocal sum=0", 7 | "\tfor int in \"\\$@\"; do", 8 | "\t\t((sum += int))", 9 | "\tdone", 10 | "\techo \\$((sum / \\$#))", 11 | "}\n" 12 | ], 13 | "description": "calculate average of given integers" 14 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-math-factorial.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn math factorial", 3 | "body": [ 4 | "# Usage: factorial n", 5 | "factorial ()", 6 | "{", 7 | "\tif (( \\$1 < 2 )); then", 8 | "\t\techo 1", 9 | "\telse", 10 | "\t\techo \\$(( \\$1 * \\$(factorial \\$(( \\$1 - 1 ))) ))", 11 | "\tfi", 12 | "}\n" 13 | ], 14 | "description": "calculate n!" 15 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-math-fibonacci-series.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn math fibonacci series", 3 | "body": [ 4 | "# Usage: fibonacciSeries n", 5 | "fibonacciSeries ()", 6 | "{", 7 | "\tfib=()", 8 | "\tfib+=(0)", 9 | "\tfib+=(1)", 10 | "", 11 | "\tfor((i=2;i<\\${1};i++)); do", 12 | "\t\tfib[i]=\\$((fib[i-1] + fib[i-2]))", 13 | "\tdone", 14 | "", 15 | "\techo \"\\${fib[@]}\"", 16 | "}\n" 17 | ], 18 | "description": "array of fibonacci series" 19 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-math-fibonacci.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn math fibonacci", 3 | "body": [ 4 | "# Usage: fibonacci n", 5 | "fibonacci ()", 6 | "{", 7 | "\tif ((\\$1 < 2)); then ", 8 | "\t\techo \"\\$1\"", 9 | "\telse", 10 | "\t\techo \\$((\\$(fibonacci \\$((\\$1 - 1))) + \\$(fibonacci \\$((\\$1 - 2)))))", 11 | "\tfi", 12 | "}\n" 13 | ], 14 | "description": "calculate Nth fibonacci number" 15 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-math-product.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn math product", 3 | "body": [ 4 | "# Usage: product int1 int2 ...", 5 | "function product () {", 6 | "\tlocal result=1", 7 | "\tfor int in \"$@\"; do", 8 | "\t\t((result *= int))", 9 | "\tdone", 10 | "\techo \"\\${result\\}\"", 11 | "}\n" 12 | ], 13 | "description": "calculate product of given integers" 14 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-math-sum.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn math sum", 3 | "body": [ 4 | "# Usage: sum int1 int2 ...", 5 | "function sum () {", 6 | "\tlocal result=0", 7 | "\tfor int in \"\\$@\"; do", 8 | "\t\t((result += int))", 9 | "\tdone", 10 | "\techo \"\\${result\\}\"", 11 | "}\n" 12 | ], 13 | "description": "calculate sum of given integers" 14 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-progress.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn progress", 3 | "body": [ 4 | "# Usage: progressBar \"message\" currentStep totalSteps", 5 | "function progressBar() {", 6 | "\tlocal bar='████████████████████'", 7 | "\tlocal space='....................'", 8 | "\tlocal wheel=('\\' '|' '/' '-')", 9 | "", 10 | "\tlocal msg=\"\\${1\\}\"", 11 | "\tlocal current=\\${2\\}", 12 | "\tlocal total=\\${3\\}", 13 | "\tlocal wheelIndex=\\$((current % 4))", 14 | "\tlocal position=\\$((100 * current / total))", 15 | "\tlocal barPosition=\\$((position / 5))", 16 | "", 17 | "\techo -ne \"\\r|\\${bar:0:\\$barPosition}\\$(tput dim)\\${space:\\$barPosition:20}\\$(tput sgr0)| \\${wheel[wheelIndex]} \\${position}% [ \\${msg} ] \"", 18 | "}\n" 19 | ], 20 | "description": "progress bar function" 21 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-scan.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn scan local", 3 | "body": [ 4 | "# Usage: scan proto host fromPort toPort", 5 | "function scan () {", 6 | "\tlocal openPortsArray=()", 7 | "\tfor ((port=\\${3\\}; port<=\\${4\\}; port++)); do", 8 | "\t\t(echo >/dev/\"\\${1\\}\"/\"\\${2\\}\"/\"\\${port\\}\") >/dev/null 2>&1 && openPortsArray+=(\"\\${port}\")", 9 | "\tdone", 10 | "\techo \"\\${openPortsArray[@]}\"", 11 | "}\n" 12 | ], 13 | "description": "scan localhost's port range (tcp/udp)" 14 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-time-format-seconds.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn time format seconds", 3 | "body": [ 4 | "# Usage: formatSeconds 70 -> 1m 10s", 5 | "# Credit: https://unix.stackexchange.com/a/27014", 6 | "function formatSeconds {", 7 | "\tlocal T=\\$1", 8 | "\tlocal D=\\$((T/60/60/24))", 9 | "\tlocal H=\\$((T/60/60%24))", 10 | "\tlocal M=\\$((T/60%60))", 11 | "\tlocal S=\\$((T%60))", 12 | "\tlocal result=\"\"", 13 | "", 14 | "\t(( D > 0 )) && result=\"\\${D}d \"", 15 | "\t(( H > 0 )) && result=\"\\${result}\\${H}h \"", 16 | "\t(( M > 0 )) && result=\"\\${result}\\${M}m \"", 17 | "\t(( S > 0 )) && result=\"\\${result}\\${S}s \"", 18 | "\techo -e \"\\${result}\" | sed -e 's/[[:space:]]*\\$//'", 19 | "}\n" 20 | ], 21 | "description": "format seconds into days/hours/minutes/seconds" 22 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-urldecode.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": ["fn urldecode"], 3 | "body": [ 4 | "function urldecode() {", 5 | ": \"${*\/\/+\/ }\"; echo -e \"${_\/\/%\/\\\\x}\";", 6 | "}\n" 7 | ], 8 | "description": "decodes encoded URL" 9 | } 10 | -------------------------------------------------------------------------------- /nsroot/fn-fx/fn-urlencode.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fn urlencode", 3 | "body": [ 4 | "# Usage: urlencode url", 5 | "function urlencode () {", 6 | "\tlocal LC_ALL=C", 7 | "\tlocal c i n=\\${#1}", 8 | "\tfor (( i=0; i v2Sub )); then", 29 | "\t\techo \">\"", 30 | "\telif (( v1Sub < v2Sub )); then", 31 | "\t\techo \"<\"", 32 | "\telse", 33 | "\t\tversionCompare \"\\$(cutDot \"\\$v1Sub\" \"\\$v1\")\" \"\\$(cutDot \"\\$v2Sub\" \"\\$v2\")\"", 34 | "\tfi", 35 | "}\n" 36 | ], 37 | "description": "function: compares two semvers and returns >, < or =" 38 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-animate-animate.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx animation animate", 3 | "body": [ 4 | "# Usage: animate framesArray interval", 5 | "animate \"\\${${1:frames}[@]}\" ${2|0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1|} ${3|circular,pendular|}\n" 6 | ], 7 | "description": "call animate function to start animation" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-animate.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx animation animate", 3 | "body": [ 4 | "# Usage: animate framesArray interval", 5 | "animate \"\\${${1:frames}[@]}\" ${2|0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1|} ${3|circular,pendular|}\n" 6 | ], 7 | "description": "call animate function to start animation" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-animation-pacman.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx animation pacman", 3 | "body": [ 4 | "# Usage: pacMan inputString interval pad", 5 | "pacMan \"${1:Hello World}\" ${2|0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1|} \"${3|.,*,+,-,~,x,o|}\"\n" 6 | ], 7 | "description": "call pacMan animation (eating input text) function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-banner-color.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx banner color", 3 | "body": [ 4 | "# Usage: bannerColor \"my title\" \"red\" \"*\"", 5 | "bannerColor \"${1:my title}\" \"${2|black,red,green,yellow,blue,magenta,cyan,white|}\" \"${3|*,:,+,.,x,o,$|}\"\n" 6 | ], 7 | "description": "call bannerColor function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-banner-simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx banner simple", 3 | "body": [ 4 | "# Usage: bannerSimple \"my title\" \"*\"", 5 | "bannerSimple \"${1:my title}\" \"${2|*,:,+,.,x,o,$|}\"\n" 6 | ], 7 | "description": "call bannerSimple function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx import", 3 | "body": [ 4 | "# Usage: import \"filename\"", 5 | "import \"${1:libname}\"\n" 6 | ], 7 | "description": "import functions from other shellscript files located in a directory (default: lib) relative to current file" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-input-choice.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "fx options", 4 | "fx input choice" 5 | ], 6 | "body": [ 7 | "# Usage: options=(\"one\" \"two\" \"three\"); inputChoice \"Choose:\" 1 \"\\${options[@]}\"; choice=\\$?; echo \"\\${options[\\$choice]}\"", 8 | "${1:options}=(${2:\"one\" \"two\" \"three\"})", 9 | "inputChoice \"${3:Choose:}\" ${4|0,1,2,3,4,5,6,7,8,9|} \"\\${${1}[@]}\"; choice=\\$?", 10 | "echo \"\\${${1}[\\$choice]}\" selected\n" 11 | ], 12 | "description": "call input choice function" 13 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-input-multi-choice.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "fx checkbox", 4 | "fx input multichoice" 5 | ], 6 | "body": [ 7 | "# Usage: multiChoice \"header message\" resultArray \"comma separated options\" \"comma separated default values\"", 8 | "multiChoice \"${1:Select options:}\" ${2:result} \"One 1;Two 2;Three 3\" \"1;0;1\"", 9 | "echo \"${${2}[@]}\"\n" 10 | ], 11 | "description": "call input multichoice function" 12 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-math fibonacci.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx math fibonacci", 3 | "body": [ 4 | "# Usage: fibonacci n", 5 | "result=\\$(fibonacci ${1:n})\n" 6 | ], 7 | "description": "calculate Nth fibonacci number" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-math-average.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx math average", 3 | "body": [ 4 | "# Usage: average int1 int2 ...", 5 | "result=\\$(average \"\\${${1:int1}\\}\" \"\\${${2:int2}\\}\" \"\\${${3:int3}\\}\")\n" 6 | ], 7 | "description": "call math average function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-math-factorial.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx math factorial", 3 | "body": [ 4 | "# Usage: factorial n", 5 | "readarray -t result < <(factorial ${1:n})", 6 | "echo \"\\${result[@]\\}\"\n" 7 | ], 8 | "description": "calculate n!" 9 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-math-fibonacci-series.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx math fibonacci series", 3 | "body": [ 4 | "# Usage: fibonacciSeries n", 5 | "readarray -t ${1:result} < <(fibonacciSeries ${2:n})", 6 | "echo \"\\${${1}[@]}\"\n" 7 | ], 8 | "description": "array of fibonacci series" 9 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-math-product.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx math product", 3 | "body": [ 4 | "# Usage: product int1 int2 ...", 5 | "result=\\$(product \\${${1:int1}\\} \\${${2:int2}\\} \\${${3:int3}\\})\n" 6 | ], 7 | "description": "call math product function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-math-sum.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx math sum", 3 | "body": [ 4 | "# Usage: sum int1 int2 ...", 5 | "result=\\$(sum \\${${1:int1}\\} \\${${2:int2}\\} \\${${3:int3}\\})\n" 6 | ], 7 | "description": "call math sum function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-progress.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx progress", 3 | "body": [ 4 | "# Usage: progressBar \"message\" currentStep totalSteps", 5 | "${1:totalSteps}=${2:100}", 6 | "", 7 | "for ((${3:currentStep}=${4:1}; ${3} <= ${1}; ${3}++)); do", 8 | "\tsleep 0.1 # simulating one step of job", 9 | "\tprogressBar \"Installing foo...\" \"\\${${3}\\}\" \"\\${${1}\\}\"", 10 | "done\n", 11 | "echo\n" 12 | ], 13 | "description": "call progress bar function" 14 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-scan.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx scan local", 3 | "body": [ 4 | "# Usage: scan proto host fromPort toPort", 5 | "readarray -t openPorts < <(scan ${1|tcp,udp|} \"${2|localhost,127.0.0.1,::1|}\" ${3:fromPort} ${4:toPort})", 6 | "${5:echo \"\\${openPorts[@]\\}\"}\n" 7 | ], 8 | "description": "call scan function to scan localhost over a port range" 9 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-time-format-seconds.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx time format seconds", 3 | "body": [ 4 | "# Usage: formatSeconds 70 -> 1m 10s", 5 | "readarray -t ${1:result} < <(formatSeconds ${2:seconds})", 6 | "echo \"${${1}[@]}\"\n" 7 | ], 8 | "description": "call formatSeconds function" 9 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-urldecode.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx urldecode", 3 | "body": [ 4 | "# Usage: urldecode url", 5 | "urldecode \"${1|encodedUrl,${encodedUrl}|}\"\n" 6 | ], 7 | "description": "call urldecode function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-urlencode.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "fx urlencode", 3 | "body": [ 4 | "# Usage: urlencode url", 5 | "urlencode \"${1|url,${url}|}\"\n" 6 | ], 7 | "description": "call urlencode function" 8 | } -------------------------------------------------------------------------------- /nsroot/fn-fx/fx-version-compare.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "fx version compare", 4 | "fx semver compare" 5 | ], 6 | "body": [ 7 | "# Usage: versionCompare \"1.2.3\" \"1.1.7\"", 8 | "versionCompare \"${1:major}.${2:minor}.${3:patch}\" \"${4:major}.${5:minor}.${6:patch}\"\n" 9 | ], 10 | "description": "call versionCompare function" 11 | } -------------------------------------------------------------------------------- /nsroot/ftp/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ftp delete file", 3 | "body": "curl ftp://${1:user}:${2:password}@${3:ipOrDomain}/${4:path}/${5:fileToDelete} -Q \"DELE ${5:fileToDelete}\"\n", 4 | "description": "delete specified file from ftp server" 5 | } -------------------------------------------------------------------------------- /nsroot/ftp/download.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ftp download", 3 | "body": "curl ftp://${1:user}:${2:password}@${3:ipOrDomain}/${4:filePathOnServer}/\n", 4 | "description": "download specified file from ftp server" 5 | } -------------------------------------------------------------------------------- /nsroot/ftp/list-of-files.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ftp list", 3 | "body": "curl ftp://${1:user}:${2:password}@${3:ipOrDomain}/${4:directoryPathOnServer}/\n", 4 | "description": "get the list of files on the ftp server at specific path" 5 | } -------------------------------------------------------------------------------- /nsroot/ftp/rename.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ftp rename", 3 | "body": "curl ftp://${1:user}:${2:password}@${3:ipOrDomain}/${4:path}/ -Q \"-RNFR ${4:path}/${5:renameFrom}\" -Q \"-RNTO ${4:path}/${6:renameTo}\"\n", 4 | "description": "rename specified file/directory on ftp server" 5 | } -------------------------------------------------------------------------------- /nsroot/ftp/upload.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ftp upload", 3 | "body": "curl -T ${1:fileToUpload} ftp://${2:user}:${3:password}@${4:ipOrDomain}/${5:directoryPathOnServer}/\n", 4 | "description": "upload specified file to ftp server" 5 | } -------------------------------------------------------------------------------- /nsroot/function/arguments-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "function arguments", 4 | "func args" 5 | ], 6 | "body": "echo \"$@\"", 7 | "description": "function all arguments array" 8 | } -------------------------------------------------------------------------------- /nsroot/function/arguments-count.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "function arguments count", 4 | "func args count" 5 | ], 6 | "body": "echo $#", 7 | "description": "number of function arguments" 8 | } -------------------------------------------------------------------------------- /nsroot/function/declare.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "function", 4 | "func" 5 | ], 6 | "body": [ 7 | "function ${1:name} () {", 8 | "\t${2:echo \"\\$1\" # arguments are accessible through \\$1, \\$2,...}", 9 | "}\n" 10 | ], 11 | "description": "function" 12 | } -------------------------------------------------------------------------------- /nsroot/function/return-value.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "function return value", 4 | "func return value", 5 | "func ret val" 6 | ], 7 | "body": "echo \"$?\"", 8 | "description": "last function/command return code" 9 | } -------------------------------------------------------------------------------- /nsroot/git/begin.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "prefix": ["git begin", "git start"], 4 | "body": ["git config ${1|--local,--global,--system|} user.name \"${2:username}\"", 5 | "git config ${3|--local,--global,--system|} user.email ${4:useremail}\n"], 6 | "description": "Initialize git configurations" 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /nsroot/git/branch-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git branch create", 3 | "body": "git checkout -b ${1:branchName}\n", 4 | "description": "create branch locally and switch into it." 5 | } -------------------------------------------------------------------------------- /nsroot/git/branch-delete-local.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git branch delete local", 3 | "body": "git branch --delete ${1:localBranch}\n", 4 | "description": "delete local branch." 5 | } -------------------------------------------------------------------------------- /nsroot/git/branch-delete-remote.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git branch delete remote", 3 | "body": "git push origin --delete ${1:remoteBranch}\n", 4 | "description": "delete remote branch." 5 | } -------------------------------------------------------------------------------- /nsroot/git/branch-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git branch list", 3 | "body": "git branch\n", 4 | "description": "list all branches." 5 | } -------------------------------------------------------------------------------- /nsroot/git/branch-push.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git branch push", 3 | "body": "git push origin ${1:branchName}\n", 4 | "description": "push branch to remote." 5 | } -------------------------------------------------------------------------------- /nsroot/git/branch-rename.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git branch rename", 3 | "body": "git branch -m ${1:newName}\n", 4 | "description": "rename current branch." 5 | } -------------------------------------------------------------------------------- /nsroot/git/changes-revert.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git changes revert", 3 | "body": "git checkout .\n", 4 | "description": "revert tracked changes" 5 | } -------------------------------------------------------------------------------- /nsroot/git/clone-branch-https.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git clone branch https", 3 | "body": "git clone -b ${1:branchName} git@${2:github.com}:${3:user}/${4:repository}.git\n", 4 | "description": "clone a remote branch to local machine over HTTPS." 5 | } -------------------------------------------------------------------------------- /nsroot/git/clone-branch.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git clone branch", 3 | "body": "git clone -b ${1:branchName} git@${2:github.com}:${3:user}/${4:repository}.git\n", 4 | "description": "clone a remote branch to local machine over SSH." 5 | } -------------------------------------------------------------------------------- /nsroot/git/clone-https.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git clone https", 3 | "body": "git clone https://${1:github.com}/${2:user}/${3:repository}.git\n", 4 | "description": "clone remote repository to local machine over HTTPS." 5 | } -------------------------------------------------------------------------------- /nsroot/git/clone.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git clone", 3 | "body": "git clone git@${1:github.com}:${2:user}/${3:repository}.git\n", 4 | "description": "clone remote repository to local machine over SSH." 5 | } -------------------------------------------------------------------------------- /nsroot/git/commit-list-notpushed.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git commit list notPushed", 3 | "body": "git log origin/master..HEAD\n", 4 | "description": "list non pushed commits." 5 | } -------------------------------------------------------------------------------- /nsroot/git/commit-search.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git commit search", 3 | "body": "git log --all --grep='${1:searchCriteria}'\n", 4 | "description": "search for a commit which contains searchCriteria." 5 | } -------------------------------------------------------------------------------- /nsroot/git/commit-undo.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git commit undo", 3 | "body": "git reset --${1|soft,hard|} HEAD~${2|1,2,3,4,5,6,7,8,9|}\n", 4 | "description": "undo last N commits (soft: preserve local changes, hard: delete local changes" 5 | } -------------------------------------------------------------------------------- /nsroot/git/commit.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git commit", 3 | "body": "git commit -m \"${1:commit message}\"\n", 4 | "description": "commit changes." 5 | } -------------------------------------------------------------------------------- /nsroot/git/config-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git config list", 3 | "body": "git config --list\n", 4 | "description": "list git configurations." 5 | } -------------------------------------------------------------------------------- /nsroot/git/config-set.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git config set", 3 | "body": "git config --${1|local,global|} ${2|user.name,user.email|} \"${3:value}\"\n", 4 | "description": "configure git." 5 | } -------------------------------------------------------------------------------- /nsroot/git/patch-apply.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git patch apply", 3 | "body": "git apply < \"${1|/path/to/patch1.patch,${patchPath}|}\"\n", 4 | "description": "apply a patch from file" 5 | } -------------------------------------------------------------------------------- /nsroot/git/patch-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git patch create", 3 | "body": "git diff > \"${1|/path/to/patch1.patch,${patchPath}|}\"\n", 4 | "description": "create a patch from changes" 5 | } -------------------------------------------------------------------------------- /nsroot/git/remote-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git remote list", 3 | "body": "git remote\n", 4 | "description": "list all remotes" 5 | } -------------------------------------------------------------------------------- /nsroot/git/remote-url-add-https.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": ["git remote urlAdd https", "git remote url add https"], 3 | "body": "git remote add origin https://${1:github.com}/${2:user}/${3:repository}.git\n", 4 | "description": "add remote url using HTTPS" 5 | } -------------------------------------------------------------------------------- /nsroot/git/remote-url-add.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "git remote urlAdd", 4 | "git remote url add", 5 | "git remote urlAdd ssh", 6 | "git remote url add ssh" 7 | ], 8 | "body": "git remote add origin git@${1:github.com}:${2:user}/${3:repository}.git\n", 9 | "description": "add remote url using SSH" 10 | } -------------------------------------------------------------------------------- /nsroot/git/remote-url-change-https.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": ["git remote urlChange https", "git remote url change https"], 3 | "body": "git remote set-url origin https://${1:github.com}/${2:user}/${3:repository}.git\n", 4 | "description": "change remote url using HTTPS" 5 | } -------------------------------------------------------------------------------- /nsroot/git/remote-url-change.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "git remote urlChange", 4 | "git remote url change", 5 | "git remote urlChange ssh", 6 | "git remote url change ssh" 7 | ], 8 | "body": "git remote set-url origin git@${1:github.com}:${2:user}/${3:repository}.git\n", 9 | "description": "change remote url using SSH" 10 | } -------------------------------------------------------------------------------- /nsroot/git/tag-commit.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "git tag commit", 4 | "git commit tag" 5 | ], 6 | "body": "git tag -a ${1:release/1.0.0} -m \"${2:1.0.0 release}\"\n", 7 | "description": "tag a commit" 8 | } -------------------------------------------------------------------------------- /nsroot/git/tag-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git tag list", 3 | "body": "git tag\n", 4 | "description": "list all tags" 5 | } -------------------------------------------------------------------------------- /nsroot/git/tag-remote-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git tag remote delete", 3 | "body": "git push --delete origin ${1:tagName} && git push origin :${1:tagName}\n", 4 | "description": "delete tag from remote" 5 | } -------------------------------------------------------------------------------- /nsroot/git/tag-remote-push.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "git tag remote push", 3 | "body": "git push origin ${1:tagName}\n", 4 | "description": "push tag to remote" 5 | } -------------------------------------------------------------------------------- /nsroot/http/cookie.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "http cookie", 3 | "body": [ 4 | "curl --request ${1|GET,POST,DELETE,PUT|} -sL \\", 5 | "\t--user-agent '${2|Shellman,Your custom user agent|}' \\", 6 | "\t--cookie ${3|'key=value',\"${key}=${value}\"|} \\", 7 | "\t--url ${4|'http://example.com',\"${url}\"|}\n" 8 | ], 9 | "description": "send http request with cookies, using curl" 10 | } -------------------------------------------------------------------------------- /nsroot/http/download.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "http download", 3 | "body": [ 4 | "curl --request ${1|GET,POST|} -sL \\", 5 | "\t--user-agent '${2|Shellman,Your custom user agent|}' \\", 6 | "\t--output ${3|'/path/to/file',\"${pathToFile}\"|} \\", 7 | "\t--url ${4|'http://example.com/file.zip',\"${downloadUrl}\"|}\n" 8 | ], 9 | "description": "download from url and save to /path/to/file, using curl" 10 | } -------------------------------------------------------------------------------- /nsroot/http/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "http GET", 4 | "http DELETE" 5 | ], 6 | "body": [ 7 | "curl --request ${1|GET,DELETE|} -sL \\", 8 | "\t--user-agent '${2|Shellman,Your custom user agent|}' \\", 9 | "\t--url ${3|'http://example.com',\"${url}\"|}\n" 10 | ], 11 | "description": "send http GET/DELETE request using curl" 12 | } -------------------------------------------------------------------------------- /nsroot/http/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "http header", 3 | "body": [ 4 | "curl --request ${1|GET,POST,DELETE,PUT|} -sL \\", 5 | "\t--user-agent '${2|Shellman,Your custom user agent|}' \\", 6 | "\t--header ${3|'key: value',\"${key}\": \"${value}\"|} \\", 7 | "\t--url ${4|'http://example.com',\"${url}\"|}\n" 8 | ], 9 | "description": "send http request with custom header, using curl" 10 | } -------------------------------------------------------------------------------- /nsroot/http/post-send-file.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "http POST file", 3 | "body": [ 4 | "curl --request POST -sL \\", 5 | "\t--user-agent '${1|Shellman,Your custom user agent|}' \\", 6 | "\t--url ${2|'http://example.com',\"${url}\"|} \\", 7 | "\t--form ${3|'key=value',\"${key}\"=\"${value}\"|} \\", 8 | "\t--form ${4|'file=@/path/to/file',file=@\"${pathToFile}\"|}\n" 9 | ], 10 | "description": "send file with http POST, using curl" 11 | } -------------------------------------------------------------------------------- /nsroot/http/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "http POST", 4 | "http PUT" 5 | ], 6 | "body": [ 7 | "curl --request ${1|POST,PUT|} -sL \\", 8 | "\t--user-agent '${2|Shellman,Your custom user agent|}' \\", 9 | "\t--url ${3|'http://example.com',\"${url}\"|} \\", 10 | "\t--data ${4|'key=value',\"${key}\"=\"${value}\"|} \n" 11 | ], 12 | "description": "send data with http POST/PUT, using curl" 13 | } -------------------------------------------------------------------------------- /nsroot/input/password.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "input password", 3 | "body": [ 4 | "echo \"${1:Please enter your password: }\"", 5 | "read -rs ${2:password}", 6 | "${3:echo \"\\${password\\}\"}\n" 7 | ], 8 | "description": "get text as input from user without showing characters" 9 | } -------------------------------------------------------------------------------- /nsroot/input/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "input text", 4 | "ask question" 5 | ], 6 | "body": [ 7 | "read -rep \"${1:Question here? }\" -i \"${2:Default answer}\" answer", 8 | "${3:echo \"\\${answer\\}\"}\n" 9 | ], 10 | "description": "get text as input from user" 11 | } -------------------------------------------------------------------------------- /nsroot/integer/if-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if int =", 3 | "body": [ 4 | "if (( ${1:int1} == ${2:int2} )); then", 5 | "\t${3:echo \"equal\"}", 6 | "fi\n" 7 | ], 8 | "description": "if integers are equal" 9 | } -------------------------------------------------------------------------------- /nsroot/integer/if-greater-or-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if int >=", 3 | "body": [ 4 | "if (( ${1:int1} >= ${2:int2} )); then", 5 | "\t${3:echo \"greater or equal\"}", 6 | "fi\n" 7 | ], 8 | "description": "if int1 is greater or equal to int2" 9 | } -------------------------------------------------------------------------------- /nsroot/integer/if-greater.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if int >", 3 | "body": [ 4 | "if (( ${1:int1} > ${2:int2} )); then", 5 | "\t${3:echo \"greater\"}", 6 | "fi\n" 7 | ], 8 | "description": "if int1 is greater than int2" 9 | } -------------------------------------------------------------------------------- /nsroot/integer/if-lesser-or-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if int <=", 3 | "body": [ 4 | "if (( ${1:int1} <= ${2:int2} )); then", 5 | "\t${3:echo \"lesser or equal\"}", 6 | "fi\n" 7 | ], 8 | "description": "if int1 is lesser or equal to int2" 9 | } -------------------------------------------------------------------------------- /nsroot/integer/if-lesser.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if int <", 3 | "body": [ 4 | "if (( ${1:int1} < ${2:int2} )); then", 5 | "\t${3:echo \"lesser\"}", 6 | "fi\n" 7 | ], 8 | "description": "if int1 is lesser than int2" 9 | } -------------------------------------------------------------------------------- /nsroot/integer/if-not-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if int !=", 3 | "body": [ 4 | "if (( ${1:int1} != ${2:int2} )); then", 5 | "\t${3:echo \"not equal\"}", 6 | "fi\n" 7 | ], 8 | "description": "if integers are not equal" 9 | } -------------------------------------------------------------------------------- /nsroot/internal/for-i-j.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "for ij", 3 | "body": [ 4 | "for((i=0;i<${1:n};i++)); do", 5 | "\tfor((j=0;j<${2:m};j++)); do", 6 | "\t\t${3:echo \"\\${i\\}, \\${j\\}\"}", 7 | "\tdone", 8 | "done\n" 9 | ], 10 | "description": "for loop by index" 11 | } -------------------------------------------------------------------------------- /nsroot/internal/for-i.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "for i", 3 | "body": [ 4 | "for((i=0;i<${1:n};i++)); do", 5 | "\t${2:echo \"\\${i\\}\"}", 6 | "done\n" 7 | ], 8 | "description": "for loop by index" 9 | } -------------------------------------------------------------------------------- /nsroot/internal/for-in-collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "for in collection", 3 | "body": [ 4 | "for ${1:item} in {${2:\"Hello World!\",a,bc,1372}}; do", 5 | "\techo \"\\${${1:item}\\}\"", 6 | "done\n" 7 | ], 8 | "description": "for loop in collection" 9 | } -------------------------------------------------------------------------------- /nsroot/internal/for-in-column.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "for in column", 3 | "body": [ 4 | "for ${1:col} in \\$(${2:docker images} | awk '{ print ${3:\\$1\":\"\\$2} }'); do", 5 | "\t${4:echo \"\\${${1:col}\\}\" | cut -d ':' -f 1}", 6 | "\t${5:echo \"\\${${1:col}\\}\" | cut -d ':' -f 2}", 7 | "done\n" 8 | ], 9 | "description": "for loop in collection" 10 | } -------------------------------------------------------------------------------- /nsroot/internal/for-in-range.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "for in range", 3 | "body": [ 4 | "for ${1:item} in ${2|{a..z},{0..20}|}; do", 5 | "\techo \"\\${${1:item}\\}\"", 6 | "done\n" 7 | ], 8 | "description": "for loop in collection" 9 | } -------------------------------------------------------------------------------- /nsroot/internal/if-elif-else.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if", 3 | "body": [ 4 | "if [ ${1:condition} ]; then", 5 | "\t${2: # if body}", 6 | "elif [ ${3:condition} ]; then", 7 | "\t${4: # else if body}", 8 | "else", 9 | "\t${5: # else body}", 10 | "fi\n" 11 | ], 12 | "description": "if" 13 | } -------------------------------------------------------------------------------- /nsroot/internal/iff-not.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "iff not", 3 | "body": "[ ${1:condition} ] || ${2:command}\n", 4 | "description": "if condition is false then run command (short circuit)" 5 | } -------------------------------------------------------------------------------- /nsroot/internal/iff.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "iff", 3 | "body": "[ ${1:condition} ] && ${2:command}\n", 4 | "description": "if condition is true then run command (short circuit)" 5 | } -------------------------------------------------------------------------------- /nsroot/internal/infinite-loop.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "loop infinite", 3 | "body": [ 4 | "while true; do", 5 | "${1:\techo \"infinite loop\"\n\tsleep 1s}", 6 | "done\n" 7 | ], 8 | "description": "infinite loop" 9 | } -------------------------------------------------------------------------------- /nsroot/internal/switch.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "switch case", 3 | "body": [ 4 | "case \"\\${${1:item}\\}\" in", 5 | "\t${2:1})", 6 | "\t\t${3:echo \"item = 1\"}", 7 | "\t;;", 8 | "\t${4:2|3})", 9 | "\t\t${5:echo \"item = 2 or item = 3\"}", 10 | "\t;;", 11 | "\t*)", 12 | "\t\t${6:echo \"default (none of above)\"}", 13 | "\t;;", 14 | "esac\n" 15 | ], 16 | "description": "switch case" 17 | } -------------------------------------------------------------------------------- /nsroot/internal/until.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "loop until", 3 | "body": [ 4 | "until [ ${1:condition} ]; do", 5 | "\t${2:# body}", 6 | "done\n" 7 | ], 8 | "description": "until loop" 9 | } -------------------------------------------------------------------------------- /nsroot/internal/while.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "loop while", 3 | "body": [ 4 | "while [ ${1:condition} ]; do", 5 | "\t${2:# body}", 6 | "done\n" 7 | ], 8 | "description": "while loop" 9 | } -------------------------------------------------------------------------------- /nsroot/ip/array-of-local.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ip local IPs", 3 | "body": "${1:IPs}=\\$(hostname)\n", 4 | "description": "array of local IPs" 5 | } -------------------------------------------------------------------------------- /nsroot/ip/info.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ip info", 3 | "body": "${1:info}=\\$(curl -s ipinfo.io/${2|ip,city,region,country,loc,postal,org|})\n", 4 | "description": "public ip information" 5 | } -------------------------------------------------------------------------------- /nsroot/ip/public-address.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "ip public", 3 | "body": "${1:publicIp}=\\$(curl -s ${2|bot.whatismyipaddress.com,ident.me,ipecho.net/plain,icanhazip.com,ifconfig.me,api.ipify.org,ipinfo.io/ip|})\n", 4 | "description": "public ip address" 5 | } -------------------------------------------------------------------------------- /nsroot/math/add.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math +", 3 | "body": "${1:result}=\\$((${2:int1} + ${3:int2}))\n", 4 | "description": "add two variables" 5 | } -------------------------------------------------------------------------------- /nsroot/math/const-gamma.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math const 𝛾", 3 | "body": "MATH_GAMMA='0.57721566490153286060651209008240243'\n", 4 | "description": "math Euler-Mascheroni constant" 5 | } -------------------------------------------------------------------------------- /nsroot/math/const-napier.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math const e", 3 | "body": "MATH_NAPIER='2.71828182845904523536028747135266249'\n", 4 | "description": "math Napier's constant" 5 | } -------------------------------------------------------------------------------- /nsroot/math/const-omega.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math const Ω", 3 | "body": "MATH_OMEGA='0.56714329040978387299996866221035554'\n", 4 | "description": "math Omega constant" 5 | } -------------------------------------------------------------------------------- /nsroot/math/const-phi.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math const ϕ", 3 | "body": "MATH_GOLDEN_RATIO='1.61803398874989484820458683436563811'\n", 4 | "description": "math golden ration constant" 5 | } -------------------------------------------------------------------------------- /nsroot/math/const-pi.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math const π", 3 | "body": "MATH_PI='3.14159265358979323846264338327950288'\n", 4 | "description": "math PI constant" 5 | } -------------------------------------------------------------------------------- /nsroot/math/decrement.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math --", 3 | "body": "((${1|int--,--int|}))\n", 4 | "description": "decrement integer variable" 5 | } -------------------------------------------------------------------------------- /nsroot/math/divide-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math /=", 3 | "body": "((${1:int1} /= ${2:int2}))\n", 4 | "description": "divide int1 by int2 and assign the whole part to int1" 5 | } -------------------------------------------------------------------------------- /nsroot/math/divide.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math /", 3 | "body": "${1:result}=\\$((${2:int1} / ${3:int2}))\n", 4 | "description": "divide int1 by int2 as integers and returns whole part" 5 | } -------------------------------------------------------------------------------- /nsroot/math/expr.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "expr", 4 | "arithmetic" 5 | ], 6 | "body": "${1:result}=\\$(expr \\${${2:int1}\\} ${3|+,-,\\*,/,%|} \\${${4:int2}\\})\n", 7 | "description": "arithmetic operations on integers" 8 | } -------------------------------------------------------------------------------- /nsroot/math/increment.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math ++", 3 | "body": "((${1|int++,++int|}))\n", 4 | "description": "increment integer variable by 1" 5 | } -------------------------------------------------------------------------------- /nsroot/math/let.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "let", 3 | "body": "let \"${1:result} = ${2:int1} ${3|+,-,*,/,%|} ${4:int2}\"\n", 4 | "description": "arithmetic operations on integers" 5 | } -------------------------------------------------------------------------------- /nsroot/math/minus-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math -=", 3 | "body": "((${1:int1} -= ${2:int2}))\n", 4 | "description": "subtract int2 from int1 and assign the result to int1" 5 | } -------------------------------------------------------------------------------- /nsroot/math/modulus-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math %=", 3 | "body": "((${1:int1} %= ${2:int2}))\n", 4 | "description": "divide int1 by int2 and assign the reminder to int1" 5 | } -------------------------------------------------------------------------------- /nsroot/math/modulus.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math %", 3 | "body": "${1:result}=\\$((${2:int1} % ${3:int2}))\n", 4 | "description": "reminder of dividing int1 by int2 (modulus)" 5 | } -------------------------------------------------------------------------------- /nsroot/math/multiply-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math *=", 3 | "body": "((${1:int1} *= ${2:int2}))\n", 4 | "description": "multiply int1 by int2 and assign the result to int1" 5 | } -------------------------------------------------------------------------------- /nsroot/math/multiply.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math *", 3 | "body": "${1:result}=\\$((${2:int1} * ${3:int2}))\n", 4 | "description": "multiply int1 by int2" 5 | } -------------------------------------------------------------------------------- /nsroot/math/plus-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math +=", 3 | "body": "((${1:int1} += ${2:int2}))\n", 4 | "description": "add int1 and int2 and assign the result to int1" 5 | } -------------------------------------------------------------------------------- /nsroot/math/power.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math ^", 3 | "body": "${1:result}=\\$((${2:base} ** ${3:power}))\n", 4 | "description": "exponentiate base to power" 5 | } -------------------------------------------------------------------------------- /nsroot/math/precision.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math 0.00", 3 | "body": "${1:result}=\\$(echo \"scale=${2|0,1,2,3,4,5,6,7,8,9|};(\\${${3:num1}\\} ${4|+,-,*,/,^|} \\${${5:num2}\\})\" | bc)\n", 4 | "description": "math operations with up to scale decimal places precision" 5 | } -------------------------------------------------------------------------------- /nsroot/math/random-number.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math random", 3 | "body": "${1:result}=\\$((${2:min} + RANDOM % \\$((${3:max}-${2:min}))))\n", 4 | "description": "generate random integer x such as min <= x <= max" 5 | } -------------------------------------------------------------------------------- /nsroot/math/sqrt.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "math √", 4 | "math sqrt" 5 | ], 6 | "body": "${1:result}=\\$(echo \"scale=${2|0,1,2,3,4,5,6,7,8,9|};sqrt(${3|num,${num}|})\" | bc)\n", 7 | "description": "square root of var up to scale decimal places" 8 | } -------------------------------------------------------------------------------- /nsroot/math/subtract.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "math -", 3 | "body": "${1:result}=\\$((${2:int1} - ${3:int2}))\n", 4 | "description": "subtract int2 from int1" 5 | } -------------------------------------------------------------------------------- /nsroot/misc/am-I-not-root.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "am I not root", 4 | "am I not sudo" 5 | ], 6 | "body": [ 7 | "if (( \\$(id -u) != 0 )); then", 8 | "\t${1:echo \"I'm not root\"}", 9 | "fi\n" 10 | ], 11 | "description": "check if script is not running as root (sudo)" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /nsroot/misc/am-I-root.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "am I root", 4 | "am I sudo" 5 | ], 6 | "body": [ 7 | "if (( $(id -u) == 0 )); then", 8 | "\t${1:echo \"I'm root\"}", 9 | "fi\n" 10 | ], 11 | "description": "check if script is running as root (sudo)" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /nsroot/misc/animation-frame.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "animation frame", 3 | "body": [ 4 | "# Your frames need to have the exact same width and height.", 5 | "# If they are different in size, fill unused space with `space`s (no `TAB`s).", 6 | "IFS='' read -r -d '' \"${1:frames}[${2|1,2,3,4,5,6,7,8,9|}]\" <<\"EOF\"", 7 | "${3:# Frame here}", 8 | "EOF\n" 9 | ], 10 | "description": "define animation frame" 11 | } -------------------------------------------------------------------------------- /nsroot/misc/arguments-parse.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "argument parsing", 4 | "parse args" 5 | ], 6 | "body": [ 7 | "POSITIONAL=()", 8 | "while (( \\$# > 0 )); do", 9 | "\tcase \"\\${1\\}\" in", 10 | "\t\t-f|--flag)", 11 | "\t\techo flag: \"\\${1\\}\"", 12 | "\t\tshift # shift once since flags have no values", 13 | "\t\t;;", 14 | "\t\t-s|--switch)", 15 | "\t\tnumOfArgs=1 # number of switch arguments", 16 | "\t\tif (( \\$# < numOfArgs + 1 )); then", 17 | "\t\t\tshift \\$#", 18 | "\t\telse", 19 | "\t\t\techo \"switch: \\${1\\} with value: \\${2\\}\"", 20 | "\t\t\tshift \\$((numOfArgs + 1)) # shift 'numOfArgs + 1' to bypass switch and its value", 21 | "\t\tfi", 22 | "\t\t;;", 23 | "\t\t*) # unknown flag/switch", 24 | "\t\tPOSITIONAL+=(\"\\${1\\}\")", 25 | "\t\tshift", 26 | "\t\t;;", 27 | "\tesac", 28 | "done\n", 29 | "set -- \"\\${POSITIONAL[@]}\" # restore positional params\n" 30 | ], 31 | "description": "parse command line arguments (flags/switches)" 32 | } -------------------------------------------------------------------------------- /nsroot/misc/echo-text.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "echo text", 4 | "print text" 5 | ], 6 | "body": "echo '${1:text here}'\n", 7 | "description": "print text, variable or both" 8 | } -------------------------------------------------------------------------------- /nsroot/misc/echo-variable.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "echo variable", 4 | "print variable" 5 | ], 6 | "body": "echo \"\\${${1|result,variable|}\\}\"\n", 7 | "description": "print text, variable or both" 8 | } -------------------------------------------------------------------------------- /nsroot/misc/exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "exit code", 3 | "body": "exit ${1|0,1,2,3,4,5,6,7,8,9,255|}\n", 4 | "description": "provide an exit code on error" 5 | } -------------------------------------------------------------------------------- /nsroot/misc/osis.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "os is", 3 | "body": "OS=$(awk -F'=' '/^ID=/ { gsub(\"\\\"\",\"\",\\$2); print tolower(\\$2) }' /etc/*-release 2> /dev/null)\n", 4 | "description": "The OS running on this machine" 5 | } -------------------------------------------------------------------------------- /nsroot/misc/region.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "region", 4 | "section" 5 | ], 6 | "body": [ 7 | "# >>>>>>>>>>>>>>>>>>>>>>>> ${1|functions,event handlers,variables,frames,argument parsing,main code|} >>>>>>>>>>>>>>>>>>>>>>>>", 8 | "$0", 9 | "# <<<<<<<<<<<<<<<<<<<<<<<< ${1} <<<<<<<<<<<<<<<<<<<<<<<<" 10 | ], 11 | "description": "comment out a special region (i.e. variable declarations)" 12 | } -------------------------------------------------------------------------------- /nsroot/misc/shebang.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "shebang", 4 | "bash", 5 | "first line" 6 | ], 7 | "body": "#!/usr/bin/env ${1|bash,node,perl,php,python,python3,ruby|}\n\n", 8 | "description": "shell shebang" 9 | } -------------------------------------------------------------------------------- /nsroot/misc/sleep.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "sleep", 3 | "body": "sleep ${1:30}${2|s,m,h,d|}\n", 4 | "description": "sleep for a specified amount of time (s: second, m: minute, h: hour, d: day)" 5 | } -------------------------------------------------------------------------------- /nsroot/misc/stopwatch-elapsed.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "stopwatch elapsed", 3 | "body": [ 4 | "stopwatchElapsedTotalSeconds=$((stopwatchEndTime - stopwatchStartTime))", 5 | "stopwatchElapsedMinutes=$((stopwatchElapsedTotalSeconds / 60))", 6 | "stopwatchElapsedSeconds=$((stopwatchElapsedTotalSeconds % 60))", 7 | "${1:echo \"elapsed \\${stopwatchElapsedMinutes\\} minutes and \\${stopwatchElapsedSeconds\\} seconds}.\"\n" 8 | ], 9 | "description": "elapsed time" 10 | } -------------------------------------------------------------------------------- /nsroot/misc/stopwatch-start.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "stopwatch start", 3 | "body": "stopwatchStartTime=\\$(date +%s)\n", 4 | "description": "start stopwatch" 5 | } -------------------------------------------------------------------------------- /nsroot/misc/stopwatch-stop.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "stopwatch stop", 3 | "body": "stopwatchEndTime=\\$(date +%s)\n", 4 | "description": "stop stopwatch" 5 | } -------------------------------------------------------------------------------- /nsroot/misc/summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "summary", 3 | "body": [ 4 | "# Title: ${1:title}", 5 | "# Description: ${2:description}", 6 | "# Author: ${3:author }", 7 | "# Date: ${4:yyyy-mm-dd}", 8 | "# Version: ${5:1.0.0}", 9 | "", 10 | "# Exit codes", 11 | "# ==========", 12 | "# 0 no error", 13 | "# 1 script interrupted", 14 | "# ${6:2} ${7:error description}\n\n" 15 | ], 16 | "description": "script summary" 17 | } -------------------------------------------------------------------------------- /nsroot/misc/timeout.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "timeout", 3 | "body": "timeout ${1:seconds} ${2:command}\n", 4 | "description": "run command within a time frame" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-black.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color black", 3 | "body": "echo \"\\$(tput setaf 0)\"${1:black text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in black" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-blue.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color blue", 3 | "body": "echo \"\\$(tput setaf 4)\"${1:blue text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in blue" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-cyan.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color cyan", 3 | "body": "echo \"\\$(tput setaf 6)\"${1:cyan text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in cyan" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-green.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color green", 3 | "body": "echo \"\\$(tput setaf 2)\"${1:green text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in green" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-magenta.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color magenta", 3 | "body": "echo \"\\$(tput setaf 5)\"${1:magenta text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in magenta" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-red.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color red", 3 | "body": "echo \"\\$(tput setaf 1)\"${1:red text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in red" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-white.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color white", 3 | "body": "echo \"\\$(tput setaf 7)\"${1:white text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in white" 5 | } -------------------------------------------------------------------------------- /nsroot/output/color-yellow.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "color yellow", 3 | "body": "echo \"\\$(tput setaf 3)\"${1:yellow text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in yellow" 5 | } -------------------------------------------------------------------------------- /nsroot/output/format-bold.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "format bold", 3 | "body": "echo \"\\$(tput bold)\"${1:bold text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in bold" 5 | } -------------------------------------------------------------------------------- /nsroot/output/format-dim.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "format dim", 3 | "body": "echo \"\\$(tput dim)\"${1:dimmed text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in dim" 5 | } -------------------------------------------------------------------------------- /nsroot/output/format-italic.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "format italic", 3 | "body": "echo \"\\$(tput sitm)\"${1:italic text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in italic" 5 | } -------------------------------------------------------------------------------- /nsroot/output/format-reverse.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "format reverse", 3 | "body": "echo \"\\$(tput rev)\"${1:reversed text}\"\\$(tput sgr0)\"\n", 4 | "description": "write in reverse" 5 | } -------------------------------------------------------------------------------- /nsroot/process/process-id.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "process ID(s)", 3 | "body": [ 4 | "readarray -t ${1:processIDsArray} < <(pgrep ${2|processName, \"${processName}\"|})", 5 | "${3:echo \"\\${${1}[@]\\}\"}\n" 6 | ], 7 | "description": "find process ID(s) aka PIDs" 8 | } -------------------------------------------------------------------------------- /nsroot/process/process-instances.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "process instances", 3 | "body": "${1:processInstances}=\\$(pgrep -l \"\\${${2:processName}\\}\")\n", 4 | "description": "list processes" 5 | } -------------------------------------------------------------------------------- /nsroot/process/process-kill.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "process kill", 3 | "body": "sudo kill -9 \"\\$(pgrep ${1:processName})\"\n", 4 | "description": "kill process by name" 5 | } -------------------------------------------------------------------------------- /nsroot/process/process-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "process list all", 3 | "body": "ps -A\n", 4 | "description": "list processes" 5 | } -------------------------------------------------------------------------------- /nsroot/process/process-name.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "process Name by ID", 3 | "body": "${1:processName}=\\$(ps -p \"\\${${2:pid}\\}\" -o comm=)\n", 4 | "description": "find process name by it's ID(s)" 5 | } -------------------------------------------------------------------------------- /nsroot/string/concat.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "string concat", 4 | "string + string" 5 | ], 6 | "body": "${1:string}=\"\\${${2:string1}}\\${${3:string2}}\"\n", 7 | "description": "concatenate two strings" 8 | } 9 | -------------------------------------------------------------------------------- /nsroot/string/contains.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "string contains", 4 | "if string contains" 5 | ], 6 | "body": [ 7 | "if [[ \"\\${${1:string}\\}\" == *\"\\${${2:substring}\\}\"* ]]; then", 8 | "\t${3:echo \"\\${${1:string}\\} contains: \\${${2:substring}\\}\"}", 9 | "fi\n" 10 | ], 11 | "description": "check whether string contains substring" 12 | } -------------------------------------------------------------------------------- /nsroot/string/first-index-substring.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "string indexOf", 3 | "body": [ 4 | "string=\"${1:hello world}\"", 5 | "substring=\"${2:world}\"", 6 | 7 | "prefix=${string%%\"\\$substring\"*}", 8 | "index=${#prefix}", 9 | 10 | "if [[ index -eq ${#string} ]]; then", 11 | "\techo -1", 12 | "else", 13 | "\techo \"\\$index\"", 14 | "fi" 15 | ], 16 | "description": "first index of substring in a string" 17 | } -------------------------------------------------------------------------------- /nsroot/string/if-empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if string empty", 3 | "body": [ 4 | "if [[ -z \"\\${${1:string}\\}\" ]]; then", 5 | "\t${2:echo \"empty string\"}", 6 | "fi\n" 7 | ], 8 | "description": "if string is empty" 9 | } -------------------------------------------------------------------------------- /nsroot/string/if-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if string =", 4 | "string equal" 5 | ], 6 | "body": [ 7 | "if [[ \"\\${${1:string1}\\}\" == \"\\${${2:string2}\\}\" ]]; then", 8 | "\t${3:echo \"The two strings are the same\"}", 9 | "fi\n" 10 | ], 11 | "description": "if strings are equal" 12 | } 13 | -------------------------------------------------------------------------------- /nsroot/string/if-not-empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "if string not empty", 3 | "body": [ 4 | "if [[ -n \"\\${${1:string}\\}\" ]]; then", 5 | "\t${2:echo \"string is not empty\"}", 6 | "fi\n" 7 | ], 8 | "description": "if string is not empty" 9 | } -------------------------------------------------------------------------------- /nsroot/string/if-not-equal.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": [ 3 | "if string !=", 4 | "string not equal" 5 | ], 6 | "body": [ 7 | "if [[ \"\\${${1:string1}\\}\" != \"\\${${2:string2}\\}\" ]]; then", 8 | "\t${3:echo \"The two strings are different\"}", 9 | "fi\n" 10 | ], 11 | "description": "if strings are not equal" 12 | } -------------------------------------------------------------------------------- /nsroot/string/length.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "string length", 3 | "body": "${1:length}=\\${#${2:string}}\n", 4 | "description": "length of string in characters" 5 | } 6 | -------------------------------------------------------------------------------- /nsroot/string/random.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "string random", 3 | "body": "${1:randomString}=$(tr -dc ${2:A-Za-z0-9} >>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 4 | 5 | # Usage: bannerColor "my title" "red" "*" 6 | function bannerColor() { 7 | case ${2} in 8 | black) color=0 9 | ;; 10 | red) color=1 11 | ;; 12 | green) color=2 13 | ;; 14 | yellow) color=3 15 | ;; 16 | blue) color=4 17 | ;; 18 | magenta) color=5 19 | ;; 20 | cyan) color=6 21 | ;; 22 | white) color=7 23 | ;; 24 | *) echo "color is not set"; exit 1 25 | ;; 26 | esac 27 | 28 | local msg="${3} ${1} ${3}" 29 | local edge 30 | edge=$(echo "${msg}" | sed "s/./${3}/g") 31 | tput setaf ${color} 32 | tput bold 33 | echo "${edge}" 34 | echo "${msg}" 35 | echo "${edge}" 36 | tput sgr 0 37 | echo 38 | } 39 | 40 | 41 | # Usage: multiChoice "header message" resultArray "comma separated options" "comma separated default values" 42 | # TODO: 1) Refactoring to return result array 2) Get input options as array 43 | function multiChoice { 44 | echo "${1}"; shift 45 | echo $(tput dim)-"Change Option: [up/down], Change Selection: [space], Done: [ENTER]" $(tput sgr0) 46 | # little helpers for terminal print control and key input 47 | ESC=$( printf "\033") 48 | cursor_blink_on() { printf "$ESC[?25h"; } 49 | cursor_blink_off() { printf "$ESC[?25l"; } 50 | cursor_to() { printf "$ESC[$1;${2:-1}H"; } 51 | print_inactive() { printf "$2 $1 "; } 52 | print_active() { printf "$2 $ESC[7m $1 $ESC[27m"; } 53 | get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; } 54 | key_input() { 55 | local key 56 | IFS= read -rsn1 key 2>/dev/null >&2 57 | if [[ $key = "" ]]; then echo enter; fi; 58 | if [[ $key = $'\x20' ]]; then echo space; fi; 59 | if [[ $key = $'\x1b' ]]; then 60 | read -rsn2 key 61 | if [[ $key = [A ]]; then echo up; fi; 62 | if [[ $key = [B ]]; then echo down; fi; 63 | fi 64 | } 65 | toggle_option() { 66 | local arr_name=$1 67 | eval "local arr=(\"\${${arr_name}[@]}\")" 68 | local option=$2 69 | if [[ ${arr[option]} == 1 ]]; then 70 | arr[option]=0 71 | else 72 | arr[option]=1 73 | fi 74 | eval $arr_name='("${arr[@]}")' 75 | } 76 | 77 | local retval=$1 78 | local options 79 | local defaults 80 | 81 | IFS=';' read -r -a options <<< "$2" 82 | if [[ -z $3 ]]; then 83 | defaults=() 84 | else 85 | IFS=';' read -r -a defaults <<< "$3" 86 | fi 87 | 88 | local selected=() 89 | 90 | for ((i=0; i<${#options[@]}; i++)); do 91 | selected+=("${defaults[i]}") 92 | printf "\n" 93 | done 94 | 95 | # determine current screen position for overwriting the options 96 | local lastrow=$(get_cursor_row) 97 | local startrow=$(($lastrow - ${#options[@]})) 98 | 99 | # ensure cursor and input echoing back on upon a ctrl+c during read -s 100 | trap "cursor_blink_on; stty echo; printf '\n'; exit" 2 101 | cursor_blink_off 102 | 103 | local active=0 104 | while true; do 105 | # print options by overwriting the last lines 106 | local idx=0 107 | for option in "${options[@]}"; do 108 | local prefix="[ ]" 109 | if [[ ${selected[idx]} == 1 ]]; then 110 | prefix="[x]" 111 | fi 112 | 113 | cursor_to $(($startrow + $idx)) 114 | if [ $idx -eq $active ]; then 115 | print_active "$option" "$prefix" 116 | else 117 | print_inactive "$option" "$prefix" 118 | fi 119 | ((idx++)) 120 | done 121 | 122 | # user key control 123 | case $(key_input) in 124 | space) toggle_option selected $active;; 125 | enter) break;; 126 | up) ((active--)); 127 | if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi;; 128 | down) ((active++)); 129 | if [ $active -ge ${#options[@]} ]; then active=0; fi;; 130 | esac 131 | done 132 | 133 | # cursor position back to normal 134 | cursor_to $lastrow 135 | printf "\n" 136 | cursor_blink_on 137 | 138 | indices=() 139 | for((i=0;i<${#selected[@]};i++)); do 140 | if ((${selected[i]} == 1)); then 141 | indices+=(${i}) 142 | fi 143 | done 144 | 145 | # eval $retval='("${selected[@]}")' 146 | eval $retval='("${indices[@]}")' 147 | } 148 | 149 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 150 | 151 | # Usage: bannerColor "my title" "red" "*" 152 | bannerColor "Publish vscode extension to market" "magenta" ":" 153 | 154 | 155 | # Usage: multiChoice "header message" resultArray "comma separated options" "comma separated default values" 156 | multiChoice "Which markets to publish:" markets "vscode;open-vsx" "1;1" 157 | 158 | if [[ "${markets[@]}" =~ '0' ]]; then 159 | echo 160 | echo "Enter vscode token:" 161 | read -s vscodeToken 162 | echo $(tput setaf 6)"Publishing to vscode..."$(tput sgr0) 163 | 164 | npx vsce publish -p "${vscodeToken}" 165 | fi 166 | 167 | if [[ "${markets[@]}" =~ '1' ]]; then 168 | echo 169 | echo "Enter open-vsx token:" 170 | read -s openVsxToken 171 | echo $(tput setaf 6)"Publishing to open-vsx..."$(tput sgr0) 172 | 173 | npx ovsx publish -p "${openVsxToken}" 174 | fi 175 | 176 | echo "DONE!" -------------------------------------------------------------------------------- /samples/animation/funny-cat.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | IFS='' read -r -d '' "frames[1]" <<"EOF" 4 | . . 5 | |\_---_/| 6 | / o_O \ 7 | | U | 8 | \ ._I_. / 9 | `-_____-' 10 | EOF 11 | 12 | IFS='' read -r -d '' "frames[2]" <<"EOF" 13 | . . 14 | |\_---_/| 15 | / O_o \ 16 | | U | 17 | \ ._I_. / 18 | `-_____-' 19 | EOF 20 | 21 | # Usage: animate frames_array interval 22 | function animate () { 23 | local frames=("$@") 24 | ((last_index=${#frames[@]} - 1)) 25 | local interval=${frames[last_index]} 26 | unset "frames[last_index]" 27 | 28 | # Comment out next two lines if you are using CTRL+C event handler. 29 | trap 'tput cnorm; echo' EXIT 30 | trap 'exit 127' HUP INT TERM 31 | 32 | tput civis # hide cursor 33 | tput sc # save cursor position 34 | 35 | while true; do 36 | for frame in "${frames[@]}"; do 37 | tput rc # restore cursor position 38 | echo "${frame}" 39 | sleep "${interval}" 40 | done 41 | done 42 | } 43 | 44 | # Usage: animate frames_array interval 45 | animate "${frames[@]}" 0.5 46 | -------------------------------------------------------------------------------- /samples/animation/pacman.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Usage: pac_man inputString interval pad 4 | # Example: pacman "Hello World" 0.5 "*" 5 | function pac_man () { 6 | local string="${1}" 7 | local interval="${2}" 8 | : "${interval:=0.2}" 9 | local pad="${3}" 10 | : "${pad:=.}" 11 | local length=${#string} 12 | local padding="" 13 | 14 | # Comment out next two lines if you are using CTRL+C event handler. 15 | trap 'tput cnorm; echo' EXIT 16 | trap 'exit 127' HUP INT TERM 17 | 18 | tput civis # hide cursor 19 | tput sc # save cursor position 20 | 21 | for((i=0;i<=length;i++)); do 22 | tput rc 23 | echo "${padding}c${string:i:length}" 24 | sleep "$interval" 25 | tput rc 26 | echo "${padding}C${string:i:length}" 27 | sleep "${interval}" 28 | padding+="${pad}" 29 | done 30 | 31 | tput cnorm 32 | tput rc 33 | echo "${padding}" 34 | } 35 | 36 | # Usage: pac_man inputString interval pad 37 | pac_man "Hello World" 0.2 "." 38 | -------------------------------------------------------------------------------- /samples/animation/pendulum.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # >>>>>>>>>>>>>>>>>>>>>>>> frames >>>>>>>>>>>>>>>>>>>>>>>> 4 | 5 | IFS='' read -r -d '' "frames[1]" <<"EOF" 6 | / 7 | / 8 | / 9 | o 10 | EOF 11 | 12 | IFS='' read -r -d '' "frames[2]" <<"EOF" 13 | | 14 | | 15 | | 16 | o 17 | EOF 18 | 19 | IFS='' read -r -d '' "frames[3]" <<"EOF" 20 | \ 21 | \ 22 | \ 23 | o 24 | EOF 25 | 26 | # <<<<<<<<<<<<<<<<<<<<<<<< frames <<<<<<<<<<<<<<<<<<<<<<<< 27 | 28 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 29 | 30 | # Usage: animate framesArray interval 31 | function animate () { 32 | local frames=("$@") 33 | 34 | ((lastIndex=${#frames[@]} - 1)) 35 | local mode=${frames[lastIndex]} 36 | unset "frames[lastIndex]" 37 | 38 | ((lastIndex=${#frames[@]} - 1)) 39 | local interval=${frames[lastIndex]} 40 | unset "frames[lastIndex]" 41 | 42 | # Comment out next two lines if you are using CTRL+C event handler. 43 | trap 'tput cnorm; echo' EXIT 44 | trap 'exit 127' HUP INT TERM 45 | 46 | tput civis # hide cursor 47 | tput sc # save cursor position 48 | 49 | tput civis # hide cursor 50 | tput sc # save cursor position 51 | 52 | index=0 53 | max="${#frames[@]}" 54 | indices=() 55 | direction="forward" 56 | forwardIndices=( $(seq 0 1 "${max}") ) 57 | backwardIndices=( $(seq "${max}" -1 0) ) 58 | 59 | while true; do 60 | if [ "${mode}" = "circular" ]; then 61 | direction="forward" 62 | elif [ "${mode}" = "pendular" ]; then 63 | if (( index >= max )); then 64 | direction="backward" 65 | elif (( index <= 0 )); then 66 | direction="forward" 67 | fi 68 | else 69 | echo "Wrong mode! Valid modes: circular, pendular" 70 | exit 255 71 | fi 72 | 73 | if [ "${direction}" = "forward" ]; then 74 | indices=( "${forwardIndices[@]}" ) 75 | else 76 | indices=( "${backwardIndices[@]}" ) 77 | fi 78 | 79 | 80 | for index in "${indices[@]}"; do 81 | tput rc # restore cursor position 82 | echo "${frames[$index]}" 83 | sleep "${interval}" 84 | done 85 | done 86 | } 87 | 88 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 89 | 90 | # Usage: animate framesArray interval 91 | animate "${frames[@]}" 0.3 pendular 92 | -------------------------------------------------------------------------------- /samples/animation/wavy-shellman.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # >>>>>>>>>>>>>>>>>>>>>>>> frames >>>>>>>>>>>>>>>>>>>>>>>> 4 | 5 | IFS='' read -r -d '' "frames[1]" <<"EOF" 6 | ==~========= 7 | | Shellman | 8 | ==~========= 9 | EOF 10 | 11 | IFS='' read -r -d '' "frames[2]" <<"EOF" 12 | ===~======== 13 | | sHellman | 14 | ===~======== 15 | EOF 16 | 17 | IFS='' read -r -d '' "frames[3]" <<"EOF" 18 | ====~======= 19 | | ShEllman | 20 | ====~======= 21 | EOF 22 | 23 | IFS='' read -r -d '' "frames[4]" <<"EOF" 24 | =====~====== 25 | | SheLlman | 26 | =====~====== 27 | EOF 28 | 29 | IFS='' read -r -d '' "frames[5]" <<"EOF" 30 | ======~===== 31 | | ShelLman | 32 | ======~===== 33 | EOF 34 | 35 | IFS='' read -r -d '' "frames[6]" <<"EOF" 36 | =======~==== 37 | | ShellMan | 38 | =======~==== 39 | EOF 40 | 41 | IFS='' read -r -d '' "frames[7]" <<"EOF" 42 | ========~=== 43 | | ShellmAn | 44 | ========~=== 45 | EOF 46 | 47 | IFS='' read -r -d '' "frames[8]" <<"EOF" 48 | =========~== 49 | | ShellmaN | 50 | =========~== 51 | EOF 52 | 53 | # <<<<<<<<<<<<<<<<<<<<<<<< frames <<<<<<<<<<<<<<<<<<<<<<<< 54 | 55 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 56 | 57 | # Usage: animate framesArray interval 58 | function animate () { 59 | local frames=("$@") 60 | 61 | ((lastIndex=${#frames[@]} - 1)) 62 | local mode=${frames[lastIndex]} 63 | unset "frames[lastIndex]" 64 | 65 | ((lastIndex=${#frames[@]} - 1)) 66 | local interval=${frames[lastIndex]} 67 | unset "frames[lastIndex]" 68 | 69 | # Comment out next two lines if you are using CTRL+C event handler. 70 | trap 'tput cnorm; echo' EXIT 71 | trap 'exit 127' HUP INT TERM 72 | 73 | tput civis # hide cursor 74 | tput sc # save cursor position 75 | 76 | tput civis # hide cursor 77 | tput sc # save cursor position 78 | 79 | index=0 80 | max="${#frames[@]}" 81 | indices=() 82 | direction="forward" 83 | forwardIndices=( $(seq 0 1 "${max}") ) 84 | backwardIndices=( $(seq "${max}" -1 0) ) 85 | 86 | while true; do 87 | if [ "${mode}" = "circular" ]; then 88 | direction="forward" 89 | elif [ "${mode}" = "pendular" ]; then 90 | if (( index >= max )); then 91 | direction="backward" 92 | elif (( index <= 0 )); then 93 | direction="forward" 94 | fi 95 | else 96 | echo "Wrong mode! Valid modes: circular, pendular" 97 | exit 255 98 | fi 99 | 100 | if [ "${direction}" = "forward" ]; then 101 | indices=( "${forwardIndices[@]}" ) 102 | else 103 | indices=( "${backwardIndices[@]}" ) 104 | fi 105 | 106 | 107 | for index in "${indices[@]}"; do 108 | tput rc # restore cursor position 109 | echo "${frames[$index]}" 110 | sleep "${interval}" 111 | done 112 | done 113 | } 114 | 115 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 116 | 117 | # Usage: animate framesArray interval 118 | animate "${frames[@]}" 0.3 pendular 119 | -------------------------------------------------------------------------------- /samples/backup-tool/README.md: -------------------------------------------------------------------------------- 1 | # Backup tool 2 | 3 | In this example we are gonna write an script to backup some folders as a `tar.gz` archive to specified directory (default directory `~/backups`). Handle `CTRL+C` and cleanup if script interrupted. Optionally play animation after backup. 4 | 5 | First we create our script structure. 6 | 7 | ## Script structure 8 | 9 | 1. shebang 10 | 2. summary & error codes 11 | 3. event handlers (related functions before them) 12 | 4. animation frames 13 | 5. functions 14 | 6. argument parsing 15 | 7. main code (entry point) 16 | 17 | Create an empty file named `backup.sh`. Continue with following steps: 18 | 19 | ### shebang 20 | 21 | Type `shebang` and press `ENTER`. From dropdown menu press `ENTER` on default option (`bash`): 22 | 23 | ```bash 24 | #!/usr/bin/env bash 25 | 26 | ``` 27 | 28 | ### summary 29 | 30 | Type `summary` and press `ENTER`. Use `TAB` and fill out fields. 31 | 32 | ```bash 33 | #!/usr/bin/env bash 34 | 35 | # Title: Backup tool 36 | # Description: Backups defined folders into backups folder in home directory 37 | # Author: Remisa Yousefvand 38 | # Date: 2020-07-07 39 | # Version: 1.0.0 40 | 41 | # Exit codes 42 | # ========== 43 | # 0 no error 44 | # TODO: more later 45 | 46 | ``` 47 | 48 | ### event handlers 49 | 50 | We want to capture `CTRL+C` if script is interrupted by user so we can run our cleanup code. 51 | 52 | Use `region` snippet to create `event handling` region. Inside that region: 53 | 54 | Type `event` and with arrow up/down keys select `event CTRL+C` and hit `ENTER`. We are using code snippets from Shellman. We already have used `shebang`, `summary` snippets. 55 | 56 | Call `cleanup` function in `on_ctrl_c` function and use `color` snippet (type `color`) and select `red` to print an error message. Above `on_ctrl_c` function use `func` snippet to define a function. Type `cleanup` as function name and add a `TODO` comment as a place holder. define `1` as error code for interruption in `Exit codes` section. 57 | 58 | ```bash 59 | #!/usr/bin/env bash 60 | 61 | # Title: Backup tool 62 | # Description: Backups defined folders into backups folder in home directory 63 | # Author: Remisa Yousefvand 64 | # Date: 2020-07-07 65 | # Version: 1.0.0 66 | 67 | # Exit codes 68 | # ========== 69 | # 0 no error 70 | # 1 script interrupted 71 | # TODO: more later 72 | 73 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 74 | function cleanup () { 75 | echo "TODO: cleanup code" 76 | } 77 | 78 | 79 | # CTRL+C event handler 80 | function on_ctrl_c() { 81 | echo # Set cursor to the next line of '^C' 82 | tput cnorm # show cursor. You need this if animation is used. 83 | cleanup # Call cleanup function 84 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 85 | 86 | exit 1 # Don't remove. Use a number (1-255) for error code. 87 | } 88 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 89 | # Register CTRL+C event handler 90 | trap on_ctrl_c SIGINT 91 | 92 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 93 | 94 | ``` 95 | 96 | ### animation frames 97 | 98 | Create a `animation frames` region using `region` snippet. Add two animation frames using `animation frame` snippet. Remember to update frame number when adding new frames. Here we use [funny cat](../animation/funny-cat.sh). 99 | 100 | ```bash 101 | #!/usr/bin/env bash 102 | 103 | # Title: Backup tool 104 | # Description: Backups defined folders into backups folder in home directory 105 | # Author: Remisa Yousefvand 106 | # Date: 2020-07-07 107 | # Version: 1.0.0 108 | 109 | # Exit codes 110 | # ========== 111 | # 0 no error 112 | # 1 script interrupted 113 | # TODO: more later 114 | 115 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 116 | function cleanup () { 117 | echo "TODO: cleanup code" 118 | } 119 | 120 | 121 | # CTRL+C event handler 122 | function on_ctrl_c() { 123 | echo # Set cursor to the next line of '^C' 124 | tput cnorm # show cursor. You need this if animation is used. 125 | cleanup # Call cleanup function 126 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 127 | 128 | exit 1 # Don't remove. Use a number (1-255) for error code. 129 | } 130 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 131 | # Register CTRL+C event handler 132 | trap on_ctrl_c SIGINT 133 | 134 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 135 | 136 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 137 | 138 | # Your frames need to have the exact same width and height. 139 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 140 | 141 | IFS='' read -r -d '' frames[1] <<"EOF" 142 | . . 143 | |\_---_/| 144 | / o_O \ 145 | | U | 146 | \ ._I_. / 147 | `-_____-' 148 | EOF 149 | 150 | IFS='' read -r -d '' frames[2] <<"EOF" 151 | . . 152 | |\_---_/| 153 | / O_o \ 154 | | U | 155 | \ ._I_. / 156 | `-_____-' 157 | EOF 158 | 159 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 160 | 161 | ``` 162 | 163 | ### functions 164 | 165 | Add a `functions` region. Put a `TODO` comments. Later we write functions here which are gonna do the real job. 166 | 167 | ```bash 168 | #!/usr/bin/env bash 169 | 170 | # Title: Backup tool 171 | # Description: Backups defined folders into backups folder in home directory 172 | # Author: Remisa Yousefvand 173 | # Date: 2020-07-07 174 | # Version: 1.0.0 175 | 176 | # Exit codes 177 | # ========== 178 | # 0 no error 179 | # 1 script interrupted 180 | # TODO: more later 181 | 182 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 183 | function cleanup () { 184 | echo "TODO: cleanup code" 185 | } 186 | 187 | 188 | # CTRL+C event handler 189 | function on_ctrl_c() { 190 | echo # Set cursor to the next line of '^C' 191 | tput cnorm # show cursor. You need this if animation is used. 192 | cleanup # Call cleanup function 193 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 194 | 195 | exit 1 # Don't remove. Use a number (1-255) for error code. 196 | } 197 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 198 | # Register CTRL+C event handler 199 | trap on_ctrl_c SIGINT 200 | 201 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 202 | 203 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 204 | 205 | # Your frames need to have the exact same width and height. 206 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 207 | 208 | IFS='' read -r -d '' frames[1] <<"EOF" 209 | . . 210 | |\_---_/| 211 | / o_O \ 212 | | U | 213 | \ ._I_. / 214 | `-_____-' 215 | EOF 216 | 217 | IFS='' read -r -d '' frames[2] <<"EOF" 218 | . . 219 | |\_---_/| 220 | / O_o \ 221 | | U | 222 | \ ._I_. / 223 | `-_____-' 224 | EOF 225 | 226 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 227 | 228 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 229 | 230 | # TODO: functions 231 | 232 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 233 | 234 | ``` 235 | 236 | ### argument parsing 237 | 238 | Add a `argument parsing` region and use `argument parsing` snippet as a scaffold for your parsing process. We need to design our script interface now. As a good practice we add `help` and `version` flags to all scripts. Also we accept `output` folder as a switch (a switch accepts parameter(s) while a flag does not) and if it was not passed to our script we are gonna use `~/backups` as default. To add a flag as an example lets play `animation` if such a flag is passed to our script (default to not play animation). Here is our design summary: 239 | 240 | | short | long | Description | Type | 241 | | ----- | ----------- | ------------------------------------- | ------ | 242 | | -h | --help | Prints help message | Flag | 243 | | -v | --version | Prints version info | Flag | 244 | | -o | --output | Backup directory | Switch | 245 | | -a | --animation | Show animation after backup completed | Flag | 246 | 247 | We define functions when needed and add a place holder for that function in `functions` region so we can write those functions later. It turns out we need a variable to store script version. Define it as top of the script as `VERSION` (in summary section). To write error messages use `color` snippet with `red` ink. 248 | 249 | ```bash 250 | #!/usr/bin/env bash 251 | 252 | # Title: Backup tool 253 | # Description: Backups defined folders into backups folder in home directory 254 | # Author: Remisa Yousefvand 255 | # Date: 2020-07-07 256 | # Version: 1.0.0 257 | VERSION="1.0.0" 258 | 259 | # Exit codes 260 | # ========== 261 | # 0 no error 262 | # 1 script interrupted 263 | # 2 unknown argument 264 | # 3 backup folder doesn't exist 265 | # 4 backup failed due to tar error 266 | 267 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 268 | function cleanup () { 269 | echo "TODO: cleanup code" 270 | } 271 | 272 | 273 | # CTRL+C event handler 274 | function on_ctrl_c() { 275 | echo # Set cursor to the next line of '^C' 276 | tput cnorm # show cursor. You need this if animation is used. 277 | cleanup # Call cleanup function 278 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 279 | 280 | exit 1 # Don't remove. Use a number (1-255) for error code. 281 | } 282 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 283 | # Register CTRL+C event handler 284 | trap on_ctrl_c SIGINT 285 | 286 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 287 | 288 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 289 | 290 | # Your frames need to have the exact same width and height. 291 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 292 | 293 | IFS='' read -r -d '' frames[1] <<"EOF" 294 | . . 295 | |\_---_/| 296 | / o_O \ 297 | | U | 298 | \ ._I_. / 299 | `-_____-' 300 | EOF 301 | 302 | IFS='' read -r -d '' frames[2] <<"EOF" 303 | . . 304 | |\_---_/| 305 | / O_o \ 306 | | U | 307 | \ ._I_. / 308 | `-_____-' 309 | EOF 310 | 311 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 312 | 313 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 314 | 315 | function help () { 316 | echo " 317 | Usage: ${0} [options] 318 | 319 | Options: 320 | -a, --animation Show animation after 321 | backup completed 322 | -h, --help Show this help message 323 | -o, --output Backup directory 324 | -v, --version Show version info 325 | " 326 | } 327 | 328 | function playAnimation () { 329 | echo "TODO: play animation" 330 | } 331 | 332 | function backup () { 333 | echo "TODO: backup" 334 | } 335 | 336 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 337 | 338 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 339 | 340 | while [[ $# > 0 ]]; do 341 | case "${1}" in 342 | -h|--help) 343 | help # call "help" function 344 | exit 0 # we don't process any other argument 345 | ;; 346 | -v|--version) 347 | echo "Backup tool v. ${VERSION}" 348 | exit 0 # we don't process any other argument 349 | ;; 350 | -o|--output) 351 | backupDirectory="${2}" 352 | shift 2 # one for switch and another for its value 353 | echo "${backupDirectory}" # test. TODO: remove 354 | ;; 355 | -a|--animation) 356 | playAnimation="true" 357 | shift # shift one for flag itself (we know it is either -a or --animation) 358 | echo "${playAnimation}" # test. TODO: remove 359 | ;; 360 | *) # unknown flag/switch 361 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 362 | exit 2 363 | ;; 364 | esac 365 | done 366 | 367 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 368 | 369 | ``` 370 | 371 | Test your script's argument parsing then remove test echo commands. 372 | 373 | ### main code 374 | 375 | Now we have parsed arguments and need to set default values if any variable is not set. To do so use `assign if empty` snippet to set default values for `backupDirectory` and `playAnimation` variables. 376 | 377 | ```bash 378 | #!/usr/bin/env bash 379 | 380 | # Title: Backup tool 381 | # Description: Backups defined folders into backups folder in home directory 382 | # Author: Remisa Yousefvand 383 | # Date: 2020-07-07 384 | # Version: 1.0.0 385 | VERSION="1.0.0" 386 | 387 | # Exit codes 388 | # ========== 389 | # 0 no error 390 | # 1 script interrupted 391 | # 2 unknown argument 392 | # 3 backup folder doesn't exist 393 | # TODO: more later 394 | 395 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 396 | function cleanup () { 397 | echo "cleanup code" 398 | } 399 | 400 | 401 | # CTRL+C event handler 402 | function on_ctrl_c() { 403 | echo # Set cursor to the next line of '^C' 404 | tput cnorm # show cursor. You need this if animation is used. 405 | cleanup # Call cleanup function 406 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 407 | 408 | exit 1 # Don't remove. Use a number (1-255) for error code. 409 | } 410 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 411 | # Register CTRL+C event handler 412 | trap on_ctrl_c SIGINT 413 | 414 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 415 | 416 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 417 | 418 | # Your frames need to have the exact same width and height. 419 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 420 | 421 | IFS='' read -r -d '' frames[1] <<"EOF" 422 | . . 423 | |\_---_/| 424 | / o_O \ 425 | | U | 426 | \ ._I_. / 427 | `-_____-' 428 | EOF 429 | 430 | IFS='' read -r -d '' frames[2] <<"EOF" 431 | . . 432 | |\_---_/| 433 | / O_o \ 434 | | U | 435 | \ ._I_. / 436 | `-_____-' 437 | EOF 438 | 439 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 440 | 441 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 442 | 443 | function help () { 444 | echo " 445 | Usage: ${0} [options] 446 | 447 | Options: 448 | -a, --animation Show animation after 449 | backup completed 450 | -h, --help Show this help message 451 | -o, --output Backup directory 452 | -v, --version Show version info 453 | " 454 | } 455 | 456 | function playAnimation () { 457 | echo "TODO: play animation" 458 | } 459 | 460 | function backup () { 461 | echo "TODO: backup" 462 | } 463 | 464 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 465 | 466 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 467 | 468 | while [[ $# > 0 ]]; do 469 | case "${1}" in 470 | -h|--help) 471 | help # call "help" function 472 | exit 0 # we don't process any other argument 473 | ;; 474 | -v|--version) 475 | echo "Backup tool v. ${VERSION}" 476 | exit 0 # we don't process any other argument 477 | ;; 478 | -o|--output) 479 | backupDirectory="${2}" 480 | shift 2 # one for switch and another for its value 481 | ;; 482 | -a|--animation) 483 | playAnimation="true" 484 | shift # shift one for flag itself (we know it is either -a or --animation) 485 | ;; 486 | *) # unknown flag/switch 487 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 488 | exit 2 489 | ;; 490 | esac 491 | done 492 | 493 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 494 | 495 | # Setting default values 496 | : "${backupDirectory:=~/backups}" 497 | : "${playAnimation:=false}" 498 | 499 | # Test default values. TODO: remove 500 | echo "${backupDirectory}" 501 | echo "${playAnimation}" 502 | 503 | ``` 504 | 505 | Test script and remove test echos for default values. 506 | 507 | First we check if backup folder exists and if not we exit with error code 3 as documented before. Use `if directory exists` and a negation (`!`) to run the code when backup directory doesn't exist: 508 | 509 | ```bash 510 | if [ ! -d "${backupDirectory}" ]; then 511 | echo `tput setaf 1`"Error! Backup directory doesn't exist: ${backupDirectory}"`tput sgr0` 512 | exit 3 513 | fi 514 | ``` 515 | 516 | Now we need to write the actual backup code inside `backup` function and call it but we should take care of few things before that. Since desired folders to backup are known and are not gonna change frequently we use an array at the top of script to hard-code those paths. Use `array declare` snippet to define this array. Also we need to chose a name for our backup file and store it inside a variable. I'm gonna use `yyyy-mm-dd.tar.gz` using `date now short` snippet. 517 | 518 | ```bash 519 | #!/usr/bin/env bash 520 | 521 | # Title: Backup tool 522 | # Description: Backups defined folders into backups folder in home directory 523 | # Author: Remisa Yousefvand 524 | # Date: 2020-07-07 525 | # Version: 1.0.0 526 | VERSION="1.0.0" 527 | 528 | # Exit codes 529 | # ========== 530 | # 0 no error 531 | # 1 script interrupted 532 | # 2 unknown argument 533 | # 3 backup folder doesn't exist 534 | # TODO: more later 535 | 536 | sourcePaths=( 537 | '~/Desktop' 538 | '~/.config' 539 | '.bash_history' 540 | ) 541 | 542 | 543 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 544 | function cleanup () { 545 | echo "cleanup code" 546 | } 547 | 548 | 549 | # CTRL+C event handler 550 | function on_ctrl_c() { 551 | echo # Set cursor to the next line of '^C' 552 | tput cnorm # show cursor. You need this if animation is used. 553 | cleanup # Call cleanup function 554 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 555 | 556 | exit 1 # Don't remove. Use a number (1-255) for error code. 557 | } 558 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 559 | # Register CTRL+C event handler 560 | trap on_ctrl_c SIGINT 561 | 562 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 563 | 564 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 565 | 566 | # Your frames need to have the exact same width and height. 567 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 568 | 569 | IFS='' read -r -d '' frames[1] <<"EOF" 570 | . . 571 | |\_---_/| 572 | / o_O \ 573 | | U | 574 | \ ._I_. / 575 | `-_____-' 576 | EOF 577 | 578 | IFS='' read -r -d '' frames[2] <<"EOF" 579 | . . 580 | |\_---_/| 581 | / O_o \ 582 | | U | 583 | \ ._I_. / 584 | `-_____-' 585 | EOF 586 | 587 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 588 | 589 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 590 | 591 | function help () { 592 | echo " 593 | Usage: ${0} [options] 594 | 595 | Options: 596 | -a, --animation Show animation after 597 | backup completed 598 | -h, --help Show this help message 599 | -o, --output Backup directory 600 | -v, --version Show version info 601 | " 602 | } 603 | 604 | function playAnimation () { 605 | echo "TODO: play animation" 606 | } 607 | 608 | function backup () { 609 | echo "TODO: backup" 610 | } 611 | 612 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 613 | 614 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 615 | 616 | while [[ $# > 0 ]]; do 617 | case "${1}" in 618 | -h|--help) 619 | help # call "help" function 620 | exit 0 # we don't process any other argument 621 | ;; 622 | -v|--version) 623 | echo "Backup tool v. ${VERSION}" 624 | exit 0 # we don't process any other argument 625 | ;; 626 | -o|--output) 627 | backupDirectory="${2}" 628 | shift 2 # one for switch and another for its value 629 | ;; 630 | -a|--animation) 631 | playAnimation="true" 632 | shift # shift one for flag itself (we know it is either -a or --animation) 633 | ;; 634 | *) # unknown flag/switch 635 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 636 | exit 2 637 | ;; 638 | esac 639 | done 640 | 641 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 642 | 643 | # Setting default values 644 | : "${backupDirectory:=~/backups}" 645 | : "${playAnimation:=false}" 646 | 647 | # test array. TODO: remove 648 | echo "${sourcePaths[@]}" 649 | 650 | backupFile=`date -I`.tar.gz 651 | 652 | # test backup file name. TODO: remove 653 | echo "${backupFile}" 654 | 655 | if [ ! -d "${backupDirectory}" ]; then 656 | echo `tput setaf 1`"Error! Backup directory doesn't exist: ${backupDirectory}"`tput sgr0` 657 | exit 3 658 | fi 659 | 660 | ``` 661 | 662 | Test the script and remove test echos. 663 | 664 | ```bash 665 | #!/usr/bin/env bash 666 | 667 | # Title: Backup tool 668 | # Description: Backups defined folders into backups folder in home directory 669 | # Author: Remisa Yousefvand 670 | # Date: 2020-07-07 671 | # Version: 1.0.0 672 | VERSION="1.0.0" 673 | 674 | # Exit codes 675 | # ========== 676 | # 0 no error 677 | # 1 script interrupted 678 | # 2 unknown argument 679 | # 3 backup folder doesn't exist 680 | # TODO: more later 681 | 682 | # assuming all in user home directory 683 | sourcePaths=( 684 | 'Desktop' 685 | '.config' 686 | '.bash_history' 687 | ) 688 | 689 | 690 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 691 | function cleanup () { 692 | echo "cleanup code" 693 | } 694 | 695 | 696 | # CTRL+C event handler 697 | function on_ctrl_c() { 698 | echo # Set cursor to the next line of '^C' 699 | tput cnorm # show cursor. You need this if animation is used. 700 | cleanup # Call cleanup function 701 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 702 | 703 | exit 1 # Don't remove. Use a number (1-255) for error code. 704 | } 705 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 706 | # Register CTRL+C event handler 707 | trap on_ctrl_c SIGINT 708 | 709 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 710 | 711 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 712 | 713 | # Your frames need to have the exact same width and height. 714 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 715 | 716 | IFS='' read -r -d '' frames[1] <<"EOF" 717 | . . 718 | |\_---_/| 719 | / o_O \ 720 | | U | 721 | \ ._I_. / 722 | `-_____-' 723 | EOF 724 | 725 | IFS='' read -r -d '' frames[2] <<"EOF" 726 | . . 727 | |\_---_/| 728 | / O_o \ 729 | | U | 730 | \ ._I_. / 731 | `-_____-' 732 | EOF 733 | 734 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 735 | 736 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 737 | 738 | function help () { 739 | echo " 740 | Usage: ${0} [options] 741 | 742 | Options: 743 | -a, --animation Show animation after 744 | backup completed 745 | -h, --help Show this help message 746 | -o, --output Backup directory 747 | -v, --version Show version info 748 | " 749 | } 750 | 751 | function playAnimation () { 752 | echo "TODO: play animation" 753 | } 754 | 755 | function backup () { 756 | echo `tput setaf 2`Backup started...`tput sgr0` 757 | echo # empty line 758 | currentPath=`pwd` # save current directory 759 | cd "${HOME}" 760 | tar -czvf "${backupDirectory}/${backupFile}" "${sourcePaths[@]}" 761 | cd "${currentPath}" # restore current directory 762 | } 763 | 764 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 765 | 766 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 767 | 768 | while [[ $# > 0 ]]; do 769 | case "${1}" in 770 | -h|--help) 771 | help # call "help" function 772 | exit 0 # we don't process any other argument 773 | ;; 774 | -v|--version) 775 | echo "Backup tool v. ${VERSION}" 776 | exit 0 # we don't process any other argument 777 | ;; 778 | -o|--output) 779 | backupDirectory="${2}" 780 | shift 2 # one for switch and another for its value 781 | ;; 782 | -a|--animation) 783 | playAnimation="true" 784 | shift # shift one for flag itself (we know it is either -a or --animation) 785 | ;; 786 | *) # unknown flag/switch 787 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 788 | exit 2 789 | ;; 790 | esac 791 | done 792 | 793 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 794 | 795 | # Setting default values 796 | : "${backupDirectory:=$HOME/backups}" 797 | : "${playAnimation:=false}" 798 | 799 | # test array. TODO: remove 800 | echo "${sourcePaths[@]}" 801 | 802 | backupFile=`date -I`.tar.gz 803 | 804 | # test backup file name. TODO: remove 805 | echo "${backupFile}" 806 | 807 | if [ ! -d "${backupDirectory}" ]; then 808 | echo `tput setaf 1`"Error! Backup directory doesn't exist: ${backupDirectory}"`tput sgr0` 809 | exit 3 810 | fi 811 | 812 | # call backup function. It has access to global variables. 813 | backup 814 | 815 | ``` 816 | 817 | If user interrupts script we need to delete incomplete `tar.gz` file. In this example `cleanup` function doesn't worth to be written as a separate function (delete the file inside `on_ctrl_c` function) but this is a toy example and usually cleanup code is more than just a single line of code. 818 | 819 | ```bash 820 | #!/usr/bin/env bash 821 | 822 | # Title: Backup tool 823 | # Description: Backups defined folders into backups folder in home directory 824 | # Author: Remisa Yousefvand 825 | # Date: 2020-07-07 826 | # Version: 1.0.0 827 | VERSION="1.0.0" 828 | 829 | # Exit codes 830 | # ========== 831 | # 0 no error 832 | # 1 script interrupted 833 | # 2 unknown argument 834 | # 3 backup folder doesn't exist 835 | # TODO: more later 836 | 837 | # assuming all in user home directory 838 | sourcePaths=( 839 | 'Desktop' 840 | '.config' 841 | '.bash_history' 842 | ) 843 | 844 | 845 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 846 | function cleanup () { 847 | rm "${backupDirectory}/${backupFile}" 848 | } 849 | 850 | 851 | # CTRL+C event handler 852 | function on_ctrl_c() { 853 | echo # Set cursor to the next line of '^C' 854 | tput cnorm # show cursor. You need this if animation is used. 855 | cleanup # Call cleanup function 856 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 857 | 858 | exit 1 # Don't remove. Use a number (1-255) for error code. 859 | } 860 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 861 | # Register CTRL+C event handler 862 | trap on_ctrl_c SIGINT 863 | 864 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 865 | 866 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 867 | 868 | # Your frames need to have the exact same width and height. 869 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 870 | 871 | IFS='' read -r -d '' frames[1] <<"EOF" 872 | . . 873 | |\_---_/| 874 | / o_O \ 875 | | U | 876 | \ ._I_. / 877 | `-_____-' 878 | EOF 879 | 880 | IFS='' read -r -d '' frames[2] <<"EOF" 881 | . . 882 | |\_---_/| 883 | / O_o \ 884 | | U | 885 | \ ._I_. / 886 | `-_____-' 887 | EOF 888 | 889 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 890 | 891 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 892 | 893 | function help () { 894 | echo " 895 | Usage: ${0} [options] 896 | 897 | Options: 898 | -a, --animation Show animation after 899 | backup completed 900 | -h, --help Show this help message 901 | -o, --output Backup directory 902 | -v, --version Show version info 903 | " 904 | } 905 | 906 | function playAnimation () { 907 | echo "TODO: play animation" 908 | } 909 | 910 | function backup () { 911 | echo `tput setaf 2`Backup started...`tput sgr0` 912 | echo # empty line 913 | currentPath=`pwd` # save current directory 914 | cd "${HOME}" 915 | tar -czvf "${backupDirectory}/${backupFile}" "${sourcePaths[@]}" 916 | cd "${currentPath}" # restore current directory 917 | } 918 | 919 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 920 | 921 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 922 | 923 | while [[ $# > 0 ]]; do 924 | case "${1}" in 925 | -h|--help) 926 | help # call "help" function 927 | exit 0 # we don't process any other argument 928 | ;; 929 | -v|--version) 930 | echo "Backup tool v. ${VERSION}" 931 | exit 0 # we don't process any other argument 932 | ;; 933 | -o|--output) 934 | backupDirectory="${2}" 935 | shift 2 # one for switch and another for its value 936 | ;; 937 | -a|--animation) 938 | playAnimation="true" 939 | shift # shift one for flag itself (we know it is either -a or --animation) 940 | ;; 941 | *) # unknown flag/switch 942 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 943 | exit 2 944 | ;; 945 | esac 946 | done 947 | 948 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 949 | 950 | # Setting default values 951 | : "${backupDirectory:=$HOME/backups}" 952 | : "${playAnimation:=false}" 953 | 954 | backupFile=`date -I`.tar.gz 955 | 956 | if [ ! -d "${backupDirectory}" ]; then 957 | echo `tput setaf 1`"Error! Backup directory doesn't exist: ${backupDirectory}"`tput sgr0` 958 | exit 3 959 | fi 960 | 961 | # call backup function. It has access to global variables. 962 | backup 963 | 964 | ``` 965 | 966 | Inside `backup` function we need to make sure `tar` has completed successfully (if `tar` returned `0`). Use `cmd failure check` snippet for that. It turns out we need a new error code for when `tar` fails. Document error code `4` as when `tar` fails. 967 | 968 | ```bash 969 | #!/usr/bin/env bash 970 | 971 | # Title: Backup tool 972 | # Description: Backups defined folders into backups folder in home directory 973 | # Author: Remisa Yousefvand 974 | # Date: 2020-07-07 975 | # Version: 1.0.0 976 | VERSION="1.0.0" 977 | 978 | # Exit codes 979 | # ========== 980 | # 0 no error 981 | # 1 script interrupted 982 | # 2 unknown argument 983 | # 3 backup folder doesn't exist 984 | # 4 backup failed due to tar error 985 | 986 | # assuming all in user home directory 987 | sourcePaths=( 988 | 'Desktop' 989 | '.config' 990 | '.bash_history' 991 | ) 992 | 993 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 994 | function cleanup () { 995 | rm "${backupDirectory}/${backupFile}" 996 | } 997 | 998 | # CTRL+C event handler 999 | function on_ctrl_c() { 1000 | echo # Set cursor to the next line of '^C' 1001 | tput cnorm # show cursor. You need this if animation is used. 1002 | cleanup # Call cleanup function 1003 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 1004 | 1005 | exit 1 # Don't remove. Use a number (1-255) for error code. 1006 | } 1007 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 1008 | # Register CTRL+C event handler 1009 | trap on_ctrl_c SIGINT 1010 | 1011 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 1012 | 1013 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 1014 | 1015 | # Your frames need to have the exact same width and height. 1016 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 1017 | 1018 | IFS='' read -r -d '' frames[1] <<"EOF" 1019 | . . 1020 | |\_---_/| 1021 | / o_O \ 1022 | | U | 1023 | \ ._I_. / 1024 | `-_____-' 1025 | EOF 1026 | 1027 | IFS='' read -r -d '' frames[2] <<"EOF" 1028 | . . 1029 | |\_---_/| 1030 | / O_o \ 1031 | | U | 1032 | \ ._I_. / 1033 | `-_____-' 1034 | EOF 1035 | 1036 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 1037 | 1038 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 1039 | 1040 | function help () { 1041 | echo " 1042 | Usage: ${0} [options] 1043 | 1044 | Options: 1045 | -a, --animation Show animation after 1046 | backup completed 1047 | -h, --help Show this help message 1048 | -o, --output Backup directory 1049 | -v, --version Show version info 1050 | " 1051 | } 1052 | 1053 | function playAnimation () { 1054 | echo "TODO: play animation" 1055 | } 1056 | 1057 | function backup () { 1058 | echo `tput setaf 2`Backup started...`tput sgr0` 1059 | echo # empty line 1060 | currentPath=`pwd` # save current directory 1061 | cd "${HOME}" 1062 | tar -czvf "${backupDirectory}/${backupFile}" "${sourcePaths[@]}" 1063 | 1064 | if [[ $? != 0 ]]; then 1065 | echo `tput setaf 1`Unknown error. Backup failed!`tput sgr0` 1066 | cd "${currentPath}" # restore current directory 1067 | exit 4 1068 | fi 1069 | 1070 | cd "${currentPath}" # restore current directory 1071 | } 1072 | 1073 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 1074 | 1075 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 1076 | 1077 | while [[ $# > 0 ]]; do 1078 | case "${1}" in 1079 | -h|--help) 1080 | help # call "help" function 1081 | exit 0 # we don't process any other argument 1082 | ;; 1083 | -v|--version) 1084 | echo "Backup tool v. ${VERSION}" 1085 | exit 0 # we don't process any other argument 1086 | ;; 1087 | -o|--output) 1088 | backupDirectory="${2}" 1089 | shift 2 # one for switch and another for its value 1090 | ;; 1091 | -a|--animation) 1092 | playAnimation="true" 1093 | shift # shift one for flag itself (we know it is either -a or --animation) 1094 | ;; 1095 | *) # unknown flag/switch 1096 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 1097 | exit 2 1098 | ;; 1099 | esac 1100 | done 1101 | 1102 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 1103 | 1104 | # Setting default values 1105 | : "${backupDirectory:=$HOME/backups}" 1106 | : "${playAnimation:=false}" 1107 | 1108 | backupFile=`date -I`.tar.gz 1109 | 1110 | if [ ! -d "${backupDirectory}" ]; then 1111 | echo `tput setaf 1`"Error! Backup directory doesn't exist: ${backupDirectory}"`tput sgr0` 1112 | exit 3 1113 | fi 1114 | 1115 | # call backup function. It has access to global variables. 1116 | backup 1117 | 1118 | ``` 1119 | 1120 | Finally play the animation if flag `-a` or `--animation` is passed to script. Use `if string =` snippet and check if `playAnimation` variable is equal to `true`. 1121 | 1122 | For playing animation we need the `animate` function. Inside `functions` region use `fn animation animate` snippet to add this function. As this function documentation says we need to comment out `CTRL+C` event handler because we already have `tput cnorm` in `on_ctrl_c` function which makes cursor visible. Now we can call `animate` function at the end of our script. Use `fx animation animate` snippet to call `animate` function and pass it arguments. 1123 | 1124 | Here we face a new problem. To stop animation user needs to press `CTRL+C` but currently we consider this as an incomplete backup. We need a variable to ignore this if backup is already successfully completed (define `backupSuccess` variable). Here is the [final script](backup.sh). 1125 | 1126 | ```bash 1127 | #!/usr/bin/env bash 1128 | 1129 | # Title: Backup tool 1130 | # Description: Backups defined folders into backups folder in home directory 1131 | # Author: Remisa Yousefvand 1132 | # Date: 2020-07-07 1133 | # Version: 1.0.0 1134 | 1135 | # Exit codes 1136 | # ========== 1137 | # 0 no error 1138 | # 1 script interrupted 1139 | # 2 unknown argument 1140 | # 3 backup folder doesn't exist 1141 | # 4 backup failed due to tar error 1142 | 1143 | # >>>>>>>>>>>>>>>>>>>>>>>> variables >>>>>>>>>>>>>>>>>>>>>>>> 1144 | 1145 | VERSION="1.0.0" 1146 | 1147 | # assuming all in user home directory 1148 | sourcePaths=( 1149 | 'Desktop' 1150 | '.config' 1151 | '.bash_history' 1152 | ) 1153 | 1154 | backupSuccess="false" 1155 | 1156 | # <<<<<<<<<<<<<<<<<<<<<<<< variables <<<<<<<<<<<<<<<<<<<<<<<< 1157 | 1158 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 1159 | function cleanup () { 1160 | rm "${backupDirectory}/${backupFile}" 1161 | } 1162 | 1163 | 1164 | # CTRL+C event handler 1165 | function on_ctrl_c() { 1166 | echo # Set cursor to the next line of '^C' 1167 | tput cnorm # show cursor. You need this if animation is used. 1168 | 1169 | if [ "${backupSuccess}" = "true" ]; then 1170 | exit 0 1171 | else 1172 | cleanup # Call cleanup function 1173 | echo `tput setaf 1`Backup canceled by user!`tput sgr0` 1174 | exit 1 # Don't remove. Use a number (1-255) for error code. 1175 | fi 1176 | } 1177 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 1178 | # Register CTRL+C event handler 1179 | trap on_ctrl_c SIGINT 1180 | 1181 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 1182 | 1183 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 1184 | 1185 | # Your frames need to have the exact same width and height. 1186 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 1187 | 1188 | IFS='' read -r -d '' frames[1] <<"EOF" 1189 | . . 1190 | |\_---_/| 1191 | / o_O \ 1192 | | U | 1193 | \ ._I_. / 1194 | `-_____-' 1195 | EOF 1196 | 1197 | IFS='' read -r -d '' frames[2] <<"EOF" 1198 | . . 1199 | |\_---_/| 1200 | / O_o \ 1201 | | U | 1202 | \ ._I_. / 1203 | `-_____-' 1204 | EOF 1205 | 1206 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 1207 | 1208 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 1209 | 1210 | function help () { 1211 | echo " 1212 | Usage: ${0} [options] 1213 | 1214 | Options: 1215 | -a, --animation Show animation after 1216 | backup completed 1217 | -h, --help Show this help message 1218 | -o, --output Backup directory 1219 | -v, --version Show version info 1220 | " 1221 | } 1222 | 1223 | function playAnimation () { 1224 | echo "TODO: play animation" 1225 | } 1226 | 1227 | function backup () { 1228 | echo `tput setaf 2`Backup started...`tput sgr0` 1229 | echo # empty line 1230 | currentPath=`pwd` # save current directory 1231 | cd "${HOME}" 1232 | tar -czvf "${backupDirectory}/${backupFile}" "${sourcePaths[@]}" 1233 | 1234 | if [[ $? != 0 ]]; then 1235 | echo `tput setaf 1`Unknown error. Backup failed!`tput sgr0` 1236 | cd "${currentPath}" # restore current directory 1237 | exit 4 1238 | fi 1239 | 1240 | cd "${currentPath}" # restore current directory 1241 | } 1242 | 1243 | # Usage: animate frames_array interval 1244 | function animate () { 1245 | local frames=("$@") 1246 | ((last_index=${#frames[@]} - 1)) 1247 | local interval=${frames[last_index]} 1248 | unset frames[last_index] 1249 | 1250 | # Comment out next two lines if you are using CTRL+C event handler. 1251 | # trap 'tput cnorm; echo' EXIT 1252 | # trap 'exit 127' HUP INT TERM 1253 | 1254 | tput civis # hide cursor 1255 | tput sc # save cursor position 1256 | 1257 | while true; do 1258 | for frame in "${frames[@]}"; do 1259 | tput rc # restore cursor position 1260 | echo "${frame}" 1261 | sleep "${interval}" 1262 | done 1263 | done 1264 | } 1265 | 1266 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 1267 | 1268 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 1269 | 1270 | while [[ $# > 0 ]]; do 1271 | case "${1}" in 1272 | -h|--help) 1273 | help # call "help" function 1274 | exit 0 # we don't process any other argument 1275 | ;; 1276 | -v|--version) 1277 | echo "Backup tool v. ${VERSION}" 1278 | exit 0 # we don't process any other argument 1279 | ;; 1280 | -o|--output) 1281 | backupDirectory="${2}" 1282 | shift 2 # one for switch and another for its value 1283 | ;; 1284 | -a|--animation) 1285 | playAnimation="true" 1286 | shift # shift one for flag itself (we know it is either -a or --animation) 1287 | ;; 1288 | *) # unknown flag/switch 1289 | echo `tput setaf 1`Error! Unknown argument: "${1}"`tput sgr0` 1290 | exit 2 1291 | ;; 1292 | esac 1293 | done 1294 | 1295 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 1296 | 1297 | # Setting default values 1298 | : "${backupDirectory:=$HOME/backups}" 1299 | : "${playAnimation:=false}" 1300 | 1301 | backupFile=`date -I`.tar.gz 1302 | 1303 | if [ ! -d "${backupDirectory}" ]; then 1304 | echo `tput setaf 1`"Error! Backup directory doesn't exist: ${backupDirectory}"`tput sgr0` 1305 | exit 3 1306 | fi 1307 | 1308 | # call backup function. It has access to global variables. 1309 | backup 1310 | 1311 | echo `tput setaf 4`Backup complete.`tput sgr0` 1312 | backupSuccess="true" 1313 | 1314 | if [ "${playAnimation}" = "true" ]; then 1315 | animate "${frames[@]}" 0.5 1316 | fi 1317 | 1318 | ``` 1319 | 1320 | Congratulations! You wrote a complete shell script to automate some task. This is an investment of your time. You consumed more time now to learn shell scripting so you can automate repetitive tasks and consume less time in the future. 1321 | -------------------------------------------------------------------------------- /samples/backup-tool/backup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # NOTE: This script has been refactored without testing. In case of error open an issue. 4 | 5 | # Title: Backup tool 6 | # Description: Backups defined folders into backups folder in home directory 7 | # Author: Remisa Yousefvand 8 | # Date: 2020-07-07 9 | # Version: 1.0.0 10 | 11 | # Exit codes 12 | # ========== 13 | # 0 no error 14 | # 1 script interrupted 15 | # 2 unknown argument 16 | # 3 backup folder doesn't exist 17 | # 4 backup failed due to tar error 18 | 19 | # >>>>>>>>>>>>>>>>>>>>>>>> variables >>>>>>>>>>>>>>>>>>>>>>>> 20 | 21 | VERSION="1.0.0" 22 | 23 | # assuming all in user home directory 24 | sourcePaths=( 25 | 'Desktop' 26 | '.config' 27 | '.bash_history' 28 | ) 29 | 30 | backupSuccess="false" 31 | 32 | # <<<<<<<<<<<<<<<<<<<<<<<< variables <<<<<<<<<<<<<<<<<<<<<<<< 33 | 34 | # >>>>>>>>>>>>>>>>>>>>>>>> event handling >>>>>>>>>>>>>>>>>>>>>>>> 35 | function cleanup () { 36 | rm "${backupDirectory}/${backupFile}" 37 | } 38 | 39 | 40 | # CTRL+C event handler 41 | function on_ctrl_c() { 42 | echo # Set cursor to the next line of '^C' 43 | tput cnorm # show cursor. You need this if animation is used. 44 | 45 | if [ "${backupSuccess}" = "true" ]; then 46 | exit 0 47 | else 48 | cleanup # Call cleanup function 49 | echo $(tput setaf 1)Backup canceled by user!$(tput sgr0) 50 | exit 1 # Don't remove. Use a number (1-255) for error code. 51 | fi 52 | } 53 | # Put this line at the beginning of your script entry point (after functions used by event handlers). 54 | # Register CTRL+C event handler 55 | trap on_ctrl_c SIGINT 56 | 57 | # <<<<<<<<<<<<<<<<<<<<<<<< event handling <<<<<<<<<<<<<<<<<<<<<<<< 58 | 59 | # >>>>>>>>>>>>>>>>>>>>>>>> animation frames >>>>>>>>>>>>>>>>>>>>>>>> 60 | 61 | # Your frames need to have the exact same width and height. 62 | # If they are different in size, fill unused space with `space`s (no `TAB`s). 63 | 64 | IFS='' read -r -d '' frames[1] <<"EOF" 65 | . . 66 | |\_---_/| 67 | / o_O \ 68 | | U | 69 | \ ._I_. / 70 | `-_____-' 71 | EOF 72 | 73 | IFS='' read -r -d '' frames[2] <<"EOF" 74 | . . 75 | |\_---_/| 76 | / O_o \ 77 | | U | 78 | \ ._I_. / 79 | `-_____-' 80 | EOF 81 | 82 | # <<<<<<<<<<<<<<<<<<<<<<<< animation frames <<<<<<<<<<<<<<<<<<<<<<<< 83 | 84 | # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>> 85 | 86 | function help () { 87 | echo " 88 | Usage: ${0} [options] 89 | 90 | Options: 91 | -a, --animation Show animation after 92 | backup completed 93 | -h, --help Show this help message 94 | -o, --output Backup directory 95 | -v, --version Show version info 96 | " 97 | } 98 | 99 | function backup () { 100 | echo $(tput setaf 2)Backup started...$(tput sgr0) 101 | echo # empty line 102 | currentPath=$(pwd) # save current directory 103 | cd "${HOME}" 104 | tar -czvf "${backupDirectory}/${backupFile}" "${sourcePaths[@]}" 105 | 106 | if [[ $? != 0 ]]; then 107 | echo $(tput setaf 1)Unknown error. Backup failed!$(tput sgr0) 108 | cd "${currentPath}" # restore current directory 109 | exit 4 110 | fi 111 | 112 | cd "${currentPath}" # restore current directory 113 | } 114 | 115 | # Usage: animate frames_array interval 116 | function animate () { 117 | local frames=("$@") 118 | ((last_index=${#frames[@]} - 1)) 119 | local interval=${frames[last_index]} 120 | unset frames[last_index] 121 | 122 | # Comment out next two lines if you are using CTRL+C event handler. 123 | # trap 'tput cnorm; echo' EXIT 124 | # trap 'exit 127' HUP INT TERM 125 | 126 | tput civis # hide cursor 127 | tput sc # save cursor position 128 | 129 | while true; do 130 | for frame in "${frames[@]}"; do 131 | tput rc # restore cursor position 132 | echo "${frame}" 133 | sleep "${interval}" 134 | done 135 | done 136 | } 137 | 138 | # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<< 139 | 140 | # >>>>>>>>>>>>>>>>>>>>>>>> argument parsing >>>>>>>>>>>>>>>>>>>>>>>> 141 | 142 | while [[ $# > 0 ]]; do 143 | case "${1}" in 144 | -h|--help) 145 | help # call "help" function 146 | exit 0 # we don't process any other argument 147 | ;; 148 | -v|--version) 149 | echo "Backup tool v. ${VERSION}" 150 | exit 0 # we don't process any other argument 151 | ;; 152 | -o|--output) 153 | backupDirectory="${2}" 154 | shift 2 # one for switch and another for its value 155 | ;; 156 | -a|--animation) 157 | playAnimation="true" 158 | shift # shift one for flag itself (we know it is either -a or --animation) 159 | ;; 160 | *) # unknown flag/switch 161 | echo $(tput setaf 1)Error! Unknown argument: "${1}"$(tput sgr0) 162 | exit 2 163 | ;; 164 | esac 165 | done 166 | 167 | # <<<<<<<<<<<<<<<<<<<<<<<< argument parsing <<<<<<<<<<<<<<<<<<<<<<<< 168 | 169 | # Setting default values 170 | : "${backupDirectory:=$HOME/backups}" 171 | : "${playAnimation:=false}" 172 | 173 | # Another way to handle "default vaules" is defining them before argument parsing 174 | # in "variables" and setting them to default values. Later in argument parsing you 175 | # can override default values when user has provided an alternative value. 176 | 177 | backupFile=$(date -I).tar.gz 178 | 179 | # Check if "backup directory" exists. 180 | if [ ! -d "${backupDirectory}" ]; then 181 | echo $(tput setaf 1)"Error! Backup directory doesn't exist: ${backupDirectory}"$(tput sgr0) 182 | exit 3 183 | fi 184 | 185 | # call backup function. It has access to global variables. 186 | backup 187 | 188 | echo $(tput setaf 4)Backup complete.$(tput sgr0) 189 | backupSuccess="true" 190 | 191 | if [ "${playAnimation}" = "true" ]; then 192 | animate "${frames[@]}" 0.5 193 | fi 194 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | --------------------------------------------------------------------------------