├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── awesomebot.yml │ └── mega-linter.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── bin ├── 600 ├── 644 ├── 700 ├── 750 ├── 755 ├── $ ├── ansi2html ├── ascii-bar ├── bindiff ├── block-until-internet ├── change-extension ├── clean-whiteboard-picture ├── csr-decode ├── cursor-hide ├── cursor-show ├── datauri ├── datestamp ├── decode-cert ├── dedupe-in-order ├── diff-summary ├── dns-resolvers ├── dumpdns ├── exiftool ├── explainshell ├── extractFromRPM ├── find-in-files ├── fix-zsh-history ├── get-distro ├── get-site-cert ├── gtouch ├── gxpr ├── headers ├── hex-to-bin ├── hide-cursor ├── html2markdown ├── http-debug ├── http-headers ├── http-reflect ├── human-path ├── human-time ├── icorrupt ├── iflip ├── ipaddresses ├── is-remote-session ├── jira ├── jmemstat ├── json-to-yaml ├── json2yaml ├── jsondiff ├── lineprof ├── local-ip-address ├── local-ipv4-address ├── local-ipv6-address ├── ls-open-ports ├── ls-sockets ├── ls-tcp-sockets ├── ls-udp-sockets ├── lsof-unlinked ├── memcached-tool ├── memcached-top ├── mfs-instatrash ├── middle ├── mtr-url ├── murder ├── name-tab ├── name-window ├── nanotime ├── newscript ├── openports ├── p7b-decode ├── pidpwd ├── ping5 ├── pjson ├── plot ├── port-listened-by ├── port-listeners-ipv4 ├── port-listeners-ipv6 ├── procs-for-path ├── pushover ├── pydoc ├── ramdisk ├── random-password ├── randsleep ├── read-windows-key-from-bios ├── relocate-virtualenv ├── remote-packet-capture ├── remote-shark ├── repair-zsh-history ├── retry ├── rpmburst ├── seq ├── shovel-gpg-keys ├── show-cursor ├── snag-dl ├── solo ├── ssh-remove-known-host ├── ssl-cert-check ├── steal ├── strip-ansi-codes ├── tableflip ├── title-text ├── urldecode ├── urlencode ├── vbox ├── whap ├── what ├── wtfis ├── yaml-to-json ├── yaml2json ├── yesterday └── yq ├── completions └── _mosh ├── jpb.plugin.zsh └── lib └── oui.txt /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Motivation and Context 4 | 5 | 6 | 7 | # Description 8 | 9 | 10 | # How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | # Rights 16 | 17 | - [ ] This repository is covered by the Apache 2.0 license (except where noted in individual scripts' source), and I agree that this PR contribution is also subject to the Apache 2.0 license. 18 | 19 | # Types of changes 20 | 21 | 22 | - [ ] Adding or updating utility script(s) 23 | - [ ] Add/update function in `jpb.plugin.zsh` 24 | - [ ] Adding link(s) to external resources 25 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 26 | - [ ] Bug fix (non-breaking change which fixes an issue) 27 | - [ ] Documentation Changes 28 | - [ ] Test updates 29 | 30 | # Checklist 31 | 32 | 33 | 34 | ## General 35 | 36 | - [ ] All new and existing tests passed. 37 | - [ ] I have added tests to cover my changes. 38 | - [ ] My change requires a change to the documentation. 39 | - [ ] I have updated Readme.md to give credit to the script author 40 | - [ ] I have updated Readme.md to describe what the script does 41 | - [ ] If a new script is not using an Apache 2.0 license, there's a link in the script source saying what license it _is_ under. 42 | 43 | ## Scripts 44 | 45 | - [ ] I have run `pylint` on all python files touched in my branch. 46 | - [ ] I have run `rubocop` on all ruby files touched in my branch. 47 | - [ ] I have run `shellcheck` on all shell scripts touched in my branch. 48 | - [ ] All scripts touched/added in this PR have valid shebang lines and are marked executable. 49 | - [ ] Any scripts added in my PR do not include language extensions in their names - no `foo.sh`, `foo.rb` or `foo.py`. We do not want to have to change other scripts just because something gets rewritten in a better fitting language. 50 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use `allow` to specify which dependencies to maintain 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | 11 | - package-ecosystem: "pip" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | -------------------------------------------------------------------------------- /.github/workflows/awesomebot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Check links in README.md 3 | 4 | on: 5 | push: 6 | branches: [ '*' ] 7 | pull_request: 8 | branches: [ '*' ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: docker://dkhamsing/awesome_bot:latest 18 | with: 19 | args: /github/workspace/README.md --allow 403,418,429,500,501,502,503,504,509,521 --allow-dupe --request-delay 1 --allow-redirect --white-list https://ipfs.io,slideshare 20 | -------------------------------------------------------------------------------- /.github/workflows/mega-linter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ########################### 3 | ########################### 4 | ## Linter GitHub Actions ## 5 | ########################### 6 | ########################### 7 | name: Lint Code Base 8 | 9 | # 10 | # Documentation: 11 | # https://help.github.com/en/articles/workflow-syntax-for-github-actions 12 | # 13 | 14 | ############################# 15 | # Start the job on all push # 16 | ############################# 17 | on: 18 | push: 19 | branches-ignore: [main] 20 | # Remove the line above to run when pushing to main 21 | pull_request: 22 | branches: [main] 23 | 24 | ############### 25 | # Set the Job # 26 | ############### 27 | jobs: 28 | build: 29 | # Name the Job 30 | name: Megalint Code Base 31 | # Set the agent to run on 32 | runs-on: ubuntu-latest 33 | 34 | ################## 35 | # Load all steps # 36 | ################## 37 | steps: 38 | ########################## 39 | # Checkout the code base # 40 | ########################## 41 | - name: Checkout Code 42 | uses: actions/checkout@v4 43 | with: 44 | # Full git history is needed to get a proper list of changed files within `super-linter` 45 | fetch-depth: 0 46 | 47 | ################################ 48 | # Run Linter against code base # 49 | ################################ 50 | - name: Lint Code Base 51 | uses: nvuillam/mega-linter@v8 52 | env: 53 | DEFAULT_BRANCH: main 54 | DISABLE_LINTERS: SPELL_CSPELL,MARKDOWN_MARKDOWN_LINK_CHECK,REPOSITORY_CHECKOV,REPOSITORY_TRIVY,REPOSITORY_GRYPE,SPELL_LYCHEE,REPOSITORY_DEVSKIM 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | ACTION_ACTIONLINT_DISABLE_ERRORS: true 57 | REPOSITORY_CHECKOV_DISABLE_ERRORS: true 58 | REPOSITORY_KICS_DISABLE_ERRORS: true 59 | VALIDATE_ALL_CODEBASE: false 60 | YAML_YAMLLINT_DISABLE_ERRORS: true 61 | 62 | # Upload Mega-Linter artifacts. They will be available on Github action page "Artifacts" section 63 | - name: Archive production artifacts 64 | if: ${{ success() }} || ${{ failure() }} 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: Mega-Linter reports 68 | path: | 69 | report 70 | mega-linter.log 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.*.un~ 2 | *.pyc 3 | *.pyo 4 | *.sublime-project 5 | *.sublime-workspace 6 | .DS_Store 7 | .idea 8 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v5.0.0 5 | hooks: 6 | - id: check-merge-conflict 7 | - id: check-yaml 8 | - id: end-of-file-fixer 9 | - id: trailing-whitespace 10 | - id: forbid-submodules 11 | - id: mixed-line-ending 12 | - repo: https://github.com/igorshubovych/markdownlint-cli 13 | rev: v0.45.0 14 | hooks: 15 | - id: markdownlint-fix 16 | args: ["--ignore", "LICENSE.md", "--disable", "~MD013"] 17 | - repo: https://github.com/thlorenz/doctoc 18 | rev: v2.2.0 19 | hooks: 20 | - id: doctoc 21 | args: ["--update-only"] 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/license/Apache-2.0) 2 | [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Funixorn%2Fjpb.zshplugin%2Fbadge%3Fref%3Dmain&style=plastic)](https://actions-badge.atrox.dev/unixorn/jpb.zshplugin/goto?ref=main) 3 | ![Awesomebot](https://github.com/unixorn/jpb.zshplugin/actions/workflows/awesomebot.yml/badge.svg) 4 | ![Mega-Linter](https://github.com/unixorn/jpb.zshplugin/actions/workflows/mega-linter.yml/badge.svg) 5 | 6 | # jpb.zshplugin 7 | 8 | This is a ZSH plugin usable with [zgenom](https://github.com/jandamm/zgenom) and other frameworks compatible with [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) to easily load some of my tool scripts. 9 | 10 | This contains miscellaneous tool scripts and aliases that are not specific enough for one of my more targeted ZSH plugins. 11 | 12 | These scripts are personal collection of helper scripts, so while I try to make them useful for other people, they're in a constant state of flux as I make them suit my needs. 13 | 14 | 15 | 16 | 17 | ## Table of Contents 18 | 19 | - [Installing](#installing) 20 | - [Antigen](#antigen) 21 | - [Zgenom](#zgenom) 22 | - [Without using a framework](#without-using-a-framework) 23 | - [Credits](#credits) 24 | - [Other useful ZSH plugins](#other-useful-zsh-plugins) 25 | 26 | 27 | 28 | ## Installing 29 | 30 | ### Antigen 31 | 32 | add `antigen bundle unixorn/jpb.zshplugin` to your `.zshrc` 33 | 34 | ### Zgenom 35 | 36 | add `zgenom load unixorn/jpb.zshplugin` to your `.zshrc` with your other `zgenom load` commands. 37 | 38 | ### Without using a framework 39 | 40 | 1. `git clone` this repository, then add its bin directory to your `$PATH`. 41 | 2. Add `source /path/to/here/jpb.plugin.zsh` to your `.zshrc` file. 42 | 43 | The scripts in this collection don't actually require you to be using ZSH as your login shell, they're being distributed as a plugin compatible with [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) because it's convenient for me. 44 | 45 | ## Credits 46 | 47 | | Script | Original Source | Description | 48 | | ------ | --------------- | ----------- | 49 | | `$` | Jordan Sissel's [dotfiles](https://github.com/jordansissel/dotfiles/blob/master/.zshrc) | A shim so that pasting a command example with a leading `$` will just work instead of failing. | 50 | | `ansi2html` | Mislav Marohnić's [dotfiles](https://github.com/mislav/dotfiles) | Convert terminal color ANSI escape sequences to HTML. | 51 | | `ascii-bar` | Wookayin's [dotfiles](https://github.com/wookayin/dotfiles/blob/master/bin/ascii-bar) | Draw an ascii bar figure from the percentage value read from `STDIN` | 52 | | `bindiff` | jpb@unixorn.net | Hexdumps two files and diffs the output | 53 | | `change-extension` | ? | Changes file extensions | 54 | | `clean-whiteboard-picture` | [https://gist.github.com/lelandbatey/8677901](https://gist.github.com/lelandbatey/8677901) | Cleans up pictures of whiteboards and pumps up contrast | 55 | | `cursor-hide` | jpb@unixorn.net | Hides cursor in iTerm2 | 56 | | `cursor-show` | jpb@unixorn.net | Enables cursor visibility in iTerm2 if a program exits without setting it visible again | 57 | | `datauri` | Alrra's [dotfiles](https://github.com/alrra/dotfiles/blob/main/src/shell/bash_functions) | Converts a file to a data URI | 58 | | `dedupe-in-order` | `awk` oneliner | Use awk to dedupe a file, outputting the lines in order. Unlike `uniq`, doesn't only dedupe adjacent lines. | 59 | | `diff-summary` | Gary Bernhardt's [dotfiles](https://github.com/garybernhardt/dotfiles/blob/main/bin/gn) | Prints a summary of piped diff files or `git diff` output | 60 | | `dumpdns` | ? | Dump DNS records for a domain | 61 | | `exiftool` | jpb@unixorn.net | Shows or removes a file's exif metadata | 62 | | `explainshell` | jpb@unixorn.net | Uses https://explainshell.com to explain shell commands. | 63 | | `extractFromRPM` | jpb@unixorn.net | Extracts files from an rpm | 64 | | `find-in-files` | [Boost Your Command Line Productivity With fzf](https://betterprogramming.pub/boost-your-command-line-productivity-with-fuzzy-finder-985aa162ba5d) | Combines `ripgrep` and `fzf` | 65 | | `fix-zsh-history` | jpb@unixorn.net | Fixes a corrupted `.zsh_history` file | 66 | | `get-distro` | [https://github.com/blueyed/dotfiles/](https://github.com/blueyed/dotfiles/blob/master/usr/bin/get_distro) | Dumps info about the distro of the linux system you're on | 67 | | `get-site-cert` | ? | Download the SSL cert from a site | 68 | | `gxpr` | [brutasse's dotfiles](https://github.com/brutasse/dotfiles/blob/master/bin/gxpr). | Uses Google and or Wolfram Alpha to evaluate expressions. Requires `URI::Escape` to be installed with `cpan`. | 69 | | `headers` | Zach Holman's [dotfiles](https://github.com/holman/dotfiles/blob/master/bin/headers) | Gets the HTTP headers from a server | 70 | | `hex-to-bin` | jpb@unixorn.net | Converts a hexstring to a binary stream | 71 | | `html2markdown` | [https://github.com/realpython/python-scripts/](https://github.com/realpython/python-scripts/blob/master/scripts/14_html_to_markdown.sh) | Convert all HTML files in a single directory to Markdown | 72 | | `http_debug` | | Dump debug info for a URL | 73 | | `http_headers` | | Dump http headers for a URL | 74 | | `human-path` | coffeeops slack `#commandline-fu` channel | Print `$PATH` with one entry per line to make it easier for humans to tell if something is missing | 75 | | `human-time` | | Converts integer seconds into human-understandable time. `human-time 88000` will print `1d 26m 40s` | 76 | | `icorrupt` | [twirrim/icorrupt](https://gist.github.com/twirrim/b87d08a2436437c8ff97a65877586182) | Corrupts a text string | 77 | | `iflip` | [twirrim/iflip](https://github.com/twirrim/iflip/blob/master/iflip) | Tableflips a text string | 78 | | `ipaddresses` | jpb@unixorn.net | Dumps all the ip addresses for the host | 79 | | `is-remote-session` | jpb@unixorn.net | Exits 0 if you're in an `ssh` remote session, 1 otherwise | 80 | | `jira` | jpb@unixorn.net | Opens a jira ticket from the command-line | 81 | | `jmemstat` | [majk1's shellrc](https://github.com/majk1/shellrc/blob/master/utils/jmemstat.sh) | Displays a memory information summary for a java process | 82 | | `json2yaml` / `json-to-yaml` | jpb@unixorn.net | Converts JSON to YAML | 83 | | `jsondiff` | ? | Diff JSON files and cope with key-order differences by processing with json.tool | 84 | | `lineprof` | Mislav Marohnić's [dotfiles](https://github.com/mislav/dotfiles) | Annotates each line of input with the number of milliseconds elapsed since the last line. Useful for figuring out slow points of output-producing programs. | 85 | | `local-ip-address` | jpb@unixorn.net | Print local IP v4 address | 86 | | `local-ipv6-address` | jpb@unixorn.net | Print local IP v6 address | 87 | | `ls-open-ports` | jpb@unixorn.net | List open ports | 88 | | `ls-sockets` | jpb@unixorn.net | List open sockets | 89 | | `ls-tcp-sockets` | jpb@unixorn.net | List open tcp sockets | 90 | | `ls-udp-sockets` | jpb@unixorn.net | List open udp sockets | 91 | | `lsof-unlinked` | [ludios/ubuntils/](https://github.com/ludios/ubuntils/blob/master/bin/lsof-unlinked) | List all open files (but not mapped files) that have been unlinked. | 92 | | `memcached-tool` | Brad Fitzpatrick | stats/management tool for memcached | 93 | | `memcached-top` | [http://code.google.com/p/memcache-top/](http://code.google.com/p/memcache-top/) | Dumps basic `memcached` stats similarly to `top` | 94 | | `middle` | [@Hefeweizen](https://github.com/Hefeweizen) | Snips lines out of the the middle of a file and dumps them to stdout | 95 | | `mtr-url` | ? | Parses hostname from a URL, then does a `mtr` to it. | 96 | | `murder` | [Anonymous Gist](https://gist.github.com/anonymous/32b1e619bc9e7fbe0eaa#!/bin/bash) | Takes a list of PIDs and ends the processes through increasingly rude means. | 97 | | `name-window` | jpb@unixorn.net | Names a terminal window/tab by sending escape codes. | 98 | | `nanotime` | jpb@unixorn.net | Times a process and gives you results in milliseconds | 99 | | `newscript` | jpb@unixorn.net | Creates a new script from a template and does `chmod 755` on it. | 100 | | `openports` | jpb@unixorn.net | Wraps `ss` and makes a prettier list of open ports. | 101 | | `pidpwd` | jpb@unixorn.net | Find the pwd of a given pid. Only works on linux since it requires `/proc` | 102 | | `pjson` | [https://coderwall.com/](https://coderwall.com/p/hwu5uq?i=9&p=1&q=sort%3Ascore+desc&t%5B%5D=zsh) | Prettify json files | 103 | | `plot` | katef's [gist](https://gist.github.com/katef/fb4cb6d47decd8052bd0e8d88c03a102) | Draw a graph in the terminal | 104 | | `port-listeners-ipv{4,6}` | jpb@unixorn.net | Show what programs are listening to a given port | 105 | | `port-listened-by}` | jpb@unixorn.net | Show what programs are listening to a given port | 106 | | `pydoc` | Hangops Slack | Look something up on [docs.python.org](https://docs.python.org) and opens it in your default browser | 107 | | `random-password` | jpb@unixorn.net | Generate a random password. If no argument, assume 32 character length | 108 | | `randsleep` | jpb@unixorn.net | Sleep a random number of seconds | 109 | | `relocate-virtualenv` | Gary Josack's [scripts](https://github.com/gmjosack/scripts) repository | This is a simple script to clean up links and references in a python virtualenv that has been relocated. | 110 | | `remote-packet-capture` | jpb@unixorn.net | Run `tcpdump` on a remote host and pipe it to local `wireshark` | 111 | | `retry` | jpb@unixorn.net | Re-run a command until it exits successfully. Waits `$DELAY` seconds between attempts. | 112 | | `seq` | Dave Taylor's [blog](https://www.askdavetaylor.com/step_through_count_numeric_values_bash_shell_script/) | Generates integer values from low...high similar to `range` in better programming languages | 113 | | `snag-dl` | ? | Moves the most recent file in `~/Downloads` into the current directory | 114 | | `solo` | Timothy Kay's `solo` script | Prevents a program from running more than one copy at a time. | 115 | | `ssh-remove-known-host` | jpb@unixorn.net | Helper script to remove a known hosts entry. I can never remember the command for removing a known_hosts entry, and the new format makes it more pain in the ass than just editing and searching for the ip/hostname. | 116 | | `steal` | jpb@unixorn.net | Helper for quickly resetting ownership of files you created with the wrong userid | 117 | | `strip-ansi-codes` | jpb@unixorn.net | Strips the ANSI codes from STDIN. Makes grepping through things like jenkins logs considerably less painful | 118 | | `tableflip` | [hangops](https://hangops.slack.com) slack | Prints a tableflip animation. | 119 | | `title-text` | jpb@unixorn.net | Set the terminal title in any application (like iTerm 2 or Terminal on macOS) that supports VT100 escape sequences. | 120 | | `urldecode` | jpb@unixorn.net | Decode an url string | 121 | | `urlencode` | jpb@unixorn.net | Encode a string to an url parameter | 122 | | `vbox` | jpb@unixorn.net | Opens VirtualBox | 123 | | `wtfis` | jpb@unixorn.net | Looks something up on [cheat.sh](http://cheat.sh) | 124 | | `yaml2json` / `yaml-to-json` | jpb@unixorn.net | Converts yaml to json | 125 | | `yq` | jpb@unixorn.net | Run `yq` in a container to minimize the things directly installed locally | 126 | 127 | ## Other useful ZSH plugins 128 | 129 | I also maintain the [awesome-zsh-plugins](https://github.com/unixorn/awesome-zsh-plugins) list of ZSH frameworks, plugins and themes. 130 | -------------------------------------------------------------------------------- /bin/$: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # From: https://github.com/jordansissel/dotfiles/blob/master/.zshrc 4 | # 5 | # Lots of command examples (especially heroku) lead command docs with '$' which 6 | # make it kind of annoying to copy/paste, especially when there's multiple 7 | # commands to copy. 8 | # 9 | # This hacks around the problem by making a '$' command that simply runs 10 | # whatever arguments are passed to it. So you can copy 11 | # '$ echo hello world' 12 | # and it will run 'echo hello world' 13 | 14 | exec "$@" 15 | -------------------------------------------------------------------------------- /bin/600: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | exec chmod 600 $* 4 | -------------------------------------------------------------------------------- /bin/644: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | exec chmod 644 $* 4 | -------------------------------------------------------------------------------- /bin/700: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | exec chmod 700 $* 4 | -------------------------------------------------------------------------------- /bin/750: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | exec chmod 750 $* 4 | -------------------------------------------------------------------------------- /bin/755: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | exec chmod 755 $* 4 | -------------------------------------------------------------------------------- /bin/ansi2html: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Usage: ansi2html [-w] [--css] 4 | # 5 | # -w : wrap HTML output in PRE tags 6 | # -css : include CSS styles 7 | # 8 | # Convert terminal color ANSI escape sequences to HTML. 9 | # 10 | # Examples: 11 | # 12 | # $ git log --oneline --decorate -10 --color | ansi2html 13 | # 14 | # $ ruby -e '30.upto(37) {|n| puts "\e[#{n}mHi \e[1;#{n}mho\e[m" }' | ansi2html 15 | # 16 | # Author: Mislav Marohnić 17 | 18 | require 'cgi' 19 | 20 | if ARGV.include?('-h') || ARGV.include?('--help') 21 | File.open(__FILE__, 'r') do |source| 22 | source.each do |line| 23 | case line 24 | when /^#!/ then next 25 | when /^#/ then puts line.sub(/^# ?/, '') 26 | else break 27 | end 28 | end 29 | end 30 | exit 0 31 | end 32 | 33 | wrap = ARGV.delete('-w') 34 | css = ARGV.delete('--css') 35 | data = ARGF.read 36 | out = $stdout 37 | 38 | COLORS = [ 39 | %w[black black #818383], 40 | %w[red #BA3521 #F9391F], 41 | %w[green #25BC22 #31E722], 42 | %w[yellow #ADAD27 #EAEC23], 43 | %w[blue #3D2EE2 #562FE2], 44 | %w[magenta #AE38D4 #F935F7], 45 | %w[cyan #2BB9C9 #14F0F0], 46 | %w[white #DEDFE1 #E9EBEB], 47 | ] 48 | 49 | def classnames(code) 50 | codes = code.split(';').map { |c| c.to_i } 51 | codes.delete(0) 52 | if color_code = codes.find { |c| c.between?(30, 37) } 53 | codes.delete(color_code) 54 | codes << COLORS[color_code - 30][0] 55 | end 56 | codes << 'bright' if codes.delete(1) 57 | codes.map { |c| "ansi-#{c}" }.join(' ') 58 | end 59 | 60 | open = false 61 | 62 | out << "
" if wrap
63 | # replace escape sequences with SPAN tags
64 | out << CGI.escapeHTML(data.rstrip).gsub(/\e\[ ([\d;]*)m /x) {
65 |   opentag = $1.empty?? nil : ""
66 |   closetag = open ? '' : nil
67 |   open = !!opentag
68 |   "#{closetag}#{opentag}"
69 | }
70 | out << "
" if wrap 71 | out << "\n" 72 | 73 | if css 74 | out.puts "\n" 81 | end 82 | -------------------------------------------------------------------------------- /bin/ascii-bar: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Original source: https://github.com/wookayin/dotfiles/blob/master/bin/ascii-bar 4 | # 5 | # Draw an ascii bar figure from the percentage value read from stdin. 6 | # e.g. 7 | # echo 17.73 | ascii-bar --width 20 8 | # [|||| ] 17.73 % 9 | # 10 | 11 | function usage() { 12 | echo "Draw an ascii bar figure from the percentage value read from stdin." 13 | echo "e.g." 14 | echo "echo 17.73 | ascii-bar --width 20" 15 | } 16 | 17 | if [[ "$1" == "--help" ]]; then 18 | usage 19 | exit 0 20 | fi 21 | if [ "$1" == "--width" ]; then 22 | shift 23 | fi 24 | 25 | width=${1:-20} 26 | if [[ ! $width =~ [0-9]+ ]]; then 27 | echo "Invalid width: $width;"; exit 1; 28 | fi 29 | 30 | exec awk "{PERCENT=\$1; printf \"[\"; \ 31 | for(i=0;i<$width;++i) if(i/$width < PERCENT/100.0) printf \"|\"; else printf \" \"; \ 32 | printf \"] %5.2f %%\", PERCENT; print \"\" }" 33 | -------------------------------------------------------------------------------- /bin/bindiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Diff two binary files 4 | # 5 | # Copyright 2023, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 10 | set -x 11 | fi 12 | fi 13 | 14 | function debug() { 15 | if [[ -n "$DEBUG" ]]; then 16 | echo "$@" 17 | fi 18 | } 19 | 20 | function echo-stderr() { 21 | echo "$@" 1>&2 ## Send message to stderr. 22 | } 23 | 24 | function fail() { 25 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 26 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 27 | } 28 | 29 | function has() { 30 | # Check if a command is in $PATH 31 | which "$@" > /dev/null 2>&1 32 | } 33 | 34 | function check-deps() { 35 | debug "Checking dependencies..." 36 | for p in 'xxd' 'diff' 37 | do 38 | if ! has $p; then 39 | fail "Can't find $p in your \$PATH" 40 | else 41 | debug "- Found $p" 42 | fi 43 | done 44 | } 45 | 46 | function get-settings() { 47 | if [[ -z "$DIFF_PROG" ]]; then 48 | for p in 'less' 'bat' 'colordiff' 49 | do 50 | if has "$p"; then 51 | DIFF_PROG=$p 52 | debug "Found $p, setting DIFF_PROG to $DIFF_PROG" 53 | fi 54 | done 55 | fi 56 | if ! has "$DIFF_PROG"; then 57 | fail "DIFF_PROG: $DIFF_PROG not found in your \$PATH" 58 | fi 59 | debug "Using $DIFF_PROG to process binary diff output" 60 | } 61 | 62 | check-deps 63 | get-settings 64 | 65 | diff -y <(xxd "$1") <(xxd "$2") | $DIFF_PROG 66 | exit $? 67 | -------------------------------------------------------------------------------- /bin/block-until-internet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Block until a network connection is obtained. 4 | # 5 | # From: https://github.com/bamos/dotfiles/blob/master/.funcs 6 | 7 | while true; do 8 | ping -c 1 8.8.8.8 &> /dev/null && break 9 | sleep 1 10 | done 11 | -------------------------------------------------------------------------------- /bin/change-extension: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | # 3 | # Usage: change-extension jpeg jpg 4 | 5 | if [[ $# != 2 ]]; then 6 | echo "Usage: $(basename $0) OldExtension NewExtension" 7 | exit 1 8 | fi 9 | foreach f (**/*.$1) 10 | mv $f $f:r.$2 11 | end 12 | -------------------------------------------------------------------------------- /bin/clean-whiteboard-picture: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Based on https://gist.github.com/lelandbatey/8677901 4 | 5 | function fail() { 6 | printf '%s\\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 7 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 8 | } 9 | 10 | function has() { 11 | # Check if a command is in $PATH 12 | which "$@" > /dev/null 2>&1 13 | } 14 | 15 | if ! has convert; then 16 | fail "Can't find 'convert' in your PATH" 17 | fi 18 | 19 | exec convert "$1" -morphology Convolve DoG:15,100,0 -negate -normalize -blur 0x1 -channel RBG -level 60%,91%,0.1 "$2" 20 | -------------------------------------------------------------------------------- /bin/csr-decode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Decode a CSR 4 | # 5 | # Copyright 2020, Joe Block 6 | 7 | set -o pipefail 8 | 9 | cert=${1?Need cert} 10 | exec openssl req -in "$cert" -text -noout 11 | -------------------------------------------------------------------------------- /bin/cursor-hide: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # hide iterm cursor 4 | # 5 | # Copyright 2024, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | # shellcheck disable=SC2086 10 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 11 | set -x 12 | fi 13 | fi 14 | 15 | function debug() { 16 | if [[ -n "$DEBUG" ]]; then 17 | echo "$@" 18 | fi 19 | } 20 | 21 | function echo-stderr() { 22 | echo "$@" 1>&2 ## Send message to stderr. 23 | } 24 | 25 | function fail() { 26 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 27 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 28 | } 29 | 30 | function has() { 31 | # Check if a command is in $PATH 32 | which "$@" > /dev/null 2>&1 33 | } 34 | 35 | function check-dependencies() { 36 | debug "Checking dependencies..." 37 | # shellcheck disable=SC2041 38 | # Placeholders for whatever programs you really need 39 | for p in 'tput' 40 | do 41 | if ! has $p; then 42 | fail "Can't find $p in your $PATH" 43 | else 44 | debug "- Found $p" 45 | fi 46 | done 47 | } 48 | 49 | function my-name() { 50 | basename "$0" 51 | } 52 | 53 | function usage() { 54 | echo "Usage: $(my-name): Hides cursor in iterm " 55 | } 56 | 57 | function path-exists() { 58 | local file="${1}" 59 | [[ -s "${file}" ]] || fail "$1 is not valid" 60 | [[ -d "${file}" ]] && return 61 | [[ -f "${file}" ]] && return 62 | fail "$1 is not a directory or file" 63 | } 64 | 65 | check-dependencies 66 | exec tput civis 67 | -------------------------------------------------------------------------------- /bin/cursor-show: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # hide iterm cursor 4 | # 5 | # Copyright 2024, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | # shellcheck disable=SC2086 10 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 11 | set -x 12 | fi 13 | fi 14 | 15 | function debug() { 16 | if [[ -n "$DEBUG" ]]; then 17 | echo "$@" 18 | fi 19 | } 20 | 21 | function echo-stderr() { 22 | echo "$@" 1>&2 ## Send message to stderr. 23 | } 24 | 25 | function fail() { 26 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 27 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 28 | } 29 | 30 | function has() { 31 | # Check if a command is in $PATH 32 | which "$@" > /dev/null 2>&1 33 | } 34 | 35 | function check-dependencies() { 36 | debug "Checking dependencies..." 37 | # shellcheck disable=SC2041 38 | # Placeholders for whatever programs you really need 39 | for p in 'tput' 40 | do 41 | if ! has $p; then 42 | fail "Can't find $p in your $PATH" 43 | else 44 | debug "- Found $p" 45 | fi 46 | done 47 | } 48 | 49 | function my-name() { 50 | basename "$0" 51 | } 52 | 53 | function usage() { 54 | echo "Usage: $(my-name): Reveal cursor in iterm " 55 | } 56 | 57 | function path-exists() { 58 | local file="${1}" 59 | [[ -s "${file}" ]] || fail "$1 is not valid" 60 | [[ -d "${file}" ]] && return 61 | [[ -f "${file}" ]] && return 62 | fail "$1 is not a directory or file" 63 | } 64 | 65 | check-dependencies 66 | exec tput cvvis 67 | -------------------------------------------------------------------------------- /bin/datauri: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Create data URI from a file. 4 | # 5 | # Based on https://github.com/alrra/dotfiles/blob/main/src/shell/bash_functions 6 | 7 | datauri() { 8 | 9 | local mimeType="" 10 | 11 | if [ ! -f "$1" ]; then 12 | printf "%s is not a file.\n" "$1" 13 | return 14 | fi 15 | 16 | mimeType=$(file --brief --mime-type "$1") 17 | # └─ do not prepend the filename to the output 18 | 19 | if [[ $mimeType == text/* ]]; then 20 | mimeType="$mimeType;charset=utf-8" 21 | fi 22 | 23 | printf "data:%s;base64,%s" \ 24 | "$mimeType" \ 25 | "$(openssl base64 -in "$1" | tr -d "\n")" 26 | 27 | } 28 | 29 | datauri "$@" 30 | -------------------------------------------------------------------------------- /bin/datestamp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # I need timestamps in so many places... 4 | 5 | exec date +%Y%m%d-%H%M%S 6 | -------------------------------------------------------------------------------- /bin/decode-cert: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Decode an SSL certificate 4 | # 5 | # Copyright 2020, Joe Block 6 | 7 | set -o pipefail 8 | 9 | cert=${1?Need cert} 10 | exec openssl x509 -in "$cert" -text -noout 11 | -------------------------------------------------------------------------------- /bin/dedupe-in-order: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Use awk to dedupe a file, outputting the lines in order 4 | # 5 | # Does _not_ just dedupe adjacent lines like uniq does 6 | 7 | exec awk '!dupelines[$0]++' < $@ 8 | -------------------------------------------------------------------------------- /bin/diff-summary: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # From Gary Bernhardt's dotfiles 4 | # https://github.com/garybernhardt/dotfiles/blob/main/bin/gn 5 | # 6 | # Print a summary of a piped diff like: 7 | # 8 | # $ git diff main | gn 9 | # 49 lines of diff 10 | # +11 lines (+18, -7) 11 | # +77 words (+110, -33) 12 | # 13 | # or with a diff/patch file: 14 | # 15 | # $ gn my-awesome-patch.diff 16 | # 49 lines of diff 17 | # +11 lines (+18, -7) 18 | # +77 words (+110, -33) 19 | 20 | import sys, os, re, fileinput 21 | 22 | def get_lines(diff_lines): 23 | # Added lines start with '+' (but not '+++', because that marks a new 24 | # file). The same goes for removed lines, except '-' instead of '+'. 25 | added_lines = [line for line in diff_lines 26 | if line.startswith('+') and not line.startswith('+++')] 27 | removed_lines = [line for line in diff_lines 28 | if line.startswith('-') and not line.startswith('---')] 29 | return added_lines, removed_lines 30 | 31 | def get_words(added_lines, removed_lines): 32 | def word_count(lines): 33 | return [word 34 | for line in lines 35 | for word in line.split() 36 | if re.match(r'^\w+', word)] 37 | 38 | return word_count(added_lines), word_count(removed_lines) 39 | 40 | if __name__ == '__main__': 41 | diff_lines = list(fileinput.input()) 42 | added_lines, removed_lines = get_lines(diff_lines) 43 | added_words, removed_words = get_words(added_lines, removed_lines) 44 | print '%i lines of diff' % len(diff_lines) 45 | print '%+i lines (+%i, -%i)' % (len(added_lines) - len(removed_lines), 46 | len(added_lines), 47 | len(removed_lines)) 48 | print '%+i words (+%i, -%i)' % (len(added_words) - len(removed_words), 49 | len(added_words), 50 | len(removed_words)) 51 | -------------------------------------------------------------------------------- /bin/dns-resolvers: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Show DNS resolver information. Of course macOS makes it more complicated 4 | # than `cat /etc/resolv.conf` 5 | # 6 | # Copyright 2023, Joe Block 7 | 8 | set -o pipefail 9 | 10 | case $(uname) in 11 | Darwin) 12 | scutil --dns | awk '/^(DNS|resolver| (search|nameserver|domain))/' 13 | ;; 14 | Linux) 15 | cat /etc/resolv.conf 16 | ;; 17 | esac 18 | -------------------------------------------------------------------------------- /bin/dumpdns: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Dump DNS records for a zone 4 | # 5 | # Copyright 2019, Joe Block 6 | 7 | exec nslookup -q=any $@ 8 | -------------------------------------------------------------------------------- /bin/exiftool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # show or remove exif data from a file 4 | # 5 | # Copyright 2022, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | set -x 10 | fi 11 | 12 | function debug() { 13 | if [[ -n "$DEBUG" ]]; then 14 | echo "$@" 15 | fi 16 | } 17 | 18 | function fail() { 19 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 20 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 21 | } 22 | 23 | function has() { 24 | # Check if a command is in $PATH 25 | which "$@" > /dev/null 2>&1 26 | } 27 | 28 | function exiftool 29 | { 30 | if [[ "$1" == "show" ]]; then 31 | # Print the content of the exif metadata 32 | exec exif "$2"; 33 | elif [[ "$1" == "rm" ]]; then 34 | # Remove exif metadatas 35 | exec mogrify -strip "$2"; 36 | else 37 | echo "Usage:" 38 | echo "exif show show the EXIF data contained in " 39 | echo "exif rm deletes all the EXIF data in " 40 | fi 41 | } 42 | 43 | function checkdeps { 44 | if ! has mogrify; then 45 | fail "mogrify is not installed" 46 | fi 47 | if ! has exif; then 48 | fail "exif is not installed" 49 | fi 50 | } 51 | 52 | checkdeps 53 | exiftool "$@" 54 | -------------------------------------------------------------------------------- /bin/explainshell: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Author: Joe Block 4 | # 5 | # Explains a command using explainshell.com 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | # shellcheck disable=SC2086 10 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 11 | set -x 12 | fi 13 | fi 14 | 15 | function debug() { 16 | if [[ -n "$DEBUG" ]]; then 17 | echo "$@" 18 | fi 19 | } 20 | 21 | function echo-stderr() { 22 | echo "$@" 1>&2 ## Send message to stderr. 23 | } 24 | 25 | function fail() { 26 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 27 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 28 | } 29 | 30 | function has() { 31 | # Check if a command is in $PATH 32 | which "$@" > /dev/null 2>&1 33 | } 34 | 35 | # on_exit and add_on_exit from http://www.linuxjournal.com/content/use-bash-trap-statement-cleanup-temporary-files 36 | 37 | # Usage: 38 | # add_on_exit rm -f /tmp/foo 39 | # add_on_exit echo "I am exiting" 40 | # tempfile=$(mktemp) 41 | # add_on_exit rm -f "$tempfile" 42 | 43 | function on_exit() 44 | { 45 | for i in "${on_exit_items[@]}" 46 | do 47 | # shellcheck disable=SC2086 48 | eval $i 49 | done 50 | } 51 | 52 | function add_on_exit() 53 | { 54 | local n=${#on_exit_items[*]} 55 | # shellcheck disable=SC2004 56 | on_exit_items[$n]="$*" 57 | if [[ $n -eq 0 ]]; then 58 | trap on_exit EXIT 59 | fi 60 | } 61 | 62 | # Set up a working scratch directory 63 | SCRATCH_D=$(mktemp -d) 64 | 65 | if [[ ! "$SCRATCH_D" || ! -d "$SCRATCH_D" ]]; then 66 | echo "Could not create temp dir" 67 | exit 1 68 | fi 69 | 70 | add_on_exit rm -rf "$SCRATCH_D" 71 | 72 | function get-settings() { 73 | TEXT_WEB_BROWSER=${TEXT_WEB_BROWSER:-'lynx'} 74 | } 75 | 76 | function check-dependencies() { 77 | debug "Checking dependencies..." 78 | # shellcheck disable=SC2041 79 | # Placeholders for whatever programs you really need 80 | for p in "$TEXT_WEB_BROWSER" 81 | do 82 | if ! has $p; then 83 | fail "Can't find $p in your $PATH" 84 | else 85 | debug "- Found $p" 86 | fi 87 | done 88 | } 89 | 90 | function my-name() { 91 | basename "$0" 92 | } 93 | 94 | function usage() { 95 | echo "Usage: $(my-name) shell command" 96 | echo 97 | echo "Gets an explanation of a command from explainshell.com" 98 | } 99 | 100 | get-settings 101 | check-dependencies 102 | if [[ $# == 0 ]]; then 103 | usage 104 | exit 1 105 | fi 106 | 107 | set -ue 108 | cmd="$(echo "$@" | tr ' ' '+')"; 109 | url="https://explainshell.com/explain?cmd=$cmd" 110 | exec lynx -dump "$url" 111 | -------------------------------------------------------------------------------- /bin/extractFromRPM: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rpm2cpio "${1}" | cpio -iv --to-stdout ./"${2}" 4 | -------------------------------------------------------------------------------- /bin/find-in-files: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Original source - https://betterprogramming.pub/boost-your-command-line-productivity-with-fuzzy-finder-985aa162ba5d 4 | 5 | set -o pipefail 6 | 7 | # find-in-file - usage: fif 8 | 9 | function fail() { 10 | printf '%s\\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 11 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 12 | } 13 | 14 | function can_has() { 15 | # Check if a command is in $PATH 16 | which "$@" > /dev/null 2>&1 17 | } 18 | 19 | if [ ! "$#" -gt 0 ]; then 20 | fail "Need a string to search for!" 21 | fi 22 | 23 | if ! can_has rg; then 24 | fail "Can't find rg in your PATH" 25 | fi 26 | 27 | rg --files-with-matches --no-messages "$1" \ 28 | | fzf "$FZF_PREVIEW_WINDOW" --preview "rg --ignore-case --pretty --context 10 '$1' {}" 29 | -------------------------------------------------------------------------------- /bin/fix-zsh-history: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Fix a corrupt ZSH history 4 | # 5 | # Copyright 2020, Joe Block 6 | 7 | set -o pipefail 8 | 9 | WORK_D=$(mktemp -d) 10 | 11 | if [[ ! "$WORK_D" || ! -d "$WORK_D" ]]; then 12 | echo "Could not create temp dir" 13 | exit 1 14 | fi 15 | 16 | cleanup() { 17 | if [[ -d "$WORK_D" ]]; then 18 | rm -fr "$WORK_D" 19 | fi 20 | } 21 | 22 | trap cleanup EXIT 23 | 24 | cp ~/.zsh_history "${WORK_D}/bad_history" 25 | strings "${WORK_D}/bad_history" > ~/.zsh_history 26 | fc -R .zsh_history 27 | -------------------------------------------------------------------------------- /bin/get-distro: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | # 3 | # https://github.com/blueyed/dotfiles/blob/master/usr/bin/get_distro 4 | # 5 | # Display information about currently used distribution. 6 | # Code is taken and adopted from byobu. 7 | 8 | if [ -r "/etc/issue" ]; then 9 | # lsb_release is *really* slow; try to use /etc/issue first 10 | issue=$(grep -m1 "^[A-Za-z]" /etc/issue) 11 | case "$issue" in 12 | Ubuntu*) distro=${${${issue%%\(*}%%\\*}%% } ;; 13 | Debian*) distro="Debian $(/dev/null 2>&1; then 24 | distro=$(echo "$issue" | sed "s/ [^0-9]* / /" | awk '{print $1 " " $2}') 25 | fi 26 | ;; 27 | esac 28 | fi 29 | 30 | if ! (( $+distro )) && which lsb_release >/dev/null 2>&1; then 31 | # If lsb_release is available, use it 32 | r=$(lsb_release -s -d) 33 | case "$r" in 34 | Ubuntu*.*.*) 35 | # Use the -d if an Ubuntu LTS 36 | distro="$r" 37 | ;; 38 | *) 39 | # But for other distros the description 40 | # is too long, so build from -i and -r 41 | distro="${(f)$(lsb_release -s -i -r)}" 42 | distro=${distro/RedHatEnterpriseServer/RHEL} 43 | ;; 44 | esac 45 | fi 46 | 47 | if ! (( $+distro )) && [[ -f /etc/synoinfo.conf ]]; then 48 | distro=$(grep '^synobios=' /etc/synoinfo.conf | sed -nr 's/^.*"(.*)"$/\1/p') 49 | fi 50 | 51 | if ! (( $+distro )) && [[ -n "$OSTYPE" ]]; then 52 | case "$OSTYPE" in 53 | darwin11.* ) distro="MacOS Lion ${OSTYPE#darwin}" ;; 54 | darwin* ) distro="MacOS ${OSTYPE#darwin}" ;; 55 | *) distro=$OSTYPE ;; 56 | esac 57 | fi 58 | 59 | (( $+distro )) || distro="unknown" 60 | distro+=" ($(uname -m))" 61 | 62 | echo $distro 63 | -------------------------------------------------------------------------------- /bin/get-site-cert: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Get the cert for a site 4 | # 5 | # Copyright 2021, Joe Block 6 | 7 | echo -n | \ 8 | openssl s_client -connect "$1" 2>/dev/null | \ 9 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | \ 10 | openssl x509 -text -noout 11 | exit $? 12 | -------------------------------------------------------------------------------- /bin/gtouch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This base script is MIT licensed so you can base your 4 | # own work on it. 5 | # 6 | # touch a file and add it to git 7 | # 8 | # Copyright 2023, Joe Block 9 | 10 | set -o pipefail 11 | if [[ -n "$DEBUG" ]]; then 12 | # shellcheck disable=SC2086 13 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 14 | set -x 15 | fi 16 | fi 17 | 18 | function debug() { 19 | if [[ -n "$DEBUG" ]]; then 20 | echo "$@" 21 | fi 22 | } 23 | 24 | function echo-stderr() { 25 | echo "$@" 1>&2 ## Send message to stderr. 26 | } 27 | 28 | function fail() { 29 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 30 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 31 | } 32 | 33 | function has() { 34 | # Check if a command is in $PATH 35 | which "$@" > /dev/null 2>&1 36 | } 37 | 38 | # on_exit and add_on_exit from http://www.linuxjournal.com/content/use-bash-trap-statement-cleanup-temporary-files 39 | 40 | # Usage: 41 | # add_on_exit rm -f /tmp/foo 42 | # add_on_exit echo "I am exiting" 43 | # tempfile=$(mktemp) 44 | # add_on_exit rm -f "$tempfile" 45 | 46 | function on_exit() 47 | { 48 | for i in "${on_exit_items[@]}" 49 | do 50 | # shellcheck disable=SC2086 51 | eval $i 52 | done 53 | } 54 | 55 | function add_on_exit() 56 | { 57 | local n=${#on_exit_items[*]} 58 | # shellcheck disable=SC2004 59 | on_exit_items[$n]="$*" 60 | if [[ $n -eq 0 ]]; then 61 | trap on_exit EXIT 62 | fi 63 | } 64 | 65 | # Set up a working scratch directory 66 | SCRATCH_D=$(mktemp -d) 67 | 68 | if [[ ! "$SCRATCH_D" || ! -d "$SCRATCH_D" ]]; then 69 | echo "Could not create temp dir" 70 | exit 1 71 | fi 72 | 73 | add_on_exit rm -rf "$SCRATCH_D" 74 | 75 | function my-name() { 76 | basename "$0" 77 | } 78 | 79 | function usage() { 80 | echo "Usage: $(my-name) ARG ARG" 81 | } 82 | 83 | if [[ $# < 1 ]];then 84 | -------------------------------------------------------------------------------- /bin/gxpr: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Original source: https://github.com/brutasse/dotfiles/blob/master/bin/gxpr 4 | # 5 | # Usage: gxpr 6 | # Like expr(1), but uses Google's calculator to evaluate . 7 | # When google fails, light the WolframAlpha signal 8 | # 9 | # Math examples: 10 | # $ gxpr '1 + 1' 11 | # 2 12 | # 13 | # $ gxpr 2 ^ 16 14 | # 65536 15 | # 16 | # $ gxpr '(2 ^ 1) + (2 ^ 2) + (2 ^ 3) + (2 ^ 5)' 17 | # 46 18 | # 19 | # $ gxpr '5*9+(sqrt 10)^3=' 20 | # 76.6227766 21 | # 22 | # Conversion examples: 23 | # $ gxpr 1GB in KB 24 | # 1048576 kilobytes 25 | # 26 | # $ gxpr 10 megabits in megabytes 27 | # 1.25 megabytes 28 | # 29 | # $ gxpr 2 miles in inches 30 | # 126720 inches 31 | 32 | CURL='curl -s --header User-Agent:gxpr/1.0' 33 | GOOGLE="http://www.google.com/ig/calculator" 34 | WOLFRAM="http://www.wolframalpha.com/input/" 35 | EXPR=$(echo "$@" | perl -MURI::Escape -ne 'chomp;print uri_escape($_)') 36 | 37 | res=$( 38 | $CURL "$GOOGLE?q=$EXPR" | 39 | perl -ne '/rhs: "?([^\[\],":]+)/ and print $1' | 40 | perl -pe 's/[^\x00-\x7F]//g' 41 | ) 42 | 43 | # if we don't have a result, try wolfram alpha 44 | test -z "$res" && { 45 | echo "google doesn't know" "$@" 1>&2 46 | echo "⌘ click: \033[4m$WOLFRAM?i=$EXPR\033[0m" 47 | exit 1 48 | } 49 | 50 | echo "$res" 51 | -------------------------------------------------------------------------------- /bin/headers: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # From https://github.com/rtomayko/dotfiles/blob/rtomayko/bin/headers 4 | 5 | curl -sv "$@" 2>&1 >/dev/null | 6 | grep -v "^\*" | 7 | grep -v "^}" | 8 | cut -c3- 9 | -------------------------------------------------------------------------------- /bin/hex-to-bin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # hex-to-bin 4 | # 5 | # Converts a hexstring to a binary stream 6 | # 7 | # Copyright 2023, Joe Block 8 | 9 | set -o pipefail 10 | if [[ -n "$DEBUG" ]]; then 11 | set -x 12 | fi 13 | 14 | function echo-stderr() { 15 | echo "$@" 1>&2 ## Send message to stderr. 16 | } 17 | 18 | function fail() { 19 | echo-stderr "$@" 20 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 21 | } 22 | 23 | function has() { 24 | # Check if a command is in $PATH 25 | which "$@" > /dev/null 2>&1 26 | } 27 | 28 | # hexstring to binary stream 29 | if has xxd; then 30 | echo -n "$@" | xxd -p -r 31 | exit $? 32 | else 33 | fail "Can't find 'xxd' in PATH" 34 | fi 35 | -------------------------------------------------------------------------------- /bin/hide-cursor: -------------------------------------------------------------------------------- 1 | cursor-hide -------------------------------------------------------------------------------- /bin/html2markdown: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # https://github.com/realpython/python-scripts/blob/master/14_html_to_markdown.sh 4 | # Convert all html files in a single directory to markdown 5 | # 6 | # 1. Install pandoc 7 | # 2. Run the script 8 | 9 | FILES=*.html 10 | for f in $FILES 11 | do 12 | # extension="${f##*.}" 13 | filename="${f%.*}" 14 | echo "Converting $f to $filename.md" 15 | `pandoc $f -t markdown -o ../mds/$filename.md` 16 | # uncomment this line to delete the source file. 17 | # rm $f 18 | done 19 | -------------------------------------------------------------------------------- /bin/http-debug: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Debug an url 4 | 5 | # shellcheck disable=SC2068 6 | exec curl $@ -o /dev/null -w "dns: %{time_namelookup}\nconnect: %{time_connect}\npretransfer: %{time_pretransfer}\nstart transfer: %{time_starttransfer} \ntotal: %{time_total}\n" 7 | -------------------------------------------------------------------------------- /bin/http-headers: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Look at the http headers from a URL 4 | 5 | exec curl -I -L "$@" 6 | -------------------------------------------------------------------------------- /bin/http-reflect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Reflects the requests from HTTP methods GET, POST, PUT, and DELETE 4 | # Written by Nathan Hamiel (2010) 5 | 6 | from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler 7 | from optparse import OptionParser 8 | 9 | 10 | class RequestHandler(BaseHTTPRequestHandler): 11 | def do_GET(self): 12 | 13 | request_path = self.path 14 | 15 | print("\n----- Request Start ----->\n") 16 | print(request_path) 17 | print(self.headers) 18 | print("<----- Request End -----\n") 19 | 20 | self.send_response(200) 21 | self.send_header("Set-Cookie", "foo=bar") 22 | 23 | def do_POST(self): 24 | 25 | request_path = self.path 26 | 27 | print("\n----- Request Start ----->\n") 28 | print(request_path) 29 | 30 | request_headers = self.headers 31 | content_length = request_headers.getheaders("content-length") 32 | length = int(content_length[0]) if content_length else 0 33 | 34 | print(request_headers) 35 | print(self.rfile.read(length)) 36 | print("<----- Request End -----\n") 37 | 38 | self.send_response(200) 39 | 40 | do_PUT = do_POST 41 | do_DELETE = do_GET 42 | 43 | 44 | def main(): 45 | port = 8080 46 | print("Listening on localhost:%s" % port) 47 | server = HTTPServer(("", port), RequestHandler) 48 | server.serve_forever() 49 | 50 | 51 | if __name__ == "__main__": 52 | parser = OptionParser() 53 | parser.usage = ( 54 | "Creates an http-server that will echo out any GET or POST parameters\n" 55 | "Run:\n\n" 56 | " reflect" 57 | ) 58 | (options, args) = parser.parse_args() 59 | 60 | main() 61 | -------------------------------------------------------------------------------- /bin/human-path: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Display $PATH in more human-friendly format. 4 | # 5 | 6 | echo -e "${PATH//:/\\n}" 7 | -------------------------------------------------------------------------------- /bin/human-time: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Turns integer seconds into human-understandable time. 4 | # 5 | # $ human-time 88000 6 | # 1d 26m 40s 7 | # 8 | # Copyright 2021, Joe Block 9 | # License: Apache 2.0 10 | 11 | # shellcheck disable=SC2004 12 | human_time() { 13 | local raw_seconds=$1 14 | local days=$(( raw_seconds / 60 / 60 / 24 )) 15 | local hours=$(( raw_seconds / 60 / 60 % 24 )) 16 | local minutes=$(( raw_seconds / 60 % 60 )) 17 | local seconds=$(( raw_seconds % 60 )) 18 | (( $days > 0 )) && echo -n "${days}d " 19 | (( $hours > 0 )) && echo -n "${hours}h " 20 | (( $minutes > 0 )) && echo -n "${minutes}m " 21 | echo "${seconds}s " 22 | } 23 | 24 | human_time "$@" 25 | -------------------------------------------------------------------------------- /bin/icorrupt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Original source: https://gist.github.com/twirrim/b87d08a2436437c8ff97a65877586182 5 | 6 | import sys 7 | import random 8 | 9 | random.seed() 10 | 11 | # Can't figure out a way to programmatically generate this 12 | diacritics = [ 13 | "\u0300", 14 | "\u0301", 15 | "\u0302", 16 | "\u0303", 17 | "\u0304", 18 | "\u0305", 19 | "\u0306", 20 | "\u0307", 21 | "\u0308", 22 | "\u0309", 23 | "\u030A", 24 | "\u030B", 25 | "\u030C", 26 | "\u030D", 27 | "\u030E", 28 | "\u030F", 29 | "\u0310", 30 | "\u0311", 31 | "\u0312", 32 | "\u0313", 33 | "\u0314", 34 | "\u0315", 35 | "\u0316", 36 | "\u0317", 37 | "\u0318", 38 | "\u0319", 39 | "\u031A", 40 | "\u031B", 41 | "\u031C", 42 | "\u031D", 43 | "\u031E", 44 | "\u031F", 45 | "\u0320", 46 | "\u0321", 47 | "\u0322", 48 | "\u0323", 49 | "\u0324", 50 | "\u0325", 51 | "\u0326", 52 | "\u0327", 53 | "\u0328", 54 | "\u0329", 55 | "\u032A", 56 | "\u032B", 57 | "\u032C", 58 | "\u032D", 59 | "\u032E", 60 | "\u032F", 61 | "\u0330", 62 | "\u0331", 63 | "\u0332", 64 | "\u0333", 65 | "\u0334", 66 | "\u0335", 67 | "\u0336", 68 | "\u0337", 69 | "\u0338", 70 | "\u0339", 71 | "\u033A", 72 | "\u033B", 73 | "\u033C", 74 | "\u033D", 75 | "\u033E", 76 | "\u033F", 77 | "\u0340", 78 | "\u0341", 79 | "\u0342", 80 | "\u0343", 81 | "\u0344", 82 | "\u0345", 83 | "\u0346", 84 | "\u0347", 85 | "\u0348", 86 | "\u0349", 87 | "\u034A", 88 | "\u034B", 89 | "\u034C", 90 | "\u034D", 91 | "\u034E", 92 | "\u034F", 93 | "\u0350", 94 | "\u0351", 95 | "\u0352", 96 | "\u0353", 97 | "\u0354", 98 | "\u0355", 99 | "\u0356", 100 | "\u0357", 101 | "\u0358", 102 | "\u0359", 103 | "\u035A", 104 | "\u035B", 105 | "\u035C", 106 | "\u035D", 107 | "\u035E", 108 | "\u035F", 109 | "\u0350", 110 | "\u0361", 111 | "\u0362", 112 | "\u0363", 113 | "\u0364", 114 | "\u0365", 115 | "\u0366", 116 | "\u0367", 117 | "\u0368", 118 | "\u0369", 119 | "\u036A", 120 | "\u036B", 121 | "\u036C", 122 | "\u036D", 123 | "\u036E", 124 | "\u036F", 125 | ] 126 | 127 | def add_a_diacritic(base_letter, diacritic_mark): 128 | return base_letter + diacritic_mark 129 | 130 | def corrupt_letter(letter): 131 | # Let's use 10 for now 132 | to_add = random.sample(diacritics, 10) 133 | 134 | for diacrit in to_add: 135 | letter = letter + diacrit 136 | 137 | return letter 138 | 139 | def corrupt_text(text): 140 | corrupted = "".join(map(corrupt_letter, text)) 141 | print(corrupted) 142 | 143 | 144 | if __name__ == "__main__": 145 | corrupt_text(sys.argv[1]) 146 | -------------------------------------------------------------------------------- /bin/iflip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Takes an input string and flips it 4 | # based on the irssi plugin fliptext.pl by Gerfried Fuchs, 5 | # (http://rhonda.deb.at/projects/irssi/scripts/fliptext.pl) 6 | # but ported to python 3 and changed to operate as a simple cli app 7 | # 8 | # Source: https://github.com/twirrim/iflip/blob/master/iflip 9 | # Author: https://github.com/twirrim 10 | # Licnense: GPL v2 11 | 12 | import sys 13 | 14 | flipdict = { 15 | 'a': 'ɐ', 16 | 'b': 'q', 17 | 'c': 'ɔ', 18 | 'd': 'p', 19 | 'e': 'ǝ', 20 | 'f': 'ɟ', 21 | 'g': 'ƃ', 22 | 'h': 'ɥ', 23 | 'i': 'ı', 24 | 'j': 'ɾ', 25 | 'k': 'ʞ', 26 | 'l': '⌉', 27 | 'm': 'ɯ', 28 | 'n': 'u', 29 | 'o': 'o', 30 | 'p': 'd', 31 | 'q': 'b', 32 | 'r': 'ɹ', 33 | 's': 's', 34 | 't': 'ʇ', 35 | 'u': 'n', 36 | 'v': 'ʌ', 37 | 'w': 'ʍ', 38 | 'x': 'x', 39 | 'y': 'ʎ', 40 | '.': '˙', 41 | '[': ']', 42 | '(': ')', 43 | '{': '}', 44 | '?': '¿', 45 | '!': '¡', 46 | "'": ',', 47 | '<': '>', 48 | '_': '‾', 49 | ';': '؛', 50 | '‿': '⁀', 51 | '⁅': '⁆', 52 | '∴': '∵', } 53 | 54 | 55 | def fliptext(text): 56 | # Reverse the text 57 | text = text[::-1] 58 | flipper = "(╯°□°)╯︵" 59 | flipped = "" 60 | # Replace each letter in the text with its upside down alternative 61 | for letter in text: 62 | if letter in flipdict: 63 | flipped = flipped+flipdict[letter] 64 | else: 65 | flipped = flipped+letter 66 | # Merge the two together 67 | print(flipper, flipped) 68 | 69 | 70 | if __name__ == '__main__': 71 | fliptext(sys.argv[1]) 72 | -------------------------------------------------------------------------------- /bin/ipaddresses: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ifconfig -a | awk '/[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ {sub(/addr:/,""); print $2 }' 4 | -------------------------------------------------------------------------------- /bin/is-remote-session: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Detects if we're in a remote session or not 4 | # 5 | # Copyright 2023, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | set -x 10 | fi 11 | 12 | function debug() { 13 | if [[ -n "$DEBUG" ]]; then 14 | echo "$@" 15 | fi 16 | } 17 | 18 | function echo-stderr() { 19 | echo "$@" 1>&2 ## Send message to stderr. 20 | } 21 | 22 | function fail() { 23 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 24 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 25 | } 26 | 27 | function has() { 28 | # Check if a command is in $PATH 29 | which "$@" > /dev/null 2>&1 30 | } 31 | 32 | if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then 33 | exit 0 34 | fi 35 | 36 | case $(ps -o comm= -p "$PPID") in 37 | sshd|*/sshd) return 0;; 38 | esac 39 | 40 | exit 1 41 | -------------------------------------------------------------------------------- /bin/jira: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Open a jira ticket from the command line 4 | # 5 | # Copyright 2022, Joe Block 6 | 7 | SETTINGS_F="${HOME}/.jpbscripts/jira-settings.sh" 8 | 9 | set -o pipefail 10 | if [[ -n "$DEBUG" ]]; then 11 | set -x 12 | fi 13 | 14 | function debug() { 15 | if [[ -n "$DEBUG" ]]; then 16 | echo "$@" 17 | fi 18 | } 19 | 20 | function fail() { 21 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 22 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 23 | } 24 | 25 | function has() { 26 | # Check if a command is in $PATH 27 | which "$@" > /dev/null 2>&1 28 | } 29 | 30 | if [[ "$(uname -s)" != "Darwin" ]]; then 31 | fail "This script only works on macOS, sorry." 32 | fi 33 | 34 | if [[ $# != 1 ]]; then 35 | fail "You must specify a JIRA ticket" 36 | fi 37 | 38 | if [[ -z "${JIRA_URL}" ]]; then 39 | if [[ ! -r "${SETTINGS_F}" ]]; then 40 | fail "You need to either set JIRA_URL in your environment or create ${SETTINGS_F} containing a line like 'JIRA_URL=http://your.jira.server'" 41 | fi 42 | 43 | # shellcheck disable=SC1090 44 | source "${SETTINGS_F}" 45 | fi 46 | debug "JIRA_URL: ${JIRA_URL}" 47 | 48 | if [[ -z "${JIRA_URL}" ]]; then 49 | fail "JIRA_URL is not set in your environment. Create ${SETTINGS_F} with 'JIRA_URL=http://your.jira.server' in it" 50 | fi 51 | 52 | open "$JIRA_URL/browse/$1" 53 | -------------------------------------------------------------------------------- /bin/jmemstat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # === Java process memory info === 4 | # 5 | # MIT License 6 | # Copyright (c) 2016 Attila Majoros 7 | # 8 | # Source: https://github.com/majk1/shellrc/blob/master/utils/jmemstat.sh 9 | # 10 | # Examples: 11 | # 12 | # list all: while read jpid jclass; do printf "%5s %20s - %s\n" $jpid $jclass "$(./jmemstat.sh -1 $jpid)"; done < <(jps | grep -v '\ Jps$') 13 | 14 | unset j_pid 15 | oneliner=0 16 | brief=0 17 | debug=0 18 | 19 | while [ ! -z "$1" ]; do 20 | param="$1" 21 | case "$param" in 22 | -b|--brief) 23 | brief=1 24 | shift 25 | ;; 26 | -1) 27 | oneliner=1 28 | shift 29 | ;; 30 | --debug) 31 | debug=1 32 | shift 33 | ;; 34 | *) 35 | j_pid="$param" 36 | shift 37 | ;; 38 | esac 39 | done 40 | 41 | if [ -z "$j_pid" ]; then 42 | echo "Usage: jmemstat [-b|--brief --debug] " >&2 43 | echo "" >&2 44 | echo " -1 - brief oneliner" >&2 45 | echo " -b|--brief - brief output, not that structured beauty :D" >&2 46 | echo " --debug - raw jstat fields printed" >&2 47 | echo "" >&2 48 | exit 1 49 | fi 50 | 51 | [ $brief -eq 0 -a $oneliner -eq 0 ] && echo "Printing memory info for java process: $j_pid" 52 | 53 | read j_eden j_old j_meta < <(jstat -gccapacity $j_pid | awk 'END{eden=$6; old=$10; meta=$13; printf "%0.2f %0.2f %0.2f", eden, old, meta}') 54 | [ $debug -eq 1 ] && echo "EC=$j_eden, OC=$j_old, MC=$j_meta" >&2 55 | read j_eden_p j_old_p j_meta_p < <(jstat -gcutil $j_pid | awk 'END{eden=$3; old=$4; meta=$5; print eden" "old" "meta}') 56 | [ $debug -eq 1 ] && echo "E=$j_eden_p, O=$j_old_p, M=$j_meta_p" >&2 57 | 58 | read j_eden_usage < <(awk 'BEGIN{usage=ARGV[1]*(ARGV[2]/100); printf "%0.2f", usage}' $j_eden $j_eden_p) 59 | read j_old_usage < <(awk 'BEGIN{usage=ARGV[1]*(ARGV[2]/100); printf "%0.2f", usage}' $j_old $j_old_p) 60 | read j_meta_usage < <(awk 'BEGIN{usage=ARGV[1]*(ARGV[2]/100); printf "%0.2f", usage}' $j_meta $j_meta_p) 61 | 62 | read j_sum < <(awk 'BEGIN{sum=0; for (i=1;i/dev/null 2>&1; then 68 | read os_rss os_dirty < <(pmap -x $j_pid | awk 'END{printf "%0.2f %0.2f\n", $4, $5}') 69 | os_mem_present=1 70 | fi 71 | 72 | if [ $oneliner -eq 1 ]; then 73 | if [ $os_mem_present -eq 1 ]; then 74 | printf "JVM: %'.2f / %'.2f (%s%%), OS: %'.2f (RSS), %'.2f (Dirty)\n" $j_sum $j_sum_usage $j_sum_p $os_rss $os_dirty 75 | else 76 | printf "JVM: %'.2f / %'.2f (%s%%)\n" $j_sum $j_sum_usage $j_sum_p 77 | fi 78 | elif [ $brief -eq 1 ]; then 79 | printf "JVM (%d) total memory usage by JVM (kB): %'.2f / %'.2f (%s%%)\n" $j_pid $j_sum $j_sum_usage $j_sum_p 80 | if [ $os_mem_present -eq 1 ]; then 81 | printf "JVM (%d) total memory usage by OS (kB): %'.2f (RSS), %'.2f (Dirty)\n" $j_pid $os_rss $os_dirty 82 | else 83 | echo "[WARN] OS memory usage cannot be determined, pmap not found" >&2 84 | fi 85 | else 86 | printf ".------------.----------------.----------------.---------.\n" 87 | printf "| %-10s | %14s | %14s | %6s%% |\n" "Mem JVM" "Allocated" "Used" "Used " 88 | printf "|------------|----------------|----------------|---------|\n" 89 | printf "| %-10s | %'14.2f | %'14.2f | %6s%% |\n" "Eden" $j_eden $j_eden_usage $j_eden_p 90 | printf "| %-10s | %'14.2f | %'14.2f | %6s%% |\n" "Oldgen" $j_old $j_old_usage $j_old_p 91 | printf "| %-10s | %'14.2f | %'14.2f | %6s%% |\n" "Metaspace" $j_meta $j_meta_usage $j_meta_p 92 | printf "|------------|----------------|----------------|---------|\n" 93 | printf "| %-10s | %'14.2f | %'14.2f | %6s%% |\n" "Total" $j_sum $j_sum_usage $j_sum_p 94 | printf "'------------'----------------'----------------'---------'\n" 95 | if [ $os_mem_present -eq 1 ]; then 96 | echo 97 | printf ".----------------.----------------.\n" 98 | printf "| %14s | %14s |\n" "OS RSS" "OS Dirty" 99 | printf "|----------------|----------------|\n" 100 | printf "| %'14.2f | %'14.2f |\n" $os_rss $os_dirty 101 | printf "'----------------'----------------'\n" 102 | echo 103 | else 104 | echo 105 | echo "[WARN] OS memory usage cannot be determined, pmap not found" >&2 106 | echo 107 | fi 108 | fi 109 | -------------------------------------------------------------------------------- /bin/json-to-yaml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Convert yaml to json 4 | # 5 | # Copyright 2022, Joe Block 6 | # 7 | # License: Apache 2 8 | 9 | set -o pipefail 10 | if [[ -n "$DEBUG" ]]; then 11 | set -x 12 | fi 13 | 14 | function debug() { 15 | if [[ -n "$DEBUG" ]]; then 16 | echo "$@" 17 | fi 18 | } 19 | 20 | function fail() { 21 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 22 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 23 | } 24 | 25 | function has() { 26 | # Check if a command is in $PATH 27 | which "$@" > /dev/null 2>&1 28 | } 29 | 30 | if ! has python3; then 31 | fail "Cannot find python3 in your PATH!" 32 | fi 33 | 34 | exec python3 -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' 35 | -------------------------------------------------------------------------------- /bin/json2yaml: -------------------------------------------------------------------------------- 1 | json-to-yaml -------------------------------------------------------------------------------- /bin/jsondiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Diff JSON files and cope with key-order differences by processing with 4 | # json.tool 5 | 6 | set -o pipefail 7 | 8 | if [[ ! -r "${1}" ]]; then 9 | echo "Can't read ${1}" 10 | exit 1 11 | fi 12 | 13 | if [[ ! -r "${2}" ]]; then 14 | echo "Can't read ${2}" 15 | exit 1 16 | fi 17 | 18 | # shellcheck disable=SC2002 19 | diff -B <( cat "${1}" | python -m json.tool ) <( cat "${2}" | python -m json.tool ) 20 | -------------------------------------------------------------------------------- /bin/lineprof: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby --disable-gems -n 2 | # 3 | # Annotates each line of input with the number of milliseconds elapsed since 4 | # the last line. Useful for figuring out slow points of output-producing programs. 5 | # 6 | # Source: https://github.com/mislav/dotfiles/blob/master/bin/lineprof 7 | 8 | BEGIN { 9 | $last = $start = Time.now 10 | } 11 | 12 | now = Time.now 13 | delta = now - $last 14 | $last = now 15 | 16 | printf '%5.2f ', delta * 1000 17 | puts $_ 18 | 19 | END { 20 | delta = $last - $start 21 | printf "%5.2f ms total\n", delta * 1000 22 | } 23 | -------------------------------------------------------------------------------- /bin/local-ip-address: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$(uname -s)" = "Linux" ]]; then 4 | active_interface=$(route | awk '{ if ($1 ~/default/) { print $8} }') 5 | 6 | ifconfig "${active_interface}" | \ 7 | grep -v inet6 | \ 8 | awk '{ if ($1 ~/inet/) { print $2} }' | \ 9 | tr ':' '\n' | \ 10 | grep -v addr 11 | fi 12 | 13 | if [[ "$(uname -s)" = "Darwin" ]]; then 14 | active_interface=$(netstat -nr -f inet | awk '/default/ && !/bridge/ { print $4 }') 15 | 16 | ifconfig "${active_interface}" inet | \ 17 | awk '/inet/ { print $2 }' 18 | fi 19 | -------------------------------------------------------------------------------- /bin/local-ipv4-address: -------------------------------------------------------------------------------- 1 | local-ip-address -------------------------------------------------------------------------------- /bin/local-ipv6-address: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$(uname -s)" = "Linux" ]]; then 4 | active_interface=$(route | awk '{ if ($1 ~/default/) { print $8} }') 5 | 6 | ifconfig ${active_interface} | \ 7 | grep inet6 | \ 8 | awk '{ if ($1 ~/inet/) { print $2} }' | \ 9 | tr ':' '\n' | \ 10 | grep -v addr 11 | fi 12 | 13 | if [[ "$(uname -s)" = "Darwin" ]]; then 14 | active_interface=$(netstat -nr | awk '{ if ($1 ~/default/) { print $6} }') 15 | 16 | ifconfig ${active_interface} | \ 17 | grep inet6 | \ 18 | awk '{ if ($1 ~/inet/) { print $2} }' 19 | fi 20 | -------------------------------------------------------------------------------- /bin/ls-open-ports: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # List open ports 4 | 5 | function has() { 6 | # Check if a command is in $PATH 7 | which "$@" > /dev/null 2>&1 8 | } 9 | 10 | function linux-listeners() { 11 | # ss is the new hotness 12 | if has ss; then 13 | ss -lnptu 14 | else 15 | lsof -i -n -P | awk 'NR==1 || /LISTEN/' 16 | fi 17 | } 18 | 19 | # Print the currently listening IP ports 20 | 21 | case $(uname) in 22 | Linux) 23 | linux-listeners 24 | ;; 25 | Darwin) 26 | lsof -i -n -P | awk 'NR==1 || /LISTEN/' 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /bin/ls-sockets: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # List open sockets 4 | 5 | exec sudo lsof -i -P 6 | -------------------------------------------------------------------------------- /bin/ls-tcp-sockets: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # List open TCP sockets 4 | 5 | sudo lsof -i -P | grep -i tcp 6 | -------------------------------------------------------------------------------- /bin/ls-udp-sockets: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # List open UDP sockets 4 | 5 | sudo lsof -i -P | grep -i udp 6 | -------------------------------------------------------------------------------- /bin/lsof-unlinked: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Original source: https://github.com/ludios/ubuntils/blob/master/bin/lsof-unlinked 4 | # 5 | # Original author: Ivan Kosik 6 | 7 | set -e 8 | set -o pipefail 9 | 10 | if [ "$(id -u)" -ne 0 ]; then 11 | echo "Warning: not running as root, some files may be missing." >&2 12 | fi 13 | 14 | # list all open files (but not mapped files) that have been unlinked 15 | # filter out 'lsof: no pwd entry for UID ' spewed on machines with unprivileged LXC containers 16 | # sort by last column, the filename 17 | lsof +L1 2> >(grep -v "^lsof: no pwd entry for UID ") | sort -k 7n 18 | -------------------------------------------------------------------------------- /bin/memcached-tool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # memcached-tool: 4 | # stats/management tool for memcached. 5 | # 6 | # Author: 7 | # Brad Fitzpatrick 8 | # 9 | # License: 10 | # public domain. I give up all rights to this 11 | # tool. modify and copy at will. 12 | # 13 | 14 | use strict; 15 | use IO::Socket::INET; 16 | 17 | my $host = shift; 18 | my $mode = shift || "display"; 19 | my ($from, $to); 20 | 21 | if ($mode eq "display") { 22 | undef $mode if @ARGV; 23 | } elsif ($mode eq "move") { 24 | $from = shift; 25 | $to = shift; 26 | undef $mode if $from < 6 || $from > 17; 27 | undef $mode if $to < 6 || $to > 17; 28 | print STDERR "ERROR: parameters out of range\n\n" unless $mode; 29 | } elsif ($mode eq 'dump') { 30 | ; 31 | } elsif ($mode eq 'stats') { 32 | ; 33 | } else { 34 | undef $mode; 35 | } 36 | 37 | undef $mode if @ARGV; 38 | 39 | die 40 | "Usage: memcached-tool [mode]\n 41 | memcached-tool 10.0.0.5:11211 display # shows slabs 42 | memcached-tool 10.0.0.5:11211 # same. (default is display) 43 | memcached-tool 10.0.0.5:11211 stats # shows general stats 44 | memcached-tool 10.0.0.5:11211 dump # dumps keys and values 45 | " unless $host && $mode; 46 | 47 | $host .= ":11211" unless $host =~ /:\d+/; 48 | 49 | my $sock = IO::Socket::INET->new(PeerAddr => $host, 50 | Proto => 'tcp'); 51 | die "Couldn't connect to $host\n" unless $sock; 52 | 53 | if ($mode eq 'dump') { 54 | my %items; 55 | my $totalitems; 56 | 57 | print $sock "stats items\r\n"; 58 | 59 | while (<$sock>) { 60 | last if /^END/; 61 | if (/^STAT items:(\d*):number (\d*)/) { 62 | $items{$1} = $2; 63 | $totalitems += $2; 64 | } 65 | } 66 | print STDERR "Dumping memcache contents\n"; 67 | print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n"; 68 | print STDERR " Number of items : $totalitems\n"; 69 | 70 | foreach my $bucket (sort(keys(%items))) { 71 | print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n"; 72 | print $sock "stats cachedump $bucket $items{$bucket}\r\n"; 73 | my %keyexp; 74 | while (<$sock>) { 75 | last if /^END/; 76 | # return format looks like this 77 | # ITEM foo [6 b; 1176415152 s] 78 | if (/^ITEM (\S+) \[.* (\d+) s\]/) { 79 | $keyexp{$1} = $2; 80 | } 81 | } 82 | 83 | foreach my $k (keys(%keyexp)) { 84 | print $sock "get $k\r\n"; 85 | my $response = <$sock>; 86 | if ($response =~ /VALUE (\S+) (\d+) (\d+)/) { 87 | my $flags = $2; 88 | my $len = $3; 89 | my $val; 90 | read $sock, $val, $len; 91 | print "add $k $flags $keyexp{$k} $len\r\n$val\r\n"; 92 | # get the END 93 | $_ = <$sock>; 94 | $_ = <$sock>; 95 | } 96 | } 97 | } 98 | exit; 99 | } 100 | 101 | if ($mode eq 'stats') { 102 | my %items; 103 | 104 | print $sock "stats\r\n"; 105 | 106 | while (<$sock>) { 107 | last if /^END/; 108 | chomp; 109 | if (/^STAT\s+(\S*)\s+(.*)/) { 110 | $items{$1} = $2; 111 | } 112 | } 113 | printf ("#%-17s %5s %11s\n", $host, "Field", "Value"); 114 | foreach my $name (sort(keys(%items))) { 115 | printf ("%24s %12s\n", $name, $items{$name}); 116 | 117 | } 118 | exit; 119 | } 120 | 121 | # display mode: 122 | 123 | my %items; # class -> { number, age, chunk_size, chunks_per_page, 124 | # total_pages, total_chunks, used_chunks, 125 | # free_chunks, free_chunks_end } 126 | 127 | print $sock "stats items\r\n"; 128 | while (<$sock>) { 129 | last if /^END/; 130 | if (/^STAT items:(\d+):(\w+) (\d+)/) { 131 | $items{$1}{$2} = $3; 132 | } 133 | } 134 | 135 | print $sock "stats slabs\r\n"; 136 | while (<$sock>) { 137 | last if /^END/; 138 | if (/^STAT (\d+):(\w+) (\d+)/) { 139 | $items{$1}{$2} = $3; 140 | } 141 | } 142 | 143 | print " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n"; 144 | foreach my $n (1..40) { 145 | my $it = $items{$n}; 146 | next if (0 == $it->{total_pages}); 147 | my $size = $it->{chunk_size} < 1024 ? 148 | "$it->{chunk_size}B" : 149 | sprintf("%.1fK", $it->{chunk_size} / 1024.0); 150 | my $full = $it->{free_chunks_end} == 0 ? "yes" : " no"; 151 | printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n", 152 | $n, $size, $it->{age}, $it->{total_pages}, 153 | $it->{number}, $full, $it->{evicted}, 154 | $it->{evicted_time}, $it->{outofmemory}); 155 | } 156 | -------------------------------------------------------------------------------- /bin/memcached-top: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl -w 2 | # 3 | # http://code.google.com/p/memcache-top/ 4 | # 5 | ################################################################################ 6 | # memcache-top.pl 7 | # 8 | # "top" for memcache - watch the traffic and other stats in real-time. Yoikes. 9 | # 10 | # NOTES: 11 | # 12 | # If Getopt::Long is installed: 13 | # - Specify instances w/ --instances (multiple times or comma separated) 14 | # - Specify default port w/ --port (defaults to 11211) 15 | # - Specify sleep time w/ --sleep (default 3) 16 | # - Specify color output w/ --color (default) or --nocolor 17 | # - Specify lifetime stats w/ --lifetime or --nolifetime (default) 18 | # NOTE: lifetime stats break thresholds for evictions, bytes. 19 | # - Specify read and write bytes w/ --bytes (default) or --nobytes 20 | # - Specify get and set commands w/ --commands or --nocommands (default) 21 | # - Specify cumulative numbers w/ --cumulative (don't use with lifetime) 22 | # - Run a single time without clearing the screen, good for scripts and 23 | # the like: --oneoff 24 | # 25 | # If Getopt::Long is not installed: 26 | # - Specify sleep time by typing a number after the command. 27 | # Specify instances in @default_instances. 28 | # Specify thresholds by defining %threshold. 29 | # Written against memcached v 1.2.3, but works fine w/ later versions. 30 | # 31 | # HISTORY: 32 | # 33 | # v0.3 - 2009-04-22 - ntang 34 | # Minor cleanups. First release to google code: 35 | # http://code.google.com/p/memcache-top/ 36 | # v0.4 - 2009-04-23 - ntang 37 | # Added ability to specify color, sleep time, and servers on command line. 38 | # Also added checks for Getopt::Long and Term::ANSIColor. 39 | # Added $default_port = "11211" and padding for short server names. Server 40 | # names over 23 characters inc. port will break the column lineups for now. 41 | # v0.4b - 2009-04-23 - ntang 42 | # Added total capacity, and changed "SERVER" to "INSTANCE" to be more clear. 43 | # Server now is the hostname, instance is hostname + port. It will 44 | # truncate the instance and/or server to fit inside the first column 45 | # correctly. (Yay!) It'll also truncate long reads/ writes (or 46 | # technically any number) to K or M or G if it exceeds certain limits for 47 | # readability. 48 | # v0.5 - 2009-04-24 - ntang 49 | # Cleaned up instances vs. servers so it's internally consistent. 50 | # Redid printing so that now it stores it all and only refreshes/ prints when 51 | # it has the full set of data. Warning: major hackishness. 52 | # Switched to per-second stats by default w/ lifetime stats available. 53 | # v0.6 - 2009-04-28 - ntang 54 | # I lied. One more change... the ability to specify read/write bytes, or 55 | # get/set commands, or both. Bear in mind if you specify both you will 56 | # exceed the width of a standard terminal! You've been warned. :P 57 | # Also, some minor display changes, etc. etc. 58 | # 59 | ################################################################################ 60 | 61 | use strict; 62 | use IO::Socket; 63 | use Time::HiRes 'time'; 64 | 65 | my (@default_instances, @instances, $remote, $sleep, %threshold, %laststats, 66 | $usecolor, @keys, $default_port, $version, @out, $lifetime, $bytes, 67 | $commands, $cumulative, $oneoff); 68 | 69 | $version = "0.7"; 70 | 71 | ################################################################################ 72 | # CONFIGURATION 73 | 74 | # Set $usecolor to 1 to push @out,in, gasp, color. 75 | $usecolor = 1; 76 | 77 | # 'Alert' threshold values at which to color the text red. 78 | %threshold = ( 79 | cache_hit => 60, # Cache hit ratio 80 | usage => 90, # % space used 81 | time => 5, # Number of ms to run the stats query 82 | evictions => 0, # Number of evictions per second 83 | curr_connections => 3500, # Number of current connections 84 | bytes_read => 1000000, # Bytes read, per second 85 | bytes_written => 1000000, # Bytes written, per second 86 | limit_maxbytes => 0, # Total space allocated 87 | bytes => 0, # Total space used 88 | cmd_get => 1000, # Get commands 89 | cmd_set => 1000, # Set commands 90 | ); 91 | 92 | # Display lifetime stats instead of per-second stats 93 | $lifetime = 0; 94 | 95 | # Display read/write bytes 96 | $bytes = 1; 97 | 98 | # Display get/set commands 99 | $commands = 0; 100 | 101 | # Show cumulative stats (since start of run) 102 | $cumulative = 0; 103 | 104 | # Default time to sleep in-between refreshes. 105 | $sleep = 3; 106 | 107 | # Run a single time as a one-off without formatting: 108 | $oneoff = 0; 109 | 110 | # List of servers/ ports to query. 111 | @default_instances = ( 112 | '127.0.0.1:11211', 113 | ); 114 | 115 | # Default port to connect to, if not specified 116 | $default_port = "11211"; 117 | 118 | # END CONFIGURATION 119 | ################################################################################ 120 | 121 | @keys = ('usage', 'cache_hit', 'curr_connections', 'time', 'cmd_get', 'cmd_set', 122 | 'bytes_read', 'bytes_written', 'evictions', 'limit_maxbytes', 'bytes'); 123 | 124 | if (@ARGV) { 125 | eval { require Getopt::Long; }; 126 | if ($@) { 127 | if ( $ARGV[0] =~ /^\d+$/ ) { 128 | $sleep = $ARGV[0]; 129 | } 130 | else { 131 | die "USAGE: memcache-top.pl \n"; 132 | } 133 | } 134 | else { 135 | use Getopt::Long; 136 | GetOptions ( 137 | 'instances=s' => \@instances, 138 | 'sleep=i' => \$sleep, 139 | 'port=i' => \$default_port, 140 | 'color!' => \$usecolor, 141 | 'lifetime!' => \$lifetime, 142 | 'bytes!' => \$bytes, 143 | 'commands!' => \$commands, 144 | 'cumulative!' => \$cumulative, 145 | 'oneoff' => \$oneoff, 146 | ); 147 | if (@instances) { 148 | @instances = split(/,/,join(',',@instances)); 149 | } else { 150 | @instances = @default_instances; 151 | } 152 | } 153 | } 154 | else { 155 | @instances = @default_instances; 156 | } 157 | 158 | if ( $lifetime && $cumulative ) { 159 | $lifetime = 0; 160 | } 161 | 162 | if ( $usecolor ) { 163 | eval { require Term::ANSIColor; }; 164 | if ($@) { $usecolor = 0; } 165 | else { use Term::ANSIColor; } 166 | } 167 | 168 | if ( $oneoff ) { 169 | $usecolor = 0; 170 | } 171 | 172 | my $i = 1; 173 | 174 | my (%original); 175 | 176 | while ($i) { 177 | 178 | @out = (); 179 | 180 | unless ($oneoff) { push @out,"\033[2J"; } # This clears the screen, yo. 181 | 182 | push @out,color 'bold' if $usecolor; 183 | push @out,"\nmemcache-top v$version\t"; 184 | push @out,color 'reset' if $usecolor; 185 | push @out,"(default port: " . sprintf("%5d",$default_port) . ", color: "; 186 | push @out,"on," if $usecolor; 187 | push @out,"off," unless $usecolor; 188 | push @out," refresh: $sleep seconds)\n\n"; 189 | push @out,color 'bold' if $usecolor; 190 | push @out,"INSTANCE\t\tUSAGE\tHIT %\tCONN\tTIME\t"; 191 | if ( $lifetime || $cumulative ) { 192 | push @out,"EVICT\t"; 193 | push @out,"GETS\tSETS\t" if $commands; 194 | push @out,"READ\tWRITE\t" if $bytes; 195 | push @out,"\n"; 196 | } else { 197 | push @out,"EVICT/s "; 198 | push @out,"GETS/s\tSETS/s\t" if $commands; 199 | push @out,"READ/s\tWRITE/s\t" if $bytes; 200 | push @out,"\n"; 201 | } 202 | push @out,color 'reset' if $usecolor; 203 | 204 | my %tot; 205 | 206 | foreach my $key (@keys) { 207 | $tot{$key} = 0; 208 | } 209 | 210 | my $count = 0; 211 | 212 | foreach my $instance (@instances) { 213 | 214 | my ($port, $server); 215 | 216 | my @split = split(/:/,$instance); 217 | unless ( $split[1] ) { 218 | $instance = $instance . ":" . $default_port; 219 | $port = $default_port; 220 | } 221 | else { 222 | $port = $split[1]; 223 | } 224 | 225 | # Some exhaustive (exhausting?) logic to determine the ideal text to push @out,for 226 | # the server name. 227 | if ( length($instance) > 22 ) { 228 | if ( $port ne $default_port ) { 229 | $server = substr($split[0],0,17) . ":" . $port; 230 | } 231 | else { 232 | if ( length($split[0]) < 18 ) { 233 | $server = $instance; 234 | } 235 | else { 236 | $server = substr($split[0],0,23); 237 | } 238 | } 239 | } 240 | elsif ( length($instance) < 8 ) { 241 | $server = "$instance\t\t"; 242 | } 243 | elsif ( length($instance) < 16 ) { 244 | $server = "$instance\t"; 245 | } 246 | else { 247 | $server = $instance; 248 | } 249 | 250 | my $t0 = time(); 251 | 252 | $remote = IO::Socket::INET->new($instance); 253 | unless ( defined($remote) ) { 254 | push @out,color 'red' if $usecolor; 255 | push @out,$instance . " is DOWN.\n"; 256 | $count++; 257 | push @out,color 'reset' if $usecolor; 258 | next; 259 | } 260 | 261 | $remote->autoflush(1); 262 | $count++; 263 | 264 | print $remote "stats\n"; 265 | 266 | my (%stats, %outstats); 267 | 268 | foreach my $key (@keys) { 269 | $outstats{$key} = 0; 270 | } 271 | 272 | LINE: while ( defined ( my $line = <$remote> ) ) { 273 | last LINE if ( $line =~ /END/ ); 274 | chomp $line; 275 | my @bits = split(' ',$line); 276 | $stats{$bits[1]} = $bits[2]; 277 | next LINE; 278 | } 279 | 280 | close $remote; 281 | 282 | my $t1 = time(); 283 | $outstats{time} = ($t1 - $t0) * 1000; 284 | 285 | if ( $lifetime || $cumulative) { 286 | foreach my $key ('cmd_get', 'cmd_set', 'get_hits', 'get_misses', 'evictions', 'bytes_read', 'bytes_written') { 287 | if ( $cumulative ) { 288 | if ( $i == 1 ) { 289 | $original{$instance}{$key} = $stats{$key}; 290 | } else { 291 | $outstats{$key} = $stats{$key} - $original{$instance}{$key}; 292 | } 293 | } else { 294 | $outstats{$key} = $stats{$key}; 295 | } 296 | } 297 | $outstats{cache_hit} = ( $stats{get_hits} / $stats{cmd_get} ) * 100; 298 | } else { 299 | foreach my $key ('cmd_get', 'cmd_set', 'get_hits', 'get_misses', 'evictions', 'bytes_read', 'bytes_written') { 300 | if ( defined ( $laststats{$instance}{$key} ) ) { 301 | $outstats{$key} = ($stats{$key} - $laststats{$instance}{$key}) / $sleep; 302 | } 303 | } 304 | $outstats{cache_hit} = 0; 305 | if ( defined($outstats{get_misses}) && $outstats{get_misses} > 0 ) { 306 | $outstats{cache_hit} = ( $laststats{$instance}{get_hits} / $laststats{$instance}{cmd_get} ) * 100; 307 | } 308 | } 309 | 310 | $outstats{limit_maxbytes} = $stats{limit_maxbytes}; 311 | $outstats{bytes} = $stats{bytes}; 312 | $outstats{usage} = ( $stats{bytes} / $stats{limit_maxbytes} * 100 ); 313 | $outstats{curr_connections} = $stats{curr_connections}; 314 | 315 | if ( $cumulative ) { 316 | foreach my $key ('cmd_get', 'cmd_set', 'get_hits', 'get_misses', 'evictions', 'bytes_read', 'bytes_written') { 317 | $threshold{$key} = $threshold{$key} * $i if $threshold{$key}; 318 | } 319 | } 320 | 321 | push @out,"$server\t"; 322 | threshold_print( $outstats{usage}, $threshold{usage}, 1, 0, '%', '%.1f'); 323 | threshold_print( $outstats{cache_hit}, $threshold{cache_hit}, 0, 0, '%', '%.1f'); 324 | threshold_print( $outstats{curr_connections}, $threshold{curr_connections}, 1, 0, '', '%.0d'); 325 | if ( $outstats{time} >= 1000 ) { 326 | threshold_print( $outstats{time}/1000, $threshold{time}/1000, 1, 0, 's', '%.2f'); 327 | } else { 328 | threshold_print( $outstats{time}, $threshold{time}, 1, 0, 'ms', '%.1f'); 329 | } 330 | threshold_print( $outstats{evictions}, $threshold{evictions}, 1, 0, '', '%.1f'); 331 | if ( $commands ) { 332 | threshold_print( $outstats{cmd_get}, $threshold{cmd_get}, 1, 0, '', '%.0f'); 333 | threshold_print( $outstats{cmd_set}, $threshold{cmd_set}, 1, 0, '', '%.0f'); 334 | } 335 | if ( $bytes ) { 336 | threshold_print( $outstats{bytes_read}, $threshold{bytes_read}, 1, 0, '', '%.0f'); 337 | threshold_print( $outstats{bytes_written}, $threshold{bytes_written}, 1, 0, '', '%.0f'); 338 | } 339 | push @out,"\n"; 340 | 341 | foreach my $key (@keys) { 342 | $tot{$key} = $tot{$key} + $outstats{$key}; 343 | } 344 | 345 | unless ( $lifetime || $cumulative ) { 346 | foreach my $key ('cmd_get', 'cmd_set', 'get_hits', 'get_misses', 'evictions', 'bytes_read', 'bytes_written') { 347 | $laststats{$instance}{$key} = $stats{$key}; 348 | } 349 | } 350 | 351 | } 352 | 353 | push @out,color 'bold' if $usecolor; 354 | push @out,"\nAVERAGE:\t\t"; 355 | threshold_print( $tot{usage}/$count, $threshold{usage}, 1, 1, '%', '%.1f'); 356 | threshold_print( $tot{cache_hit}/$count, $threshold{cache_hit}, 0, 1, '%', '%.1f'); 357 | threshold_print( $tot{curr_connections}/$count, $threshold{curr_connections}, 1, 1, '', '%.0d'); 358 | if ( ( $tot{time}/$count ) >= 1000 ) { 359 | threshold_print( ($tot{time}/$count)/1000, $threshold{time}/1000, 1, 1, 's', '%.2f'); 360 | } else { 361 | threshold_print( $tot{time}/$count, $threshold{time}, 1, 1, 'ms', '%.1f'); 362 | } 363 | threshold_print( $tot{evictions}/$count, $threshold{evictions}, 1, 1, '', '%.1f'); 364 | if ( $commands ) { 365 | threshold_print( $tot{cmd_get}/$count, $threshold{cmd_get}, 1, 1, '', '%.0f'); 366 | threshold_print( $tot{cmd_set}/$count, $threshold{cmd_set}, 1, 1, '', '%.0f'); 367 | } 368 | if ( $bytes ) { 369 | threshold_print( $tot{bytes_read}/$count, $threshold{bytes_read}, 1, 1, '', '%.0f'); 370 | threshold_print( $tot{bytes_written}/$count, $threshold{bytes_written}, 1, 1, '', '%.0f'); 371 | } 372 | push @out,"\n"; 373 | push @out,"\nTOTAL:\t\t"; 374 | threshold_print( $tot{bytes}, $threshold{bytes}, 0, 1, 'B/', '%.0f'); 375 | threshold_print( $tot{limit_maxbytes}, $threshold{limit_maxbytes}, 0, 1, "B\t", '%.0f'); 376 | threshold_print( $tot{curr_connections}, $threshold{curr_connections}*$count, 1, 1, '', '%.0d'); 377 | if ( $tot{time} >= 1000 ) { 378 | threshold_print( $tot{time}/1000, ($threshold{time}*$count)/1000, 1, 1, 's', '%.2f'); 379 | } else { 380 | threshold_print( $tot{time}, $threshold{time}*$count, 1, 1, 'ms', '%.1f'); 381 | } 382 | threshold_print( $tot{evictions}, $threshold{evictions}*$count, 1, 1, '', '%.1f'); 383 | if ( $commands ) { 384 | threshold_print( $tot{cmd_get}, $threshold{cmd_get}*$count, 1, 1, '', '%.0f'); 385 | threshold_print( $tot{cmd_set}, $threshold{cmd_set}*$count, 1, 1, '', '%.0f'); 386 | } 387 | if ( $bytes ) { 388 | threshold_print( $tot{bytes_read}, $threshold{bytes_read}*$count, 1, 1, '', '%.0f'); 389 | threshold_print( $tot{bytes_written}, $threshold{bytes_written}*$count, 1, 1, '', '%.0f'); 390 | } 391 | push @out,color 'reset' if $usecolor; 392 | push @out,"\n"; 393 | unless ($oneoff) { push @out,"(ctrl-c to quit.)\n"; } 394 | sleep($sleep); 395 | 396 | print @out; 397 | $i++; 398 | 399 | if ($oneoff) { exit 0; } 400 | 401 | } 402 | 403 | ################################################################################ 404 | # threshold_print 405 | # takes two variables, compares them (greater then if $gt == 1), and then prints 406 | # it. It uses red as the default color for successful comparisons, but sets 407 | # it to red bold if $bold == 1. $trail specifies trailing characters to print. 408 | # $sprintf lets you specify the format for sprintf(). 409 | # 410 | sub threshold_print { 411 | 412 | my ($stat, $threshold, $gt, $bold, $trail, $sprintf) = @_; 413 | 414 | my $color = 'red'; 415 | my $offcolor = 'reset'; 416 | if ( $bold ) { 417 | $color = 'bold red'; 418 | $offcolor = 'reset bold'; 419 | } 420 | 421 | if ( $gt ) { 422 | if ( $stat > $threshold ) { 423 | push @out, color $color if $usecolor; 424 | } 425 | } else { 426 | if ( $stat < $threshold ) { 427 | push @out, color $color if $usecolor; 428 | } 429 | } 430 | 431 | if ( $stat > 999999999999 ) { 432 | $stat = $stat / (1024*1024*1024*1024); 433 | $trail = 'T' . $trail; 434 | $sprintf = '%.1f'; 435 | } elsif ( $stat > 99999999 ) { 436 | $stat = $stat / (1024*1024*1024); 437 | $trail = "G" . $trail; 438 | $sprintf = '%.1f'; 439 | } elsif ( $stat > 999999 ) { 440 | $stat = $stat / (1024*1024); 441 | $trail = 'M' . $trail; 442 | $sprintf = '%.1f'; 443 | } elsif ( $stat > 9999 ) { 444 | $stat = $stat/1024; 445 | $trail = 'K' . $trail; 446 | $sprintf = '%.1f'; 447 | } 448 | 449 | push @out,sprintf($sprintf,$stat) . $trail; 450 | push @out,color $offcolor if $usecolor; 451 | push @out,"\t"; 452 | } 453 | ################################################################################ 454 | -------------------------------------------------------------------------------- /bin/mfs-instatrash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Set trash expiry time for a file/directory to 0 4 | # 5 | # Script skeleton 6 | # 7 | # Copyright 2024, Your Name 8 | 9 | set -o pipefail 10 | if [[ -n "$DEBUG" ]]; then 11 | # shellcheck disable=SC2086 12 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 13 | set -x 14 | fi 15 | fi 16 | 17 | function debug() { 18 | if [[ -n "$DEBUG" ]]; then 19 | echo "$@" 20 | fi 21 | } 22 | 23 | function echo-stderr() { 24 | echo "$@" 1>&2 ## Send message to stderr. 25 | } 26 | 27 | function fail() { 28 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 29 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 30 | } 31 | 32 | function has() { 33 | # Check if a command is in $PATH 34 | which "$@" > /dev/null 2>&1 35 | } 36 | 37 | # on_exit and add_on_exit from http://www.linuxjournal.com/content/use-bash-trap-statement-cleanup-temporary-files 38 | 39 | # Usage: 40 | # add_on_exit rm -f /tmp/foo 41 | # add_on_exit echo "I am exiting" 42 | # tempfile=$(mktemp) 43 | # add_on_exit rm -f "$tempfile" 44 | 45 | function on_exit() 46 | { 47 | for i in "${on_exit_items[@]}" 48 | do 49 | # shellcheck disable=SC2086 50 | eval $i 51 | done 52 | } 53 | 54 | function add_on_exit() 55 | { 56 | local n=${#on_exit_items[*]} 57 | # shellcheck disable=SC2004 58 | on_exit_items[$n]="$*" 59 | if [[ $n -eq 0 ]]; then 60 | trap on_exit EXIT 61 | fi 62 | } 63 | 64 | # Set up a working scratch directory 65 | SCRATCH_D=$(mktemp -d) 66 | 67 | if [[ ! "$SCRATCH_D" || ! -d "$SCRATCH_D" ]]; then 68 | echo "Could not create temp dir" 69 | exit 1 70 | fi 71 | 72 | add_on_exit rm -rf "$SCRATCH_D" 73 | 74 | function get-settings() { 75 | SETTING_ENV_NAME=${SETTING_ENV_NAME:-'default-value'} 76 | } 77 | 78 | function check-dependencies() { 79 | debug "Checking dependencies..." 80 | # shellcheck disable=SC2041 81 | # Placeholders for whatever programs you really need 82 | for p in 'mfssettrashtime' 83 | do 84 | if ! has $p; then 85 | fail "Can't find $p in your $PATH" 86 | else 87 | debug "- Found $p" 88 | fi 89 | done 90 | } 91 | 92 | function approve() { 93 | # $1 = prompt for user, or Continue? 94 | # $2 = message for non approved 95 | local prompt 96 | local reject_message 97 | prompt=${1-'Ok to continue?'} 98 | reject_message=${2-'Exiting...'} 99 | echo -n "$prompt [yes/NO]" 100 | read -r answer 101 | # shellcheck disable=SC2086 102 | if [[ ! "$(echo $answer | tr '[:upper:]' '[:lower:]')" == "yes" ]]; then 103 | fail "$reject_message" 104 | fi 105 | } 106 | 107 | function my-name() { 108 | basename "$0" 109 | } 110 | 111 | function usage() { 112 | echo "Usage: $(my-name) ARG ARG" 113 | } 114 | 115 | function path-exists() { 116 | local file="${1}" 117 | [[ -s "${file}" ]] || fail "$1 is not valid" 118 | [[ -d "${file}" ]] && return 119 | [[ -f "${file}" ]] && return 120 | fail "$1 is not a directory or file" 121 | } 122 | 123 | check-dependencies 124 | path-exists "$1" 125 | mfssettrashtime 0 "$1" 126 | -------------------------------------------------------------------------------- /bin/middle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Originally from #commandline-fu on coffeeops slack 4 | # Author [@Hefeweizen](https://github.com/Hefeweizen) 5 | # 6 | # Usage: middle 5,10 README.md 7 | # or: middle /re1/,/re2/ 8 | # Nice for comparing two parts of a file: 9 | # diff -y <(middle 8,20 file.txt) <(middle 58,70 file.txt) 10 | 11 | __offset=$1; 12 | shift; 13 | sed -n "${__offset}p" "$@"; 14 | unset __offset 15 | -------------------------------------------------------------------------------- /bin/mtr-url: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # mtr to a host from a URL to make it easier to see why we're 4 | # having issues connecting. 5 | # 6 | # Copyright 2020, Joe Block 7 | 8 | host=$(ruby -ruri -e "puts (URI.parse('$1').host or '$1')") 9 | exec sudo mtr -t $host 10 | -------------------------------------------------------------------------------- /bin/murder: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # No author listed on the source git. 4 | # 5 | # https://gist.github.com/anonymous/32b1e619bc9e7fbe0eaa#!/bin/bash 6 | 7 | # Signal numbers to use. Configurable, as long as the number of elements in this 8 | # array matches the number of elements in $delays. 9 | signals=(15 2 1 9) 10 | 11 | # How much time processes should be given to finish their work before we try the 12 | # next signal. Configurable, as long as the number of elements in this array 13 | # matches the number of elements in $signals. 14 | delays=(2 3 4 0) 15 | 16 | # Takes a list of PIDs and ends the processes through increasingly rude means. 17 | function mykill { 18 | # Cygwin's default ps is gimped. Use the GNU version instead. 19 | local ps='ps' 20 | if [[ $(uname -o) == 'Cygwin' ]]; then 21 | ps='procps' 22 | fi 23 | ps="$ps o pid=" 24 | 25 | # Figure out the names of the signals we will try. 26 | local names=($(kill -l ${signals[@]})) 27 | 28 | local retval=0 29 | if [[ $# -eq 0 ]]; then 30 | printf 'murder: Too few arguments: %d\nmurder: Usage: murder \n' $# 31 | retval=1 32 | else 33 | for pid in $@; do 34 | local index=0 35 | # Try the next signal if the process is alive AND there are more 36 | # signals to attempt AND the previous kill reported no errors. 37 | while $ps $pid > /dev/null && [[ $index -lt ${#signals} ]] && [[ $retval -eq 0 ]]; do 38 | output=$(kill -s ${signals[$index]} $pid 2>&1) 39 | retval=$? 40 | local killed=1 41 | # Give the process some time to finish. 42 | sleep ${delays[$index]} 43 | index=$(($index + 1)) 44 | done 45 | 46 | if [[ -z $killed ]]; then 47 | printf 'murder: Process with PID %d does not exist.\n' $pid 48 | 49 | elif ! ${ps} ${pid} > /dev/null; then 50 | index=$(($index - 1)) 51 | printf 'murder: Killed process with PID %d with signal %s (%d).\n' $pid ${names[$index]} ${signals[$index]} 52 | 53 | elif [[ ${retval} -ne 0 ]]; then 54 | printf 'murder: kill failed: %s\n' $(echo ${output} | cut -d ' ' -f 5-) 55 | 56 | elif [[ ${index} -eq ${#signals} ]]; then 57 | printf 'murder: Failed to kill process with PID %d with signals %s\n' $pid "$(echo ${names[@]})" 58 | retval=-1 59 | fi 60 | done 61 | fi 62 | return ${retval} 63 | } 64 | 65 | mykill $@ 66 | -------------------------------------------------------------------------------- /bin/name-tab: -------------------------------------------------------------------------------- 1 | name-window -------------------------------------------------------------------------------- /bin/name-window: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Name a terminal window/tab 4 | # 5 | # Copyright 2021, Joe Block 6 | 7 | echo -ne "\e]1;$1\a" 8 | -------------------------------------------------------------------------------- /bin/nanotime: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Joe Block 4 | # License: Apache 2 5 | 6 | set -euo pipefail 7 | 8 | function fail() { 9 | printf '%s\\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 10 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 11 | } 12 | 13 | function nanotime() { 14 | ts=$(date +%s%N) 15 | 16 | "$@" 17 | 18 | # shellcheck disable=SC2004 19 | tt=$((($(date +%s%N) - $ts)/1000000)) 20 | echo "time $tt ms" >&2 21 | } 22 | 23 | if [[ "$(uname)" != "Linux" ]];then 24 | fail "Date command only supports nanoseconds on Linux" 25 | fi 26 | 27 | nanotime "$@" 28 | -------------------------------------------------------------------------------- /bin/newscript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # shellcheck disable=SC1234,SC1071 3 | # Shellcheck finds the embedded bash template and complains because of the 4 | # ruby and python also found in the script 5 | # 6 | # Copyright 2016-2025 Joe Block 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | 21 | # Got tired of 'vim foo' && chmod +x foo && cat /path/to/template > foo, 22 | # so made a helper to create new scripts with the base stuff I wanted in them. 23 | 24 | require "rubygems" 25 | require "pp" 26 | require "optimist" 27 | 28 | def clean_name(name) 29 | # get rid of toxic spaces 30 | return name.gsub(" ", "_") 31 | end 32 | 33 | def write_script(name:, source:) 34 | if File.exist?(name) 35 | puts "There's already a file at #{name}. Bailing out." 36 | exit 13 37 | else 38 | File.open(name, "w") do |content| 39 | content.write(source) 40 | `chmod 755 #{name}` 41 | end 42 | end 43 | end 44 | 45 | def generate_bash_script(name:) 46 | copyright_year = `date -u "+%Y"`.chomp 47 | rawScript =<<"END_BASH_SCRIPT" 48 | #!/usr/bin/env bash 49 | # 50 | # This base script is MIT licensed so you can base your 51 | # own work on it. 52 | # 53 | # #{name} script skeleton 54 | # 55 | # Copyright #{copyright_year}, Your Name 56 | 57 | set -o pipefail 58 | if [[ -n "$DEBUG" ]]; then 59 | # shellcheck disable=SC2086 60 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 61 | set -x 62 | fi 63 | fi 64 | 65 | function echo-stderr() { 66 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 67 | } 68 | 69 | function debug() { 70 | if [[ -n "$DEBUG" ]]; then 71 | echo-stderr "$@" 72 | fi 73 | } 74 | 75 | function fail() { 76 | echo-stderr "$1" 77 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 78 | } 79 | 80 | function has() { 81 | # Check if a command is in $PATH 82 | which "$@" > /dev/null 2>&1 83 | } 84 | 85 | # on_exit and add_on_exit from http://www.linuxjournal.com/content/use-bash-trap-statement-cleanup-temporary-files 86 | 87 | # Usage: 88 | # add_on_exit rm -f /tmp/foo 89 | # add_on_exit echo "I am exiting" 90 | # tempfile=$(mktemp) 91 | # add_on_exit rm -f "$tempfile" 92 | 93 | function on_exit() 94 | { 95 | for i in "${on_exit_items[@]}" 96 | do 97 | # shellcheck disable=SC2086 98 | eval $i 99 | done 100 | } 101 | 102 | function add_on_exit() 103 | { 104 | local n=${#on_exit_items[*]} 105 | # shellcheck disable=SC2004 106 | on_exit_items[$n]="$*" 107 | if [[ $n -eq 0 ]]; then 108 | trap on_exit EXIT 109 | fi 110 | } 111 | 112 | # Set up a working scratch directory 113 | SCRATCH_D=$(mktemp -d) 114 | 115 | if [[ ! "$SCRATCH_D" || ! -d "$SCRATCH_D" ]]; then 116 | echo "Could not create temp dir" 117 | exit 1 118 | fi 119 | 120 | add_on_exit rm -rf "$SCRATCH_D" 121 | 122 | function get-settings() { 123 | SETTING_ENV_NAME=${SETTING_ENV_NAME:-'default-value'} 124 | } 125 | 126 | function only-run-on() { 127 | # shellcheck disable=SC2086 128 | if [[ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "$(echo $1 | tr '[:upper:]' '[:lower:]')" ]]; then 129 | fail "This script only runs on $1, this machine is running $(uname -s)" 130 | else 131 | debug "OS ($(uname -s)) is valid..." 132 | fi 133 | } 134 | 135 | function check-dependency() { 136 | if ! (builtin command -V "$1" >/dev/null 2>&1); then 137 | fail "missing dependency: can't find $1 in your PATH" 138 | fi 139 | } 140 | 141 | function check-dependencies() { 142 | debug "Checking dependencies..." 143 | # shellcheck disable=SC2041 144 | # Placeholders for whatever programs you really need 145 | for dep in "$@" 146 | do 147 | if ! has "$dep"; then 148 | fail "Can't find $dep in your $PATH" 149 | else 150 | debug "- Found $dep" 151 | fi 152 | done 153 | } 154 | 155 | function approve() { 156 | # $1 = prompt for user, or Continue? 157 | # $2 = message for non approved 158 | local prompt 159 | local reject_message 160 | prompt=${1-'Ok to continue?'} 161 | reject_message=${2-'Exiting...'} 162 | echo -n "$prompt [yes/NO]" 163 | read -r answer 164 | # shellcheck disable=SC2086 165 | if [[ ! "$(echo $answer | tr '[:upper:]' '[:lower:]')" == "yes" ]]; then 166 | fail "$reject_message" 167 | fi 168 | } 169 | 170 | function my-name() { 171 | basename "$0" 172 | } 173 | 174 | function usage() { 175 | echo "Usage: $(my-name) ARG ARG" 176 | } 177 | 178 | function path-exists() { 179 | local file="${1}" 180 | [[ -s "${file}" ]] || fail "$1 is not valid" 181 | [[ -d "${file}" ]] && return 182 | [[ -f "${file}" ]] && return 183 | fail "$1 is not a directory or file" 184 | } 185 | 186 | # If you need to restrict to a specific os, use 187 | # only-run-on Darwin 188 | # or 189 | # only-run-on Linux 190 | 191 | # Placeholders for your script's real dependencies 192 | check-dependencies bash cat 193 | 194 | # parse arguments 195 | A_BOOL_SETTING='false' 196 | 197 | while : 198 | do 199 | if [[ $# == 0 ]]; then 200 | break 201 | fi 202 | case "$1" in 203 | --help | -h) 204 | usage 205 | exit 0 206 | ;; 207 | --set-a-bool-to-true) 208 | A_BOOL_SETTING='true' 209 | shift 210 | ;; 211 | --set-a-value) 212 | shift 213 | A_VALUE="$1" 214 | shift 215 | ;; 216 | -- ) 217 | shift; 218 | break 219 | ;; 220 | *) 221 | echo-stderr "Unexpected option: $1" 222 | usage 223 | fail "Invalid CLI argument: '$1'" 224 | ;; 225 | esac 226 | done 227 | 228 | END_BASH_SCRIPT 229 | write_script(name: name, source: rawScript) 230 | end 231 | 232 | def generate_python_script(name:) 233 | copyright_year = `date -u "+%Y"`.chomp 234 | rawScript =<<"END_PYTHON_SCRIPT" 235 | #!/usr/bin/env python3 236 | # 237 | # This base script is MIT licensed so you can base your 238 | # own work on it. 239 | # 240 | # ${name} script skeleton 241 | # 242 | # Copyright #{copyright_year}, Your Name 243 | 244 | import argparse 245 | import logging 246 | import os 247 | import sys 248 | 249 | 250 | def parseCLI(): 251 | ''' 252 | Parse the command line options 253 | ''' 254 | parser = argparse.ArgumentParser() 255 | parser.add_argument('-d', '--debug', 256 | help='Debug setting', 257 | action='store_true') 258 | parser.add_argument('-l', '--log-level', 259 | type=str.upper, 260 | help='set log level', 261 | choices=['DEBUG','INFO','ERROR','WARNING','CRITICAL'], 262 | default='INFO') 263 | 264 | cli = parser.parse_args() 265 | loglevel = getattr(logging, cli.log_level.upper(), None) 266 | logFormat = "[%(asctime)s][%(levelname)8s][%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s" 267 | logging.basicConfig(level=loglevel, format=logFormat) 268 | logging.info('Set log level to %s', cli.log_level.upper()) 269 | return cli 270 | 271 | 272 | def main(): 273 | ''' 274 | Main program driver 275 | ''' 276 | cli = parseCLI() 277 | 278 | 279 | if __name__ == '__main__': 280 | main() 281 | 282 | END_PYTHON_SCRIPT 283 | write_script(name: name, source: rawScript) 284 | end 285 | 286 | def generate_ruby_script(name:) 287 | copyright_year = `date -u "+%Y"`.chomp 288 | rawScript =<<'END_RUBY_SCRIPT' 289 | #!/usr/bin/env ruby 290 | # 291 | # Script skeleton 292 | # 293 | # Copyright #{copyright_year}, Your Name 294 | 295 | require 'trollop' 296 | require 'logger' 297 | 298 | def setup_logging 299 | logger = Logger.new(STDOUT) 300 | case $opts[:log_level].upcase 301 | when 'DEBUG' 302 | logger.level = Logger::DEBUG 303 | when 'ERROR' 304 | logger.level = Logger::ERROR 305 | when 'FATAL' 306 | logger.level = Logger::FATAL 307 | when 'INFO' 308 | logger.level = Logger::INFO 309 | when 'WARN' 310 | logger.level = Logger::WARN 311 | end 312 | logger.formatter = proc do |severity, datetime, progname, msg| 313 | date_format = datetime.strftime("%Y-%m-%d %H:%M:%S") 314 | if severity == "INFO" or severity == "WARN" 315 | "[#{date_format}] #{severity} (#{progname}): #{msg}\n" 316 | else 317 | "[#{date_format}] #{severity} (#{progname}): #{msg}\n" 318 | end 319 | end 320 | return logger 321 | end 322 | 323 | def main 324 | $opts = Trollop::options do 325 | version "yourscript 0.1 (c) 2016 Your Name " 326 | banner <<-EOS 327 | The quick brown fox jumped over the lazy dog. 328 | Usage: 329 | yourscript [options] 330 | where [options] are: 331 | EOS 332 | 333 | opt :debug, "Set debug level", :default => 0 334 | opt :log_level, "Set log level", :default => "INFO", :type => String 335 | end 336 | 337 | validLevels = ['DEBUG', 'ERROR', 'FATAL', 'INFO', 'WARN'] 338 | Trollop::die :log_level, "#{$opts[:log_level]} must be in #{validLevels}" unless validLevels.include?($opts[:log_level].upcase) 339 | 340 | $logger = setup_logging 341 | 342 | end 343 | 344 | main 345 | 346 | END_RUBY_SCRIPT 347 | write_script(name: name, source: rawScript) 348 | end 349 | 350 | def main 351 | $opts = Optimist::options do 352 | version "newscript 0.1 (c) 2016 Joe Block " 353 | banner <<-EOS 354 | Generate a new script file from a template. 355 | Usage: 356 | newscript [options] name 357 | where [options] are: 358 | EOS 359 | 360 | opt :debug, "Set debug level", default: 0 361 | opt :script_type, "Script type. Valid options: bash, python or ruby", type: String, default: "bash" 362 | end 363 | 364 | validFlavors = ["bash", "python", "ruby"] 365 | 366 | # Sanity check args 367 | Optimist::die :script_type, "#{$opts[:script_type]} must be in #{validFlavors}" unless validFlavors.include?($opts[:script_type]) 368 | Optimist::die "Specify at least one file name" unless ARGV.length > 0 369 | 370 | ARGV.each { |name| 371 | case $opts[:script_type] 372 | when "bash" 373 | generate_bash_script(name: name) 374 | when "python" 375 | generate_python_script(name: name) 376 | when "ruby" 377 | generate_ruby_script(name: name) 378 | else 379 | puts "script type #{$opts[:script_type]} unknown" 380 | exit 13 381 | end 382 | } 383 | 384 | end 385 | 386 | main 387 | -------------------------------------------------------------------------------- /bin/openports: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Use ss to get a list of open ports 4 | # 5 | # Copyright 2023, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | # shellcheck disable=SC2086 10 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 11 | set -x 12 | fi 13 | fi 14 | 15 | function debug() { 16 | if [[ -n "$DEBUG" ]]; then 17 | echo "$@" 18 | fi 19 | } 20 | 21 | function echo-stderr() { 22 | echo "$@" 1>&2 ## Send message to stderr. 23 | } 24 | 25 | function fail() { 26 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 27 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 28 | } 29 | 30 | function has() { 31 | # Check if a command is in $PATH 32 | which "$@" > /dev/null 2>&1 33 | } 34 | 35 | function check-dependency() { 36 | if ! (builtin command -V "$1" >/dev/null 2>&1); then 37 | fail "missing dependency: can't find $1 in your PATH" 38 | fi 39 | } 40 | 41 | function check-dependencies() { 42 | debug "Checking dependencies..." 43 | # shellcheck disable=SC2041 44 | # Placeholders for whatever programs you really need 45 | for dep in "$@" 46 | do 47 | if ! has "$dep"; then 48 | fail "Can't find $dep in your $PATH" 49 | else 50 | debug "- Found $dep" 51 | fi 52 | done 53 | } 54 | 55 | function my-name() { 56 | basename "$0" 57 | } 58 | 59 | function usage() { 60 | echo "Usage: $(my-name) ARG ARG" 61 | } 62 | 63 | check-dependencies ss 64 | 65 | exec sudo ss -nplutO '! ( src = localhost )' | \ 66 | sed 's/\(udp\|tcp\).*:\([0-9][0-9]*\)/\2\t\1\t/;s/\([0-9][0-9]*\t[udtcp]*\t\)[^u]*users:(("/\1/;s/".*//;s/.*Address:Port.*/Netid\tPort\tProcess/' | \ 67 | sort -nu 68 | -------------------------------------------------------------------------------- /bin/p7b-decode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Decode a p7b file with openssl 4 | # 5 | # Copyright 2020-2025, Joe Block 6 | 7 | set -o pipefail 8 | 9 | function check-dependencies() { 10 | debug "Checking dependencies..." 11 | # shellcheck disable=SC2041 12 | # Placeholders for whatever programs you really need 13 | for dep in "$@" 14 | do 15 | if ! has "$dep"; then 16 | fail "Can't find $dep in your $PATH" 17 | else 18 | debug "- Found $dep" 19 | fi 20 | done 21 | } 22 | 23 | check-dependencies openssl 24 | cert=${1?Need cert} 25 | exec openssl pkcs7 -in "$cert" -print_certs -text -noout 26 | -------------------------------------------------------------------------------- /bin/pidpwd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Find the pwd of a given pid. Only works on linux since it requires 4 | # /proc 5 | # 6 | # Copyright 2017-2025, Joe Block 7 | 8 | set -o pipefail 9 | 10 | function debug() { 11 | if [[ -n "$DEBUG" ]]; then 12 | echo-stderr "$@" 13 | fi 14 | } 15 | 16 | function echo-stderr() { 17 | echo "$@" 1>&2 ## Send message to stderr. 18 | } 19 | 20 | function fail() { 21 | printf '%s\\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 22 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 23 | } 24 | 25 | function only-run-on() { 26 | # shellcheck disable=SC2086 27 | if [[ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "$(echo $1 | tr '[:upper:]' '[:lower:]')" ]]; then 28 | fail "This script only runs on $1, this machine is running $(uname -s)" 29 | else 30 | debug "OS ($(uname -s)) is valid..." 31 | fi 32 | } 33 | 34 | only-run-on-linux 35 | exec ls -d /proc/"$1"/cwd 36 | -------------------------------------------------------------------------------- /bin/ping5: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2022, Joe Block 4 | 5 | set -o pipefail 6 | if [[ -n "$DEBUG" ]]; then 7 | set -x 8 | fi 9 | 10 | function debug() { 11 | if [[ -n "$DEBUG" ]]; then 12 | echo "$@" 13 | fi 14 | } 15 | 16 | function echo-stderr() { 17 | echo "$@" 1>&2 18 | } 19 | 20 | function fail() { 21 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 22 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 23 | } 24 | 25 | function has() { 26 | # Check if a command is in $PATH 27 | which "$@" > /dev/null 2>&1 28 | } 29 | 30 | if has grc; then 31 | exec grc --colour=auto ping -c 5 "$@" 32 | else 33 | exec ping -c 5 "$@" 34 | fi 35 | -------------------------------------------------------------------------------- /bin/pjson: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Pretty up json 4 | # 5 | # from: https://coderwall.com/p/hwu5uq?i=9&p=1&q=sort%3Ascore+desc&t%5B%5D=zsh 6 | 7 | set -o pipefail 8 | 9 | if [ $# -gt 0 ]; then 10 | for arg in "$@" 11 | do 12 | if [ -f "$arg" ]; then 13 | python -m json.tool < "$arg" 14 | else 15 | echo "$arg" | python -m json.tool 16 | fi 17 | done 18 | fi 19 | -------------------------------------------------------------------------------- /bin/plot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # 3 | # Source: https://gist.github.com/katef/fb4cb6d47decd8052bd0e8d88c03a102 4 | # Author: https://github.com/katef 5 | # 6 | # This program is a copy of guff, a plot device. https://github.com/silentbicycle/guff 7 | # My copy here is written in awk instead of C, has no compelling benefit. 8 | # Public domain. @thingskatedid 9 | 10 | # Run as awk -v x=xyz ... or env variables for stuff? 11 | # Assumptions: the data is evenly spaced along the x-axis 12 | 13 | # TODO: moving average 14 | # TODO: trend lines, or guess at complexities 15 | # TODO: points vs. lines 16 | # TODO: colourblind safe scheme 17 | # TODO: center data around the 0 axis 18 | # TODO: scanning for all float formats input, including -inf, NaN etc 19 | # TODO: guess at whether to use lines or circles, based on delta within a window? 20 | 21 | function hastitle() { 22 | for (i = 1; i <= NF; i++) { 23 | if ($i ~ /[^-0-9.]/) { 24 | return 1 25 | } 26 | } 27 | 28 | return 0 29 | } 30 | 31 | function amax(a, i, max) { 32 | max = -1 33 | 34 | for (i in a) { 35 | if (max == -1 || a[i] > a[max]) { 36 | max = i 37 | } 38 | } 39 | 40 | return max 41 | } 42 | 43 | function normalise( delta) { 44 | for (i = 1; i <= NF; i++) { 45 | max[i] = 0 46 | min[i] = 0 47 | 48 | for (j = 1; j <= NR; j++) { 49 | if (a[i, j] > max[i]) { 50 | max[i] = a[i, j] 51 | } else 52 | if (a[i, j] < min[i]) { 53 | min[i] = a[i, j] 54 | } 55 | } 56 | 57 | delta[i] = max[i] - min[i] 58 | 59 | for (j = 1; j <= NR; j++) { 60 | a[i, j] -= min[i] 61 | if (delta[i] > 0) { 62 | a[i, j] /= delta[i] 63 | } 64 | } 65 | } 66 | 67 | # TODO: rescale to center around 0 68 | 69 | # Here the data is squished slightly in descending order of deltas. 70 | # Each column is scaled independently anyway, so they're never to scale. 71 | # The idea here is to help show intutively which are smaller, but without 72 | # actually drawing them to size (since then very small deltas would not be 73 | # visible at all). 74 | 75 | k = 0 76 | prev = -1 77 | 78 | while (length(delta) > 0) { 79 | i = amax(delta) 80 | 81 | # Several columns can share the same delta 82 | # Formatting to %.3f here is just for sake of rounding 83 | if (prev != -1 && sprintf("%.3f", prev) != sprintf("%.3f", delta[i])) { 84 | k++ 85 | } 86 | 87 | # +2 to squish things upwards a bit 88 | scale = (NF + 2 - k) / (NF + 2) 89 | 90 | # there's no need to scale by 1 91 | if (scale != 1) { 92 | for (j = 1; j <= NR; j++) { 93 | a[i, j] *= scale 94 | } 95 | } 96 | 97 | prev = delta[i] 98 | delete delta[i] 99 | } 100 | } 101 | 102 | # internal coordinates to svg coordinates 103 | function point(x, y) { 104 | x = x * (chart_width - 2 * xmargin) + xmargin 105 | y = (height - 2 * ymargin) - y * (height - 2 * ymargin) + ymargin 106 | 107 | return sprintf("%u,%u", x, y) 108 | } 109 | 110 | function line(i) { 111 | printf " \n" 119 | } 120 | 121 | function circles(i) { 122 | for (j = 1; j <= NR; j++) { 123 | p = point((j - 1) / NR, a[i, j]) 124 | split(p, q, ",") 125 | printf " \n", 126 | q[1], q[2], color[i], alpha, color[i], alpha 127 | } 128 | } 129 | 130 | function legend_text(i, title) { 131 | printf " \n", chart_width + gutter, i * line_height 132 | printf " \n", 133 | -10, -line_height / 2 + 5, color[i], color[i] 134 | printf " %-*s[%.3g, %.3g]\n", 135 | sprintf("fill: %s; font-size: %upx; font-family: mono", fg, font_size), 136 | (title_width > 0) ? title_width + 1 : 0, title, min[i], max[i] 137 | printf " \n" 138 | } 139 | 140 | function display() { 141 | print "" 142 | printf "\n", 143 | "http://www.w3.org/2000/svg", 144 | chart_width + gutter + legend_width, height 145 | 146 | title_width = 0 147 | for (i = 1; i <= NF; i++) { 148 | if (length(title[i]) > title_width) { 149 | title_width = length(title[i]) 150 | } 151 | } 152 | 153 | for (i = 1; i <= NF; i++) { 154 | # line(i) 155 | circles(i) 156 | } 157 | 158 | if (length(title)) { 159 | for (i = 1; i <= NF; i++) { 160 | legend_text(i, title[i]) 161 | } 162 | } 163 | 164 | print "" 165 | } 166 | 167 | NR == 1 { 168 | if (hastitle()) { 169 | for (i = 1; i <= NF; i++) { 170 | title[i] = $i 171 | } 172 | 173 | NR-- 174 | next 175 | } 176 | } 177 | 178 | { 179 | for (i = 1; i <= NF; i++) { 180 | a[i, NR] = $i 181 | } 182 | } 183 | 184 | END { 185 | fg = "#eeeeee" 186 | alpha = "ff" 187 | 188 | # Bang Wong's colour-safe palette, https://www.nature.com/articles/nmeth.1618 189 | # (using just the last five colours) 190 | color[3] = "#009E73" 191 | color[2] = "#F0E442" 192 | color[1] = "#0072B2" 193 | color[4] = "#CC79A7" 194 | color[5] = "#D55E00" 195 | 196 | color[6] = fg 197 | 198 | if (NF == 1) { 199 | color[1] = fg 200 | } 201 | 202 | if (NF > length(color)) { 203 | print "too many fields" >> "/dev/stderr" 204 | exit 1 205 | } 206 | 207 | chart_width=320 208 | legend_width=300 209 | height=120 210 | xmargin=0 211 | ymargin=5 212 | gutter=30 213 | font_size=15 214 | line_height=20 215 | 216 | # the data is scaled 0..1 for our internal coordinate space 217 | normalise() 218 | 219 | display() 220 | } 221 | -------------------------------------------------------------------------------- /bin/port-listened-by: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This base script is MIT licensed so you can base your 4 | # own work on it. 5 | # 6 | # Find what is listening on a port 7 | # 8 | # Copyright 2025, Joe Block 9 | 10 | set -o pipefail 11 | if [[ -n "$DEBUG" ]]; then 12 | # shellcheck disable=SC2086 13 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 14 | set -x 15 | fi 16 | fi 17 | 18 | function debug() { 19 | if [[ -n "$DEBUG" ]]; then 20 | echo "$@" 21 | fi 22 | } 23 | 24 | function echo-stderr() { 25 | echo "$@" 1>&2 ## Send message to stderr. 26 | } 27 | 28 | function fail() { 29 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 30 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 31 | } 32 | 33 | function has() { 34 | # Check if a command is in $PATH 35 | which "$@" > /dev/null 2>&1 36 | } 37 | 38 | function only-run-on() { 39 | # shellcheck disable=SC2086 40 | if [[ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "$(echo $1 | tr '[:upper:]' '[:lower:]')" ]]; then 41 | fail "This script only runs on $1, this machine is running $(uname -s)" 42 | else 43 | debug "OS ($(uname -s)) is valid..." 44 | fi 45 | } 46 | 47 | function check-dependency() { 48 | if ! (builtin command -V "$1" >/dev/null 2>&1); then 49 | fail "missing dependency: can't find $1 in your PATH" 50 | fi 51 | } 52 | 53 | function check-dependencies() { 54 | debug "Checking dependencies..." 55 | # shellcheck disable=SC2041 56 | # Placeholders for whatever programs you really need 57 | for dep in "$@" 58 | do 59 | if ! has "$dep"; then 60 | fail "Can't find $dep in your $PATH" 61 | else 62 | debug "- Found $dep" 63 | fi 64 | done 65 | } 66 | 67 | function my-name() { 68 | basename "$0" 69 | } 70 | 71 | function usage() { 72 | echo "Usage: $(my-name) PORT" 73 | echo 74 | echo "Uses lsof to see what is listening on PORT" 75 | } 76 | 77 | check-dependencies awk lsof uniq 78 | 79 | lsof -i -P -n | grep "$@" | awk '/LISTEN/ {print $9}' | uniq 80 | -------------------------------------------------------------------------------- /bin/port-listeners-ipv4: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # What programs are listening to what ipv4 ports 4 | 5 | exec lsof -Pnl +M -i4 6 | -------------------------------------------------------------------------------- /bin/port-listeners-ipv6: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # What programs are listening to what ipv6 ports 4 | 5 | exec lsof -Pnl +M -i6 6 | -------------------------------------------------------------------------------- /bin/procs-for-path: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # processes accessing a path 4 | # 5 | # Copyright 2020, Joe Block 6 | 7 | set -o pipefail 8 | 9 | for pid in $(lsof "$*" | cut -d' ' -f 3 | sort | uniq) 10 | do 11 | ps -f -p "$pid" 12 | done 13 | -------------------------------------------------------------------------------- /bin/pushover: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # push notification to a device 4 | 5 | function pushover { 6 | # pipe text to send a notification to a device 7 | local token msg title device user 8 | token="$(sec get pushover_token)" 9 | user="$(sec get pushover_user)" 10 | device="pixel" 11 | title="cmd" 12 | 13 | read msg; 14 | curl https://api.pushover.net/1/messages.json \ 15 | -F "token=$token" \ 16 | -F "user=$user" \ 17 | -F "device=$device" \ 18 | -F "title=$title" \ 19 | -F "message=$msg" 20 | } 21 | 22 | pushover $@ 23 | -------------------------------------------------------------------------------- /bin/pydoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Originally from a Hangops slack thread 4 | 5 | set -o pipefail 6 | if [[ -n "$DEBUG" ]]; then 7 | # shellcheck disable=SC2086 8 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 9 | set -x 10 | fi 11 | fi 12 | 13 | function debug() { 14 | if [[ -n "$DEBUG" ]]; then 15 | echo "$@" 16 | fi 17 | } 18 | 19 | function echo-stderr() { 20 | echo "$@" 1>&2 ## Send message to stderr. 21 | } 22 | 23 | function fail() { 24 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 25 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 26 | } 27 | 28 | function has() { 29 | # Check if a command is in $PATH 30 | which "$@" > /dev/null 2>&1 31 | } 32 | 33 | function get-settings() { 34 | OPENER=${OPENER:-'open'} 35 | } 36 | 37 | function check-dependencies() { 38 | debug "Checking dependencies..." 39 | for c in 'xdg-open' 'open' 40 | do 41 | if has "$c"; then 42 | OPENER="$c" 43 | fi 44 | done 45 | # shellcheck disable=SC2041 46 | for p in "$OPENER" 47 | do 48 | if ! has "$p"; then 49 | fail "Can't find $p in your $PATH" 50 | else 51 | debug "- Found $p" 52 | fi 53 | done 54 | } 55 | 56 | function my-name() { 57 | basename "$0" 58 | } 59 | 60 | function usage() { 61 | echo "Usage: $(my-name) THING" 62 | echo 63 | echo "Looks up THING on docs.python.org and opens it in your default web browser" 64 | } 65 | 66 | function path-exists() { 67 | local file="${1}" 68 | [[ -s "${file}" ]] || fail "$1 is not valid" 69 | [[ -d "${file}" ]] && return 70 | [[ -f "${file}" ]] && return 71 | fail "$1 is not a directory or file" 72 | } 73 | 74 | get-settings 75 | check-dependencies 76 | exec $OPENER "https://docs.python.org/$(python --version | cut -d' ' -f2 | cut -d. -f1,2)/search.html?q=$*" 77 | -------------------------------------------------------------------------------- /bin/ramdisk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Python rewrite since the original bash script had no license and no way to 4 | # specify the size of the RAM disk. 5 | # 6 | # Copyright 2014 Joe Block 7 | # 8 | # Apache 2.0 License (see LICENSE in this repo for further details) 9 | 10 | import argparse 11 | import logging 12 | import os 13 | import sys 14 | 15 | from subprocess import check_call, check_output 16 | 17 | g_CLIargs = None 18 | 19 | def darwinRAMDisk(args=None): 20 | """We're running on OS X, create a RAMDisk""" 21 | if args.megabytes: 22 | blocks = int(args.megabytes) * 2048 23 | if args.gigabytes: 24 | blocks = int(args.gigabytes) * 1024 * 2048 25 | deviceCreateCommand = ("hdiutil attach -nomount ram://%s" % blocks).split(' ') 26 | rawVolume = check_output(deviceCreateCommand).strip() 27 | check_output(['diskutil', 'erasevolume', 'HFS+', args.name, rawVolume]) 28 | 29 | 30 | def linuxRAMDisk(args=None): 31 | """Running on Linux, create a RAM disk""" 32 | if not os.path.is_dir(args.name): 33 | print "%s is not a directory" % args.name 34 | if args.megabytes: 35 | size="size=%sm" % args.megabytes 36 | if args.gigabytes: 37 | size="size=%sg" % args.gigabytes 38 | check_output(['mount', '-t', 'ramfs', '-o', size, 'ramfs', args.name]) 39 | 40 | 41 | def setupRAMdisk(args=None): 42 | """Get the operating system flavor""" 43 | if sys.platform.startswith('darwin'): 44 | darwinRAMDisk(args) 45 | elif sys.platform.startswith('linux'): 46 | linuxRAMDisk(args) 47 | else: 48 | print "Can't create RAM disk on %s" % sys.platform 49 | sys.exit(1) 50 | 51 | 52 | if __name__ == "__main__": 53 | parser = argparse.ArgumentParser(description="RAMDisk creator") 54 | parser.add_argument("--name", 55 | action="store", 56 | dest="name", 57 | help="Specify the name of the RAM disk on OSX, or the path to mountpoint on FreeBSD or Linux") 58 | parser.add_argument("--megabytes", 59 | action="store", 60 | dest="megabytes", 61 | help="Size of RAM disk in megabytes") 62 | parser.add_argument("--gigabytes", 63 | action="store", 64 | dest="gigabytes", 65 | help="Size of RAM disk in gigabytes") 66 | args = parser.parse_args() 67 | 68 | # sanity check arguments 69 | sane = True 70 | if not args.name: 71 | print "You must specify a name for the RAM disk with --name." 72 | sane = False 73 | if (not args.megabytes) and (not args.gigabytes): 74 | print "You must specify the disk size with either --megabytes or --gigabytes." 75 | sane = False 76 | if args.megabytes and args.gigabytes: 77 | print "Please specify either megabytes or gigabytes, not both." 78 | sane = False 79 | if not sane: 80 | sys.exit(1) 81 | setupRAMdisk(args) 82 | -------------------------------------------------------------------------------- /bin/random-password: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Author: Joe Block 4 | # License: Apache 2.0 (see LICENSE) 5 | # 6 | # Generate a random password. If no argument, assume 32 characters 7 | 8 | if [ -z "$PASSWORD_SIZE" ]; then 9 | PASSWORD_SIZE=32 10 | fi 11 | 12 | if [ ! -z "$1" ] ; then 13 | PASSWORD_SIZE="$1" 14 | fi 15 | 16 | if [ "$1" == '--help' ]; then 17 | myname=$(basename $0) 18 | echo "${myname}: usage:" 19 | echo 20 | echo "${myname} will generate a random string for use as a password. You can specify a length" 21 | echo "by setting \$PASSWORD_SIZE" 22 | echo 23 | echo "example:" 24 | echo " PASSWORD_SIZE=32 ${myname}" 25 | echo 26 | exit 0 27 | fi 28 | 29 | # sha512 outputs 128 characters 30 | if [ ${PASSWORD_SIZE} -gt 128 ]; then 31 | PASSWORD_SIZE=128 32 | fi 33 | 34 | od -N${PASSWORD_SIZE} -An -i < /dev/urandom | # Get N random bytes from kernel 35 | openssl md5 | # Convert into password-friendly format 36 | head -c ${PASSWORD_SIZE} ; echo # Trim to requested password size 37 | -------------------------------------------------------------------------------- /bin/randsleep: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2013 Joe Block 4 | # 5 | # Sleeps a random number of seconds to keep tasks from becoming a 6 | # thundering herd 7 | 8 | LIMIT=600 9 | if [ -n "$1" ] ; then 10 | LIMIT=$1 11 | fi 12 | 13 | if [ "$1" == "--help" ] ; then 14 | # shellcheck disable=SC2086 15 | echo "$(basename $0) [max time to sleep in seconds]" 16 | echo "time to sleep is in seconds, defaults to 600" 17 | echo "Will sleep between zero and LIMIT" 18 | exit 1 19 | fi 20 | 21 | # shellcheck disable=SC2004 22 | sleep $(( RANDOM % ${LIMIT} )) 23 | -------------------------------------------------------------------------------- /bin/read-windows-key-from-bios: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # From https://twitter.com/ProfFalken/status/653580127742394369 4 | # 5 | # Read the windows key out of bios 6 | 7 | exec sudo hexdump -s 56 -e '"MSDM key: " /29 "%s\n"' /sys/firmware/acpi/tables/MSDM 8 | -------------------------------------------------------------------------------- /bin/relocate-virtualenv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # fix_virtualenv_location.sh 4 | # 5 | # Author: Gary M. Josack 6 | # Repository: https://github.com/gmjosack/scripts 7 | # License: MIT 8 | # 9 | # This is a simple script to clean up links and references in a 10 | # python virtualenv that has been relocated. 11 | 12 | function wrap_color(){ 13 | local red="\e[31m" 14 | local blue="\e[34m" 15 | local reset="\e[0m" 16 | 17 | local color="$1"; shift 18 | local data="$*" 19 | local output="" 20 | 21 | case "$color" in 22 | red) output="${red}${data}${reset}" ;; 23 | blue) output="${blue}${data}${reset}" ;; 24 | *) output="${data}" ;; 25 | esac 26 | 27 | echo "$output" 28 | } 29 | 30 | 31 | function log(){ 32 | local name="$1" 33 | local line="$2" 34 | local stream="${3:-stdout}" 35 | local color="$4" 36 | 37 | local msg="[$name] $line" 38 | [[ -n "$color" ]] && msg=$(wrap_color "$color" "$msg") 39 | 40 | if [[ "$stream" == "stderr" ]]; then 41 | echo -e $msg 1>&2 42 | else 43 | echo -e $msg 44 | fi 45 | } 46 | 47 | 48 | function info(){ 49 | log "info" "$*" "stdout" "blue" 50 | } 51 | 52 | 53 | function err(){ 54 | log "error" "$*" "stderr" "red" 55 | } 56 | 57 | 58 | function die(){ 59 | err "$*" 60 | exit 1 61 | } 62 | 63 | 64 | function abspath(){ 65 | echo $(cd "$1"; pwd) 66 | } 67 | 68 | 69 | function is_absolute(){ 70 | [[ "${1:0:1}" == "/" ]] && return 0 || return 1 71 | } 72 | 73 | 74 | function add_trailing_slash(){ 75 | [[ "${1:(-1)}" == "/" ]] && echo "$1" || echo "$1/" 76 | } 77 | 78 | 79 | function rm_trailing_slash(){ 80 | [[ "${1:(-1)}" == "/" ]] && echo "${1:0:-1}" || echo "$1" 81 | } 82 | 83 | 84 | function usage(){ 85 | local preamble="$1" 86 | local prog=$(basename $0) 87 | 88 | [[ -n "$preamble" ]] && err "${preamble}\n" 89 | echo "Usage: ${PROG} " 90 | echo " - The name of the old path where the virtualenv existed. This" 91 | echo " location does not need to exist. Must be absolute." 92 | echo " - The name of the new path where the virtualenv will exist. This" 93 | echo " location does not need to exist. Must be absolute." 94 | echo " - The path of the virtualenv to modify." 95 | 96 | exit 1 97 | } 98 | 99 | 100 | function fix_bad_symlinks(){ 101 | local old_path="$1" 102 | local new_path="$2" 103 | local search_path="$3" 104 | 105 | info "Fixing symlinks..." 106 | for symlink in $(find "$search_path" -type l); do 107 | local value=$(readlink "${symlink}") 108 | if [[ "$value" == *"$old_path"* ]]; then 109 | info "Found $symlink pointing $value. Correcting..." 110 | ln -sTf "${value/$old_path/$new_path}" "$symlink" 111 | fi 112 | 113 | done 114 | } 115 | 116 | 117 | function purge_pycs(){ 118 | local search_path="$1" 119 | 120 | info "Purging the following *.pyc files..." 121 | find -P "$search_path" -type f -name "*.pyc" -printf "\t%p\n" -exec rm -f {} \; 122 | } 123 | 124 | 125 | function rename_refs(){ 126 | local old_path=$(rm_trailing_slash "$1") 127 | local new_path=$(rm_trailing_slash "$2") 128 | local search_path=$(rm_trailing_slash "$3") 129 | 130 | local old_pattern="${old_path}(/|\"|$|')" 131 | local new_pattern="${new_path}(/|\"|$|')" 132 | 133 | info "Replacing references of ${old_path} with ${new_path}..." 134 | 135 | for file in $(find -P "${search_path}" \! -type l -type f | xargs grep -El "$old_pattern" | xargs grep -lv "$new_pattern"); do 136 | [[ -L "$file" ]] && continue # Ignore symlinks. 137 | file -b "$file" | grep -vq ASCII && continue # Skip non-ascii files. 138 | info "Updating ${file}" 139 | sed -i -e "s:${old_path}:${new_path}:g" "$file" 140 | done 141 | } 142 | 143 | function main(){ 144 | [[ "$#" -eq 3 ]] || usage "Invalid number of parameters." 145 | 146 | local old_path="$1" 147 | local new_path="$2" 148 | local virtualenv="$3" 149 | 150 | virtualenv=$(abspath "$virtualenv") 151 | 152 | old_path=$(add_trailing_slash "$old_path") 153 | new_path=$(add_trailing_slash "$new_path") 154 | virtualenv=$(add_trailing_slash "$virtualenv") 155 | 156 | # Simple sanity checks that the path exists and 157 | # appears to be a virtual env. 158 | is_absolute "$old_path" || usage "$old_path is not absolute." 159 | is_absolute "$new_path" || usage "$new_path is not absolute." 160 | [[ -d "$virtualenv" ]] || usage "No such directory: ${virtualenv}" 161 | [[ -f "$virtualenv/bin/activate" ]] || usage "Doesn't appear to be a virtualenv: ${virtualenv}" 162 | 163 | fix_bad_symlinks "$old_path" "$new_path" "$virtualenv" 164 | purge_pycs "$virtualenv" 165 | rename_refs "$old_path" "$new_path" "$virtualenv" 166 | 167 | } 168 | main "$@" 169 | -------------------------------------------------------------------------------- /bin/remote-packet-capture: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Wrap wireshark remote capture 4 | # 5 | # Copyright 2023, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | if [[ "$DEBUG" == "VERBOSE" ]]; then 10 | set -x 11 | fi 12 | fi 13 | 14 | function debug() { 15 | if [[ -n "$DEBUG" ]]; then 16 | echo "$@" 17 | fi 18 | } 19 | 20 | function echo-stderr() { 21 | echo "$@" 1>&2 ## Send message to stderr. 22 | } 23 | 24 | function fail() { 25 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 26 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 27 | } 28 | 29 | function has() { 30 | # Check if a command is in $PATH 31 | which "$@" > /dev/null 2>&1 32 | } 33 | 34 | function check-dependencies() { 35 | debug "Checking dependencies..." 36 | for p in 'wireshark' 37 | do 38 | if ! has $p; then 39 | fail "Can't find $p in your \$PATH" 40 | else 41 | debug "- Found $p" 42 | fi 43 | done 44 | } 45 | 46 | check-dependencies 47 | 48 | # Wireshark remote capture 49 | host=${1} 50 | ignore_port=${2-22} 51 | ssh ${host} tcpdump -U -s0 -w - "not port ${ignore_port}" | wireshark -k -i - 52 | exit $? 53 | -------------------------------------------------------------------------------- /bin/remote-shark: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Run wireshark on a remote host 4 | # 5 | # Copyright 2023, Joe Block 6 | 7 | set -o pipefail 8 | 9 | host=${1} 10 | port=${2-22} 11 | ssh "${host}" tcpdump -U -s0 -w - "not port ${port}" | wireshark -k -i - 12 | exit $? 13 | -------------------------------------------------------------------------------- /bin/repair-zsh-history: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Repair your ZSH history 4 | # 5 | # Copyright 2020, Joe Block 6 | 7 | if [[ -n "$DEBUG" ]]; then 8 | set -x 9 | fi 10 | 11 | # Set up a working scratch directory 12 | TEMP_REPAIR_D=$(mktemp -d) 13 | 14 | if [[ ! "$TEMP_REPAIR_D" || ! -d "$TEMP_REPAIR_D" ]]; then 15 | echo "Could not create temp dir" 16 | exit 1 17 | fi 18 | 19 | cleanup() { 20 | if [[ -d "$TEMP_REPAIR_D" ]]; then 21 | rm -fr "$TEMP_REPAIR_D" 22 | fi 23 | } 24 | 25 | fail() { 26 | echo "$@" 27 | cleanup 28 | exit 1 29 | } 30 | 31 | strings ~/.zsh_history > "$TEMP_REPAIR_D"/.zsh_history || fail "Couldn't run strings on ~/.zsh_history, sorry" 32 | cat "$TEMP_REPAIR_D"/.zsh_history > ~/.zsh_history 33 | cleanup 34 | -------------------------------------------------------------------------------- /bin/retry: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Run a command over and over until it exits 0. Wait $DELAY seconds between 4 | # attempts. 5 | # 6 | # I don't remember whether I wrote this or someone gave it to me. Call it 7 | # BSD licensed until/unless someone claims ownership. 8 | 9 | if [ -z "$DELAY" ]; then 10 | DELAY=5 11 | fi 12 | 13 | if [ "$1" == "--help" ] ; then 14 | echo "$(basename $0) command" 15 | echo "or" 16 | echo "MAXRETRIES=5 DELAY=30 $(basename $0) command" 17 | echo 18 | echo "Keep trying command until it succeeds. Wait DELAY seconds between attempts" 19 | echo 20 | exit 1 21 | fi 22 | 23 | attempts=0 24 | while true 25 | do 26 | (( attempts = $attempts +1 )) 27 | $@ 28 | if [[ $? == 0 ]]; then 29 | exit 0 30 | else 31 | echo -n "'$@' attempt $attempts failed; waiting to retry..." 32 | if [ ! -z $MAXRETRIES ]; then 33 | if (( $attempts >= $MAXRETRIES )); then 34 | echo "max retries was $MAXRETRIES, $attempts failed attempts, exiting..." 35 | exit 1 36 | fi 37 | fi 38 | echo 39 | sleep ${DELAY} 40 | fi 41 | done 42 | -------------------------------------------------------------------------------- /bin/rpmburst: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Burst an rpm. Does it in the current directory, so look out. 4 | 5 | if [ ! -f "${1}" ]; then 6 | echo "${1} not found!" 7 | exit 1 8 | fi 9 | 10 | exec rpm2cpio "${1}" | cpio -idmv 11 | -------------------------------------------------------------------------------- /bin/seq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # http://www.askdavetaylor.com/step_through_count_numeric_values_bash_shell_script.html 4 | # seq - step through numeric values until you get to the max value 5 | 6 | if [ "$#" -lt 2 ] ; then 7 | echo "Usage: seq low high - generate list of integers from low to high" 8 | echo "Usage: seq low high increment - Generate list of integers from low to high, bumping by interval" 9 | exit 1 10 | fi 11 | 12 | counter="$1" 13 | max="$2" 14 | 15 | if [ "$#" -eq 2 ] ; then 16 | step=1 17 | else 18 | step="$3" 19 | fi 20 | 21 | while [ "$counter" -le "$max" ] ; do 22 | echo "$counter" 23 | # shellcheck disable=SC2003 24 | counter=$( expr "$counter" + "$step" ) 25 | done 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /bin/shovel-gpg-keys: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Apache 2 license, if it matters. 4 | # 5 | # Author: Joe Block 6 | 7 | KEYSERVERS="pgp.mit.edu keyserver.ubuntu.com pool.sks-keyservers.net" 8 | 9 | for keyserver in $KEYSERVERS 10 | do 11 | gpg --keyserver ${keyserver} --send-key $@ 12 | done 13 | -------------------------------------------------------------------------------- /bin/show-cursor: -------------------------------------------------------------------------------- 1 | cursor-show -------------------------------------------------------------------------------- /bin/snag-dl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Move the most recent file in Downloads into the current directory. 4 | 5 | path="$(ls -t ~/Downloads/* | head -1)" 6 | echo $(basename "$path") 7 | mv "$path" . 8 | -------------------------------------------------------------------------------- /bin/solo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl -s 2 | # 3 | # solo v1.5 4 | # Prevents multiple cron instances from running simultaneously. 5 | # 6 | # Copyright 2007-2010 Timothy Kay 7 | # http://timkay.com/solo/ 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | use Socket; 24 | 25 | alarm $timeout if $timeout; 26 | 27 | $port =~ /^\d+$/ or $noport or die "Usage: $0 -port=PORT COMMAND\n"; 28 | 29 | if ($port) 30 | { 31 | $addr = pack(CnC, 127, $<, 1); 32 | print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n" if $verbose; 33 | 34 | $^F = 10; # unset close-on-exec 35 | 36 | socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "socket: $!"; 37 | bind(SOLO, sockaddr_in($port, $addr)) or $silent? exit: die "solo($port): $!\n"; 38 | } 39 | 40 | sleep $sleep if $sleep; 41 | 42 | exec @ARGV; 43 | -------------------------------------------------------------------------------- /bin/ssh-remove-known-host: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Helper script to remove entries for ~/.ssh/known hosts 4 | # 5 | # I can never remember the command to remove a host, and since the format update, you can't just edit `~/.ssh/known_hosts` and search for the IP/hostname. 6 | # Not sure why they switched to a new format that's more pain in the ass. 7 | # 8 | # Copyright 2023, Joe Block 9 | # License: BSD 10 | 11 | set -o pipefail 12 | if [[ -n "$DEBUG" ]]; then 13 | # shellcheck disable=SC2086 14 | if [[ "$(echo $DEBUG | tr '[:upper:]' '[:lower:]')" == "verbose" ]]; then 15 | set -x 16 | fi 17 | fi 18 | 19 | function debug() { 20 | if [[ -n "$DEBUG" ]]; then 21 | echo "$@" 22 | fi 23 | } 24 | 25 | function echo-stderr() { 26 | echo "$@" 1>&2 ## Send message to stderr. 27 | } 28 | 29 | function fail() { 30 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 31 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 32 | } 33 | 34 | function has() { 35 | # Check if a command is in $PATH 36 | which "$@" > /dev/null 2>&1 37 | } 38 | 39 | function check-dependencies() { 40 | debug "Checking dependencies..." 41 | # shellcheck disable=SC2041 42 | for p in 'ssh-keygen' 43 | do 44 | if ! has $p; then 45 | fail "Can't find $p in your $PATH" 46 | else 47 | debug "- Found $p" 48 | fi 49 | done 50 | } 51 | 52 | function my-name() { 53 | basename "$0" 54 | } 55 | 56 | function usage() { 57 | echo "Usage: $(my-name) dns_or_ip" 58 | echo "Deletes a host or IP from your known_hosts file" 59 | } 60 | 61 | check-dependencies 62 | 63 | exec ssh-keygen -f ~/.ssh/known_hosts -R "$@" 64 | -------------------------------------------------------------------------------- /bin/ssl-cert-check: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Check an SSL certificate 4 | # 5 | # Copyright 2023, Joe Block 6 | 7 | set -o pipefail 8 | if [[ -n "$DEBUG" ]]; then 9 | set -x 10 | fi 11 | 12 | function debug() { 13 | if [[ -n "$DEBUG" ]]; then 14 | echo "$@" 15 | fi 16 | } 17 | 18 | function echo-stderr() { 19 | echo "$@" 1>&2 ## Send message to stderr. 20 | } 21 | 22 | function fail() { 23 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 24 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 25 | } 26 | 27 | function has() { 28 | # Check if a command is in $PATH 29 | which "$@" > /dev/null 2>&1 30 | } 31 | 32 | if ! has openssl; then 33 | fail "Can't find 'openssl' in PATH" 34 | fi 35 | 36 | function usage() { 37 | # shellcheck disable=SC2086 38 | my_name="$(basename $0)" 39 | echo "Usage:" 40 | echo "$my_name DOMAIN [PORT]" 41 | echo 42 | } 43 | 44 | if [[ "$1" == "--help" ]]; then 45 | usage 46 | exit 0 47 | fi 48 | 49 | if [[ $# == 0 ]]; then 50 | usage 51 | exit 0 52 | fi 53 | 54 | DOMAIN="$1" 55 | PORT=${2:-"443"} 56 | debug "DOMAIN: $DOMAIN" 57 | debug "PORT: $PORT" 58 | 59 | echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:$PORT" | openssl x509 -noout -dates 60 | exit $? 61 | -------------------------------------------------------------------------------- /bin/steal: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Steal ownership of a file so I don't have to keep remembering to sudo it 4 | # 5 | # Copyright 2018 Joe Block 6 | # 7 | # This is released under the Apache 2.0 license 8 | 9 | set -o pipefail 10 | 11 | if [[ -n "${SUDO_USER}" ]]; then 12 | NEW_OWNER="${SUDO_USER}" 13 | else 14 | NEW_OWNER="${USER}" 15 | fi 16 | 17 | sudo chown "${NEW_OWNER}" "$@" 18 | -------------------------------------------------------------------------------- /bin/strip-ansi-codes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2022, Joe Block 4 | # License: Apache 2.0 5 | # 6 | # Strip the ansi codes out of STDIN. 7 | # This makes jenkins build logs considerably less painful to read and grep through. 8 | 9 | exec sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" 10 | -------------------------------------------------------------------------------- /bin/tableflip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo -en "( º_º) ┬─┬ \r" 4 | sleep .2 5 | echo -en " ( º_º) ┬─┬ \r" 6 | sleep .2 7 | echo -en " ( ºДº)┬─┬ \r" 8 | sleep .4 9 | echo -en " (╯'Д')╯︵⊏ \r" 10 | sleep .2 11 | echo -en " (╯'□')╯︵ ⊏ \r" 12 | sleep .1 13 | echo " (╯°□°)╯︵ ┻━┻ " 14 | sleep .4 15 | -------------------------------------------------------------------------------- /bin/title-text: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Set iTerm2 tab title text 4 | # 5 | # Copyright 2018, Joe Block 6 | # 7 | # This is released under the Apache 2.0 license. See LICENSE in this 8 | # repository for details 9 | 10 | echo -ne "\033]0;"$*"\007" 11 | -------------------------------------------------------------------------------- /bin/urldecode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # urldecode 4 | # 5 | # Copyright 2022, Joe Block 6 | 7 | import sys 8 | import urllib.parse 9 | 10 | 11 | def urldecode(data: str): 12 | """ 13 | Decode an url 14 | """ 15 | print(urllib.parse.unquote(data)) 16 | 17 | 18 | if __name__ == "__main__": 19 | urldecode(sys.argv[1]) 20 | -------------------------------------------------------------------------------- /bin/urlencode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # urlencode 4 | # 5 | # Copyright 2022, Joe Block 6 | 7 | import sys 8 | import urllib.parse 9 | 10 | 11 | def urlencode(data: str): 12 | """ 13 | Encode data as an url 14 | """ 15 | print(urllib.parse.quote_plus(data)) 16 | 17 | 18 | if __name__ == "__main__": 19 | urlencode(sys.argv[1]) 20 | -------------------------------------------------------------------------------- /bin/vbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec osascript -e 'tell application "VirtualBox" to activate' 4 | -------------------------------------------------------------------------------- /bin/whap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """This small script offers a python equivalent to "which" 4 | 5 | For every argument on the command line it lists 6 | all python files and all directories with that name 7 | in each of the directories in sys.path 8 | 9 | If a name is found more than once each is listed 10 | (but python will only import one, probably the first listed) 11 | """ 12 | 13 | _copyright = """ 14 | (c) J Alan Brogan 2013 15 | The source is released under the MIT license 16 | See http://jalanb.mit-license.org/ for more information 17 | """ 18 | 19 | 20 | import os 21 | import sys 22 | import fnmatch 23 | import importlib 24 | 25 | 26 | def directory_list(path): 27 | """A list of all items in that path 28 | 29 | If path is not a directory, an empty list 30 | """ 31 | if not os.path.isdir(path): 32 | return [] 33 | return os.listdir(path) 34 | 35 | 36 | def is_file_in(path, name): 37 | """Whether that name is a file in the directory at that path""" 38 | path_to_name = os.path.join(path, name) 39 | return os.path.isfile(path_to_name) 40 | 41 | 42 | def is_matching_file_in(path, name, glob): 43 | """Whether that name is a file in path and matches that glob""" 44 | return is_file_in(path, name) and fnmatch.fnmatch(name, glob) 45 | 46 | 47 | def path_to_module(path, name): 48 | """Whether the name matches a python source or compiled file in that path 49 | 50 | If source and compiled files are found, give the source 51 | """ 52 | glob = '%s.py*' % name 53 | python_files = [f 54 | for f in directory_list(path) 55 | if is_matching_file_in(path, f, glob)] 56 | if not python_files: 57 | return None 58 | source_files = [f for f in python_files if os.path.splitext(f)[-1] == '.py'] 59 | python_file = source_files and source_files[0] or python_files[0] 60 | return os.path.join(path, python_file) 61 | 62 | 63 | def path_to_sub_directory(path, name): 64 | """If name is a real sub-directory of path, return that""" 65 | result = os.path.join(path, name) 66 | if os.path.isdir(result): 67 | return result 68 | 69 | 70 | def path_to_python(path, name): 71 | """Path to either a module or sub-dir of that path, with that name""" 72 | result = path_to_sub_directory(path, name) 73 | if result: 74 | return result 75 | return path_to_module(path, name) 76 | 77 | 78 | def built_in(name): 79 | """Whether the name is that of one of python's builtin modules""" 80 | try: 81 | # Not all builtin modules are initially imported, so bring it in first 82 | __import__(name) 83 | except ImportError: 84 | return False 85 | return '(built-in)' in str(sys.modules[name]) 86 | 87 | 88 | def path_to_import(string): 89 | module = importlib.import_module(string) 90 | if module: 91 | path_to_file = module.__file__ 92 | if '.egg/' in path_to_file: 93 | return path_to_file.split('.egg/')[0] + '.egg' 94 | return path_to_file 95 | return None 96 | 97 | 98 | def main(strings): 99 | """Run the program""" 100 | for string in strings: 101 | if built_in(string): 102 | print 'builtin', string 103 | path_to_imported_module = path_to_import(string) 104 | if path_to_imported_module: 105 | os.system('ls -ld %s' % path_to_imported_module) 106 | 107 | 108 | if __name__ == '__main__': 109 | args = sys.argv[1:] 110 | if not args: 111 | from what import test 112 | sys.exit(test()) 113 | else: 114 | sys.exit(main(args)) 115 | -------------------------------------------------------------------------------- /bin/what: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | """Show the text behind commands 4 | 5 | This script is intended to replace the standard which command 6 | It should look for commands in aliases, bash functions, and the bash $PATH 7 | It assumes aliases and functions have been written to files before starting 8 | (because we cannot reliably get them from sub-shells called hence) 9 | """ 10 | 11 | _copyright = """ 12 | (c) J Alan Brogan 2013 13 | The source is released under the MIT license 14 | See http://jalanb.mit-license.org/ for more information 15 | """ 16 | 17 | 18 | import os 19 | import re 20 | import sys 21 | import stat 22 | import doctest 23 | import optparse 24 | import subprocess 25 | 26 | 27 | def get_options(): 28 | """The values of options set by user on command line""" 29 | return None 30 | 31 | 32 | def environment_value(key): 33 | """A value from the shell environment, defaults to empty string 34 | 35 | >>> environment_value('SHELL') is not None 36 | True 37 | """ 38 | return os.environ.get(key, '') 39 | 40 | 41 | class BashError(ValueError): 42 | """Use this class to have better name appear in tracebacks""" 43 | pass 44 | 45 | 46 | class Bash(object): 47 | """This class is a namespace to hold bash commands to be used later""" 48 | # pylint wants an __init__(), but I don't 49 | # pylint: disable=no-init 50 | view_file = 'vimcat' # https://github.com/vim-scripts/vimcat, YMMV 51 | declare_f = 'declare -f' # This is a bash builtin 52 | ls = 'ls' # This is often in path, and more often aliased 53 | 54 | 55 | def replace_alias(command): 56 | """Replace any alias with its value at start of the command""" 57 | if ' ' not in command: 58 | return command 59 | command, arguments = command.split(' ', 1) 60 | return '%s %s' % (get_alias(command), arguments) 61 | 62 | 63 | def bash_executable(): 64 | """The first executable called 'bash' in the $PATH""" 65 | return file_in_environment_path('bash') 66 | 67 | 68 | def show_output_of_shell_command(command): 69 | """Run the given command using bash""" 70 | command = replace_alias(command) 71 | bash_command = [bash_executable(), '-c', command] 72 | process = subprocess.Popen( 73 | bash_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 74 | stdout, stderr = process.communicate() 75 | if not process.returncode: 76 | print stdout 77 | if get_options().hide_errors: 78 | return 79 | if not stderr: 80 | return 81 | raise BashError(''' 82 | command: %r 83 | status: %s 84 | stderr: %s 85 | stdout: %s''' % (command, process.returncode, stderr, stdout)) 86 | 87 | 88 | def strip_quotes(string): 89 | """Remove quotes from front and back of string 90 | 91 | >>> strip_quotes('"fred"') == 'fred' 92 | True 93 | """ 94 | if not string: 95 | return string 96 | first = string[0] 97 | last = string[-1] 98 | if first == last and first in '"\'': 99 | return string[1:-1] 100 | return string 101 | 102 | 103 | def memoize(method): 104 | """Cache the return value of the method, which takes no arguments""" 105 | cache = [] # use a list to workaround some warnings from pylint 106 | 107 | def new_method(): 108 | if not cache: 109 | cache.append(method()) 110 | return cache[0] 111 | new_method.__doc__ = method.__doc__ 112 | new_method.__name__ = 'memoized_%s' % method.__name__ 113 | return new_method 114 | 115 | 116 | @memoize 117 | def get_aliases(): 118 | """Read a dictionary of aliases from a file""" 119 | try: 120 | lines = [l.rstrip() for l in file(get_options().aliases)] 121 | except IOError: 122 | return {} 123 | alias_lines = [l[6:] for l in lines if l.startswith('alias ')] 124 | alias_strings = [l.split('=', 1) for l in alias_lines] 125 | alias_commands = [(n, strip_quotes(c)) for (n, c) in alias_strings] 126 | return dict(alias_commands) 127 | 128 | 129 | def get_alias(string): 130 | """Give the alias for that string, or the string itself""" 131 | return get_aliases().get(string, string) 132 | 133 | 134 | @memoize 135 | def get_functions(): 136 | """Read a dictionary of functions from a known file""" 137 | try: 138 | lines = [l.rstrip() for l in file(get_options().functions)] 139 | except IOError: 140 | return {} 141 | name = function_lines = None 142 | functions = {} 143 | for line in lines: 144 | if line == '{': 145 | continue 146 | elif line == '}': 147 | functions[name] = function_lines[:] 148 | else: 149 | words = line.split() 150 | if not words: 151 | continue 152 | if len(words) == 2 and words[1] == '()': 153 | name = words[0] 154 | function_lines = [] 155 | continue 156 | function_lines.append(line) 157 | result = {} 158 | for name, lines in functions.iteritems(): 159 | result[name] = '%s ()\n{\n%s\n}\n' % (name, '\n'.join(lines)) 160 | return result 161 | 162 | 163 | def environment_paths(): 164 | """A list of paths in the environment's PATH 165 | 166 | >>> '/bin' in environment_paths() 167 | True 168 | """ 169 | return environment_value('PATH').split(':') 170 | 171 | 172 | def contractuser(path): 173 | """Replace the home directory in that path with ~ 174 | 175 | This is the opposite of os.path.expanduser() 176 | 177 | >>> contractuser(os.path.expanduser('~/.bashrc')) == '~/.bashrc' 178 | True 179 | """ 180 | home_symbol = '~' 181 | home = os.path.expanduser(home_symbol) 182 | if path.startswith(home): 183 | return path.replace(home, home_symbol) 184 | return path 185 | 186 | 187 | def items_in(path): 188 | """A list of all items in the given path 189 | 190 | >>> ('local', '/usr/local') in items_in('/usr') 191 | True 192 | """ 193 | try: 194 | return [(name, os.path.join(path, name)) for name in os.listdir(path)] 195 | except OSError: 196 | return [] 197 | 198 | 199 | def files_in(path): 200 | """A list of all files in the given path 201 | 202 | >>> ('bash', '/bin/bash') in files_in('/bin') 203 | True 204 | """ 205 | return [(f, p) for (f, p) in items_in(path) if os.path.isfile(p)] 206 | 207 | 208 | def is_executable(path_to_file): 209 | """Whether the file has any executable bits set 210 | 211 | >>> is_executable(sys.executable) 212 | True 213 | """ 214 | executable_bits = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH 215 | try: 216 | return bool(os.stat(path_to_file).st_mode & executable_bits) 217 | except OSError: 218 | return False 219 | 220 | 221 | def executables_in(path): 222 | """A list of all executable files in the given path""" 223 | return [(f, p) for (f, p) in items_in(path) if is_executable(p)] 224 | 225 | 226 | @memoize 227 | def files_in_environment_path(): 228 | """Gives a dictionary of all executable files in the environment's PATH 229 | 230 | >>> files_in_environment_path()['python'] == sys.executable or True 231 | True 232 | """ 233 | known_filenames = set() 234 | result = {} 235 | for path in environment_paths(): 236 | for filename, path_to_file in executables_in(path): 237 | if filename in known_filenames: 238 | continue 239 | known_filenames.add(filename) 240 | result[filename] = path_to_file 241 | return dict(result) 242 | 243 | 244 | def file_in_environment_path(string): 245 | """Gives the path to string, or string.exe 246 | 247 | >>> file_in_environment_path('python') == sys.executable or True 248 | True 249 | """ 250 | try: 251 | return files_in_environment_path()[string] 252 | except KeyError: 253 | return file_in_environment_path('%s.exe' % string) 254 | 255 | 256 | def showable(language): 257 | """A list of languages whose source files we are interested in viewing""" 258 | return language in ['python', 'python2', 'python3', 'bash', 'sh'] 259 | 260 | 261 | def show_function(command): 262 | """Show a function to the user""" 263 | if not get_options().verbose: 264 | print command, 'is a function' 265 | else: 266 | commands = ' | '.join([ 267 | "shopt -s extglob; . %s; %s %s" % ( 268 | get_options().functions, Bash.declare_f, command), 269 | "sed '1 i\\\n#! /bin/bash\n'", 270 | Bash.view_file 271 | ]) 272 | show_output_of_shell_command(commands) 273 | 274 | 275 | def shebang_command(path_to_file): 276 | """Get the shebang line of that file 277 | 278 | Which is the first line, if that line starts with #! 279 | """ 280 | try: 281 | first_line = file(path_to_file).readlines()[0] 282 | if first_line.startswith('#!'): 283 | return first_line[2:].strip() 284 | except (IndexError, IOError): 285 | pass 286 | return '' 287 | 288 | 289 | def extension_language(path_to_file): 290 | """Guess the language used to run a file from its extension""" 291 | _, extension = os.path.splitext(path_to_file) 292 | known_languages = {'.py': 'python', '.sh': 'bash'} 293 | return known_languages.get(extension, None) 294 | 295 | 296 | def shebang_language(path_to_file): 297 | """Guess the language used to run a file from its shebang line""" 298 | run_command = shebang_command(path_to_file) 299 | command_words = re.split('[ /]', run_command) 300 | try: 301 | last_word = command_words[-1] 302 | except IndexError: 303 | last_word = None 304 | return last_word 305 | 306 | 307 | def script_language(path_to_file): 308 | """Guess the language used to run a file from its first line 309 | 310 | The language should be the last word on the shebang line (if present) 311 | If no shebang line is found, try an extension 312 | 313 | >>> script_language('what.py') == 'python' 314 | True 315 | >>> script_language('script.sh') == 'bash' 316 | True 317 | """ 318 | for get_language in [shebang_language, extension_language]: 319 | language = get_language(path_to_file) 320 | if language: 321 | return language 322 | 323 | 324 | def show_command_in_path(command): 325 | """Show a command which is a file in $PATH""" 326 | path_to_command = file_in_environment_path(command) 327 | show_path_to_command(path_to_command) 328 | 329 | 330 | def show_path_to_command(path_to_command): 331 | """Show a command which is a file at that path""" 332 | if get_options().file: 333 | print os.path.realpath(path_to_command) 334 | return 335 | show_output_of_shell_command('%s -l %r' % (Bash.ls, path_to_command)) 336 | if not get_options().verbose: 337 | return 338 | language = script_language(path_to_command) 339 | if showable(language): 340 | show_output_of_shell_command('%s %r' % ( 341 | Bash.view_file, str(path_to_command))) 342 | 343 | 344 | def show_alias(command): 345 | """Show a command defined by alias""" 346 | aliases = get_aliases() 347 | print 'alias %s=%r' % (command, aliases[command]) 348 | if not get_options().verbose: 349 | return 350 | sub_command = aliases[command].split()[0].strip() 351 | if os.path.dirname(sub_command) in environment_paths(): 352 | show_command(os.path.basename(sub_command)) 353 | else: 354 | show_command(sub_command) 355 | 356 | 357 | def show_command(command): 358 | """Show whatever is behind a command""" 359 | methods = [ 360 | (lambda x: x in get_aliases(), show_alias), 361 | (lambda x: x in get_functions(), show_function), 362 | (lambda x: x in files_in_environment_path(), show_command_in_path), 363 | (os.path.isfile, show_path_to_command), 364 | ] 365 | for found, show in methods: 366 | if found(command): 367 | if not get_options().quiet: 368 | show(command) 369 | return 0 370 | return 1 371 | 372 | 373 | def nearby_file(named_file, extension): 374 | """Return the name of that file, changed to use that extension 375 | 376 | >>> os.path.basename(nearby_file('what.pyc', '.txt')) == 'what.txt' 377 | True 378 | """ 379 | return os.path.splitext(named_file)[0] + extension 380 | 381 | 382 | def read_command_line(): 383 | """Look for options from user on the command line for this script""" 384 | parser = optparse.OptionParser( 385 | 'Usage: what [options] command\n\n%s' % __doc__) 386 | parser.add_option('-e', '--hide_errors', action='store_true', 387 | help='hide error messages from successful commands') 388 | parser.add_option('-f', '--file', action='store_true', 389 | help='show real path to file (if it is a file)') 390 | parser.add_option('-q', '--quiet', action='store_true', 391 | help='do not show any output') 392 | parser.add_option('-v', '--verbose', action='store_true', 393 | help='whether to show more info, such as file contents') 394 | parser.add_option('-A', '--aliases', default='/tmp/aliases', 395 | help='path to file which holds aliases') 396 | parser.add_option('-F', '--functions', default='/tmp/functions', 397 | help='path to file which holds functions') 398 | parser.add_option('-U', '--debugging', action='store_true', 399 | help='debug with pudb') 400 | options, arguments = parser.parse_args() 401 | if options.debugging: 402 | import pudb 403 | pudb.set_trace() 404 | # plint does not seem to notice that methods are globals 405 | # pylint: disable=global-variable-undefined 406 | global get_options 407 | get_options = lambda: options 408 | return arguments 409 | 410 | 411 | def test(): 412 | """Run any doctests in this script or associated test scripts""" 413 | options = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE 414 | all_failures, all_tests = 0, 0 415 | main_module = sys.modules['__main__'] 416 | failures, tests = doctest.testmod( 417 | main_module, 418 | optionflags=options, 419 | ) 420 | all_failures += failures 421 | all_tests += tests 422 | for extension in ['.test', '.tests']: 423 | main_test = nearby_file(main_module.__file__, extension) 424 | if not os.path.isfile(main_test): 425 | continue 426 | failures, tests = doctest.testfile( 427 | main_test, 428 | optionflags=options, 429 | module_relative=False, 430 | ) 431 | all_failures += failures 432 | all_tests += tests 433 | print 'Ran', all_tests, 'tests,', all_failures, 'failures' 434 | return 0 435 | 436 | 437 | def main(commands): 438 | """Run the program""" 439 | result = 0 440 | for arg in commands: 441 | result |= show_command(arg) 442 | return result 443 | 444 | 445 | if __name__ == '__main__': 446 | args = read_command_line() 447 | if not args: 448 | sys.exit(test()) 449 | else: 450 | sys.exit(main(args)) 451 | -------------------------------------------------------------------------------- /bin/wtfis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Lookup a command on cheat.sh 4 | # 5 | # Copyright 2021, Joe Block 6 | 7 | if [[ "$(uname)" == 'Darwin' ]]; then 8 | exec open "http://cheat.sh/$1" 9 | else 10 | exec curl -s "cheat.sh/$1" 11 | fi 12 | -------------------------------------------------------------------------------- /bin/yaml-to-json: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Convert yaml to json 4 | # 5 | # Copyright 2022, Joe Block 6 | # 7 | # License: Apache 2 8 | 9 | set -o pipefail 10 | if [[ -n "$DEBUG" ]]; then 11 | set -x 12 | fi 13 | 14 | function debug() { 15 | if [[ -n "$DEBUG" ]]; then 16 | echo "$@" 17 | fi 18 | } 19 | 20 | function fail() { 21 | printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way. 22 | exit "${2-1}" ## Return a code specified by $2 or 1 by default. 23 | } 24 | 25 | function has() { 26 | # Check if a command is in $PATH 27 | which "$@" > /dev/null 2>&1 28 | } 29 | 30 | if ! has python3; then 31 | fail "Cannot find python3 in your PATH!" 32 | fi 33 | 34 | exec python3 -c 'import sys, yaml, json; json.dump(yaml.load(sys.stdin, Loader=yaml.FullLoader), sys.stdout, indent=4)' 35 | -------------------------------------------------------------------------------- /bin/yaml2json: -------------------------------------------------------------------------------- 1 | yaml-to-json -------------------------------------------------------------------------------- /bin/yesterday: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Original source 4 | # https://github.com/ClarkGoble/Scripts/blob/master/yesterday.py 5 | ############################### 6 | # today.py 7 | ############################### 8 | # 9 | # Types today's date formated 10 | 11 | import sys, datetime, string 12 | from appscript import * 13 | 14 | 15 | def getdate(): 16 | d=datetime.date.today() 17 | d = d + datetime.timedelta(-1) 18 | return d.strftime("%m/%d/%Y") 19 | 20 | 21 | def typestring(s): 22 | SE = app(u'System Events') 23 | 24 | for i in range(0,len(s)): 25 | c = s[i] 26 | 27 | if c == '\n': 28 | continue 29 | 30 | if c in string.ascii_uppercase: 31 | SE.keystroke(s[i], using=k.shift_down) 32 | else: 33 | SE.keystroke(s[i]) 34 | 35 | 36 | def main(argv=None): 37 | print getdate() 38 | 39 | 40 | if __name__=='__main__': 41 | sys.exit(main()) 42 | -------------------------------------------------------------------------------- /bin/yq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Run yq via a docker container so it's one less thing to install directly 4 | # on machines 5 | # 6 | # Copyright 2020, Joe Block 7 | 8 | exec docker run --rm -i -v "${PWD}":/workdir unixorn/yq yq "$@" 9 | -------------------------------------------------------------------------------- /completions/_mosh: -------------------------------------------------------------------------------- 1 | #compdef mosh 2 | # ------------------------------------------------------------------------------ 3 | # Description 4 | # ----------- 5 | # 6 | # Completion script for mosh (http://mosh.mit.edu). 7 | # 8 | # Source: https://gist.github.com/2242920 9 | # 10 | # ------------------------------------------------------------------------------ 11 | # Authors 12 | # ------- 13 | # 14 | # * Ben O'Hara (https://github.com/benohara) 15 | # 16 | # ------------------------------------------------------------------------------ 17 | 18 | _arguments \ 19 | '--client=:client helper:_command_names -e' \ 20 | '--server=:server helper:_files' \ 21 | '--ssh=:ssh command to run:_files' \ 22 | '(-a -n)--predict=:when:(adaptive always never)' \ 23 | '(--predict -n)-a[predict always]' \ 24 | '(--predict -a)-n[predict never]' \ 25 | {-p,--port=}':port:_ports' \ 26 | ':remote:_hosts' \ 27 | ':remote command:_command_names -e' 28 | 29 | # Local Variables: 30 | # mode: Shell-Script 31 | # sh-indentation: 2 32 | # indent-tabs-mode: nil 33 | # sh-basic-offset: 2 34 | # End: 35 | # vim: ft=zsh sw=2 ts=2 et 36 | -------------------------------------------------------------------------------- /jpb.plugin.zsh: -------------------------------------------------------------------------------- 1 | # Copyright 2006-2024 Joseph Block 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # What platform are we on? 16 | on_linux() { [[ "$(uname -s)" = 'Linux' ]] } 17 | on_macos() { [[ "$(uname -s)" = 'Darwin' ]] } 18 | 19 | # Deprecated, OS name has changed to macOS 20 | on_osx() { [[ "$(uname -s)" = 'Darwin' ]] } 21 | 22 | # check if a command is available 23 | 24 | function exists() { 25 | if (( $+commands[$1] )); then 26 | return 0 27 | else 28 | return 1 29 | fi 30 | } 31 | 32 | alias dmesg='sudo dmesg' 33 | 34 | # check if this is an interactive session 35 | # (tests if stdout is a tty) 36 | function is-interactive() { [ -t 1 ] } 37 | function is-interactive-session() { [ -t 1 ] } 38 | 39 | # Add plugin_root/bin to user's path, but only if it exists and is a directory 40 | if [[ -d "${0:h}/bin" ]];then 41 | path+=("${0:h}/bin") 42 | fi 43 | 44 | if [[ "$(uname -s)" = 'Linux' ]]; then 45 | # We're on linux 46 | alias cputop='top -o cpu' 47 | alias l-d='ls -lFad' 48 | alias l='ls -la' 49 | alias l='ls -laF' 50 | alias ll='ls -lFa | TERM=vt100 less' 51 | 52 | # Simulate OSX's pbcopy and pbpaste on Linux 53 | alias pbcopy='xsel --clipboard --input' 54 | alias pbpaste='xsel --clipboard --output' 55 | fi 56 | 57 | # View HTTP traffic 58 | alias historysummary="history | awk '{a[\$2]++} END{for(i in a){printf \"%5d\t%s\n\",a[i],i}}'| sort -rn| head -30" 59 | alias httpdump="sudo tcpdump -i en1 -n -s 0 -w - | grep -a -o -E \"Host\: .*|GET \/.*\"" 60 | alias sniff="sudo ngrep -d 'en1' -t '^(GET|POST) ' 'tcp and port 80'" 61 | 62 | function historygram() { 63 | history | \ 64 | awk '{print $2}' | \ 65 | sort | \ 66 | uniq -c | \ 67 | sort -rn | \ 68 | head -20 | \ 69 | awk '!max{max=$1;}{r="";i=s=60*$1/max;while(i-->0)r=r"#";printf "%15s %5d %s %s",$2,$1,r,"\n";}' 70 | } 71 | 72 | function version_greater_equal() 73 | { 74 | printf '%s\n%s\n' "$2" "$1" | sort --check=quiet --version-sort 75 | } 76 | 77 | # IP address fiddling 78 | alias external_ip='curl -s icanhazip.com' 79 | alias my_ips="ifconfig -a | perl -nle'/(\d+\.\d+\.\d+\.\d+)/ && print $1'" 80 | alias myip="dig +short myip.opendns.com @resolver1.opendns.com" 81 | 82 | alias reattach='screen -r' 83 | 84 | # SSH stuff 85 | alias scp-no-hostchecks='scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null' 86 | alias scpi='scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null' 87 | alias ssh-force-password-no-hostcheck='ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null' 88 | alias ssh-force-password='ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no' 89 | alias ssh-no-hostchecks='ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null' 90 | alias ssh='ssh -A' 91 | alias sshi='ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null' 92 | # Until my fingers forget the old alias, keep it around 93 | alias sshnohostchecks='ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null' 94 | 95 | # Colors 96 | NO_COLOR='\033[0m' 97 | BLUE='\033[0;34m' 98 | CYAN='\033[0;36m' 99 | GREEN='\033[0;32m' 100 | ORANGE='\033[0;33m' 101 | PURPLE='\033[0;35m' 102 | RED='\033[0;31m' 103 | DARK_GRAY='\033[1;30m' 104 | LIGHT_BLUE='\033[1;34m' 105 | LIGHT_CYAN='\033[1;36m' 106 | LIGHT_GRAY='\033[0;37m' 107 | LIGHT_GREEN='\033[1;32m' 108 | LIGHT_PURPLE='\033[1;35m' 109 | LIGHT_RED='\033[1;31m' 110 | WHITE='\033[1;37m' 111 | YELLOW='\033[1;33m' 112 | 113 | # Strip ANSI codes out of a stream 114 | alias stripcolors='sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g"' 115 | 116 | alias wget='wget -c' 117 | alias wget-images='wget -nd -r -l 2 -A jpg,jpeg,png,gif,bmp' 118 | 119 | # git aliases 120 | # Deal with my tyops 121 | alias annotate='git annotate' 122 | alias blame='git blame' 123 | alias gadd='git add' 124 | alias gblame='git blame' 125 | alias gci='git ci -v' 126 | alias gdiff='git diff' 127 | alias git-ignored='git ls-files --others --i --exclude-standard' 128 | alias gitadd='git add' 129 | alias gitci='git ci -v' 130 | alias gitdiff='git diff' 131 | alias gitignored='git ls-files --others --i --exclude-standard' 132 | alias gitlgg="git log --pretty=format:'%Cred%h%Creset -%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'" 133 | alias gitlog='git log' 134 | alias gitloll='git log --graph --decorate --pretty=oneline --abbrev-commit' 135 | alias gitmerge='git merge' 136 | alias gitpull='git pull' 137 | alias gitpus='git push' 138 | alias gitpush='git push' 139 | alias gitrebase='git rebase' 140 | alias gitst='git status' 141 | alias glg='git log' 142 | alias glog='git log' 143 | alias gpull='git pull' 144 | alias gpush='git push' 145 | alias grebase='git rebase -i' 146 | alias hlog='git log --all --date-order --graph --date=short --format="%C(green)%H%Creset %C(yellow)%an%Creset %C(blue bold)%ad%Creset %C(red bold)%d%Creset%s"' 147 | 148 | # GPG stuff 149 | alias sign='gpg --detach-sign --armor' 150 | 151 | # more of my common tyops 152 | alias ':q'="exit" 153 | alias ..='cd ..' 154 | alias gerp='grep' 155 | alias grep-i='grep -i' 156 | if version_greater_equal $(grep -V | head -1 | grep GNU | cut -d" " -f4 ) 3.8; then 157 | alias grep='GREP_COLORS="mt=1;37;41" LANG=C grep --color=auto' 158 | else 159 | alias grep='GREP_COLOR="1;37;41" LANG=C grep --color=auto' 160 | fi 161 | alias grepi='grep -i' 162 | alias maek='make' 163 | alias mkdir-p='mkdir -p' 164 | alias mkdirp='mkdir -p' 165 | alias psax='ps ax' 166 | alias pswax='ps wax' 167 | alias psxa='ps ax' 168 | alias raek='rake' 169 | alias tar-tvf='tar tvf' 170 | alias tar-tvzf='tar tvzf' 171 | alias tar-xvf='tar xvf' 172 | alias tar-xvzf='tar xvzf' 173 | alias tartvf='tar tvf' 174 | alias tartvzf='tar tvzf' 175 | alias tarxvf='tar xvf' 176 | alias tarxvzf='tar xvzf' 177 | alias zz='exit' 178 | 179 | # from cads 180 | ff() { 181 | find . -type f -iname '*'$*'*' -ls 182 | } 183 | 184 | # Got tired of constantly doing history | grep X | tail 185 | hgrep40() { 186 | history | grep -i "$@" | tail -40 187 | } 188 | 189 | hgrep() { 190 | history | grep -i "$@" | tail -20 191 | } 192 | 193 | # Syntax-highlight JSON strings or files 194 | # Usage: `json '{"foo":42}'` or `echo '{"foo":42}' | json` 195 | # From https://github.com/yramagicman/zsh-aliases/blob/master/functions/functions.plugin.zsh 196 | function json() { 197 | if [ -t 0 ]; then # argument 198 | python -mjson.tool <<<"$*" | pygmentize -l javascript 199 | else # pipe 200 | python -mjson.tool | pygmentize -l javascript 201 | fi 202 | } 203 | 204 | rot13() { 205 | echo $1 | tr "A-Za-z" "N-ZA-Mn-za-m" 206 | } 207 | 208 | # SHA stuff 209 | sha1() { 210 | echo -n $1 | openssl sha1 /dev/stdin 211 | } 212 | 213 | sha256() { 214 | echo -n $1 | openssl sha256 /dev/stdin 215 | } 216 | 217 | sha512() { 218 | echo -n $1 | openssl sha512 /dev/stdin 219 | } 220 | 221 | # from commandlinefu.com 222 | watch() { t=$1; shift; while test :; do clear; date=$(date); echo -e "Every $ts: $@ \t\t\t\t $date"; $@; sleep $t; done } 223 | 224 | calc() { 225 | awk "BEGIN{ print $* }" ; 226 | } 227 | 228 | alias procs_for_path='procs-for-path' 229 | 230 | # begin sysadvent2011 functions 231 | _awk_col() { 232 | echo "$1" | egrep -v '^[0-9]+$' || echo "\$$1" 233 | } 234 | 235 | sum() { 236 | [ "${1#-F}" != "$1" ] && SP=${1} && shift 237 | [ "$#" -eq 0 ] && set -- 0 238 | key="$(_awk_col "$1")" 239 | awk $SP "{ x+=$key } END { printf(\"%d\n\", x) }" 240 | } 241 | 242 | sumby() { 243 | [ "${1#-F}" != "$1" ] && SP=${1} && shift 244 | [ "$#" -lt 0 ] && set -- 0 1 245 | key="$(_awk_col "$1")" 246 | val="$(_awk_col "$2")" 247 | awk $SP "{ a[$key] += $val } END { for (i in a) { printf(\"%d %s\\n\", a[i], i) } }" 248 | } 249 | 250 | countby() { 251 | [ "${1#-F}" != "$1" ] && SP=${1} && shift 252 | [ "$#" -eq 0 ] && set -- 0 253 | key="$(_awk_col "$1")" 254 | awk $SP "{ a[$key]++ } END { for (i in a) { printf(\"%d %s\\n\", a[i], i) } }" 255 | } 256 | # end sysadvent 257 | 258 | # Make easier to remember alias for removing entries from known_hosts 259 | rmhost() { 260 | ssh-keygen -R "$@" 261 | } 262 | 263 | get_load() { 264 | uptime | awk '{print $11}' | tr ',' ' ' 265 | } 266 | 267 | bash_repeat() { 268 | n=$1 269 | shift 270 | while [ $(( n -= 1 )) -ge 0 ] 271 | do 272 | "$@" 273 | done 274 | } 275 | 276 | # Deal with various stupidities 277 | 278 | authme() { 279 | ssh "$1" 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys' \ 280 | < ~/.ssh/id_dsa.pub 281 | } 282 | 283 | jsoncurl() { 284 | curl "$@" | python -m json.tool 285 | } 286 | 287 | # recursively touch, e.g. touch + mkdir -p 288 | # so files can easily be created at depth 289 | canhaz() { 290 | mkdir -p $(dirname "${1}") && \ 291 | touch "${1}" 292 | } 293 | 294 | canhaz_script() { 295 | mkdir -p $(dirname "${1}") && \ 296 | touch "${1}" && \ 297 | chmod +x "${1}" 298 | } 299 | 300 | pong() { 301 | ping -c 10 "$@" 302 | } 303 | 304 | show_terminal_colors() { 305 | for i in {0..255} ; do 306 | printf "\x1b[38;5;${i}mcolor${i}\n" 307 | done 308 | } 309 | 310 | # batch change extension 311 | # change-extension erb haml 312 | function change-extension() { 313 | foreach f (**/*.$1) 314 | mv $f $f:r.$2 315 | end 316 | } 317 | alias chgext='change-extension' 318 | 319 | # From Dan Ryan's blog - http://danryan.co/using-antigen-for-zsh.html 320 | man() { 321 | env \ 322 | LESS_TERMCAP_mb=$(printf "\e[1;31m") \ 323 | LESS_TERMCAP_md=$(printf "\e[1;31m") \ 324 | LESS_TERMCAP_me=$(printf "\e[0m") \ 325 | LESS_TERMCAP_se=$(printf "\e[0m") \ 326 | LESS_TERMCAP_so=$(printf "\e[1;44;33m") \ 327 | LESS_TERMCAP_ue=$(printf "\e[0m") \ 328 | LESS_TERMCAP_us=$(printf "\e[1;32m") \ 329 | man "$@" 330 | } 331 | 332 | # History tools 333 | alias zh='fc -l -d -D' 334 | delete-from-zsh-history () { 335 | # Prevent the specified history line from being saved. 336 | local HISTORY_IGNORE="${(b)$(fc -ln $1 $1)}" 337 | 338 | # Write out the history to file, excluding lines that match `$HISTORY_IGNORE`. 339 | fc -W 340 | 341 | # Dispose of the current history and read the new history from file. 342 | fc -p $HISTFILE $HISTSIZE $SAVEHIST 343 | 344 | # TA-DA! 345 | print "Deleted '$HISTORY_IGNORE' from history." 346 | } 347 | 348 | if exists open;then 349 | alias -s pdf=open 350 | fi 351 | alias edit="$EDITOR"' $(eval ${$(fc -l -1)[2,-1]} -l)' 352 | alias knife='nocorrect knife' 353 | 354 | function hexpass() { 355 | openssl rand -hex 24 $@ 356 | } 357 | 358 | function sshaddme { 359 | ssh $1 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" < ~/.ssh/id_?sa.pub # '?sa' is a glob, not a typo! 360 | } 361 | alias ssh-addme='sshaddme' 362 | 363 | # mkdir & cd 364 | function mkcd { 365 | mkdir -p "$@" && cd $_ 366 | } 367 | 368 | # delete .pyc and .pyo files 369 | function pyclean { 370 | find -E . -type f -regex '.*\.(pyc|pyo)' -delete 371 | } 372 | 373 | function smite() { 374 | ps wax | grep ${1} | grep -v grep | awk '{print $1}' | xargs murder 375 | } 376 | 377 | alias python_module_path="echo 'import sys; t=__import__(sys.argv[1],fromlist=[\".\"]); print(t.__file__)' | python - " 378 | 379 | function htmime { 380 | if [[ -z $1 ]]; then 381 | print 'USAGE: htmime ' 382 | return 1 383 | fi 384 | mime=$(curl -sIX HEAD $1 | sed -nr "s/Content-Type: (.+)/\1/p") 385 | print $mime 386 | } 387 | 388 | # Lesspipe 389 | LESSPIPE=$(command -v lesspipe.sh) 390 | if [ -n "${LESSPIPE}" ]; then 391 | export LESSOPEN="| ${LESSPIPE} %s" 392 | export LESS=' -R ' 393 | fi 394 | 395 | # Yes, these are a pain to customize. Fortunately, Geoff Greer made an online 396 | # tool that makes it easy to customize your color scheme and keep them in sync 397 | # across Linux and macOS/*BSD at http://geoff.greer.fm/lscolors/ 398 | 399 | export LSCOLORS=exfxcxdxbxegedAbAgacad 400 | export LS_COLORS="di=34;40:ln=35;40:so=32;40:pi=33;40:ex=31;40:bd=34;46:cd=34;43:su=1;;41:sg=1;;46:tw=0;42:ow=0;43:" 401 | 402 | alias asroot='sudo $(fc -ln -1)' 403 | 404 | # show newest files 405 | # http://www.commandlinefu.com/commands/view/9015/find-the-most-recently-changed-files-recursively 406 | newest (){ 407 | find . -type f -printf '%TY-%Tm-%Td %TT %p\n' | \ 408 | grep -v cache | \ 409 | grep -v '.hg' | grep -v '.git' | \ 410 | sort -r | \ 411 | less 412 | } 413 | 414 | # print a separator banner, as wide as the terminal 415 | function hr { 416 | print ${(l:COLUMNS::=:)} 417 | } 418 | 419 | # urlencode text 420 | function urlencode { 421 | print "${${(j: :)@}//(#b)(?)/%$[[##16]##${match[1]}]}" 422 | } 423 | 424 | # Create short urls via http://goo.gl using curl(1). 425 | # Contributed back to grml zshrc 426 | # API reference: https://code.google.com/apis/urlshortener/ 427 | function zurl { 428 | if [[ -z $1 ]]; then 429 | print "USAGE: $0 " 430 | return 1 431 | fi 432 | 433 | local url=$1 434 | local api='https://www.googleapis.com/urlshortener/v1/url' 435 | local data 436 | 437 | # Prepend "http://" to given URL where necessary for later output. 438 | if [[ $url != http(s|)://* ]]; then 439 | url="http://$url" 440 | fi 441 | local json="{\"longUrl\": \"$url\"}" 442 | 443 | data=$(curl --silent -H "Content-Type: application/json" -d $json $api) 444 | # Match against a regex and print it 445 | if [[ $data =~ '"id": "(http://goo.gl/[[:alnum:]]+)"' ]]; then 446 | print $match 447 | fi 448 | } 449 | 450 | function stopwatch(){ 451 | case "$(uname)" in 452 | "Linux") DATE=date ;; 453 | "Darwin") DATE=gdate ;; 454 | esac 455 | local BEGIN=`$DATE +%s` 456 | while true; do 457 | echo -ne "$($DATE -u --date @$((`$DATE +%s` - $BEGIN)) +%H:%M:%S)\r"; 458 | done 459 | } 460 | 461 | # lists zombie processes 462 | function zombie() { 463 | ps aux | awk '{if ($8=="Z") { print $2 }}' 464 | } 465 | alias zombies=zombie 466 | 467 | # strip comments from config files 468 | function justconfig() { 469 | sed -e '/^[[:space:]]*#/d;/^[[:space:]]*;/d;s/\r//g;/^[[:space:]]*$/d' "$1" 470 | } 471 | 472 | # from: https://github.com/chilicuil/shundle-plugins/blob/master/aliazator/aliases/extra/wget.aliases 473 | alias test-broadband='wget -O /dev/null http://speedtest.wdc01.softlayer.com/downloads/test500.zip' 474 | alias wcat='wget -q -O -' 475 | 476 | alias grepm='grep --exclude-dir={node_modules,bower_components,dist,.bzr,.cvs,.git,.hg,.svn,.tmp} --color=always' 477 | 478 | if [[ -z "$BULLETRAIN_CONTEXT_SHOW" ]]; then 479 | export BULLETTRAIN_CONTEXT_SHOW=true 480 | fi 481 | if [[ -z "$BULLETTRAIN_IS_SSH_CLIENT" ]]; then 482 | export BULLETTRAIN_IS_SSH_CLIENT=true 483 | fi 484 | 485 | # Calculate how many days since epoch 486 | epochdays() { 487 | if command -v perl &>/dev/null; then 488 | epoch=$(perl -e "print time") 489 | elif command -v truss >/dev/null 2>&1 && [[ $(uname) = SunOS ]]; then 490 | epoch=$(truss date 2>&1 | grep ^time | awk -F"= " '{print $2}') 491 | elif command -v truss >/dev/null 2>&1 && [[ $(uname) = FreeBSD ]]; then 492 | epoch=$(truss date 2>&1 | grep ^gettimeofday | cut -d "{" -f2 | cut -d "." -f1) 493 | elif [[ $(uname) = Linux ]]; then 494 | epoch=$(date +%s) 495 | fi 496 | printf "%s\n" "$(( epoch / 86400 ))" 497 | } 498 | 499 | # Perly stuff from https://github.com/massa/massazsh/blob/master/massa.sh 500 | 501 | # pgs - Perl Global Substitution 502 | # find pattern = 1st arg 503 | # replace pattern = 2nd arg 504 | # filename = 3rd arg 505 | pgs() { # [find] [replace] [filename] 506 | perl -i.orig -pe 's/'"$1"'/'"$2"'/g' "$3" 507 | } 508 | 509 | # Perl grep, because 'grep -P' is terrible. Lets you work with pipes or files. 510 | prep() { # [pattern] [filename unless STDOUT] 511 | perl -nle 'print if /'"$1"'/;' $2 512 | } 513 | alias perl-grep='pgrep' 514 | alias perlgrep='pgrep' 515 | alias pl-grep='pgrep' 516 | 517 | function stockquote() { 518 | curl -s "http://download.finance.yahoo.com/d/quotes.csv?s=$1&f=l1" 519 | } 520 | 521 | # One of @janmoesen’s ProTip™s 522 | for method in GET HEAD POST PUT DELETE TRACE OPTIONS; do 523 | alias "$method"="lwp-request -m '$method'" 524 | done 525 | 526 | getcert() { 527 | host=${1}; 528 | port=${2:-443}; 529 | openssl s_client -connect ${host}:${port} 2> /dev/null /dev/null) || modified_time=0 541 | echo "${modified_time}" 542 | } 543 | 544 | elif [[ $(uname | grep -ci Linux) = 1 ]]; then 545 | 546 | # Linux version 547 | get_file_modification_time() { 548 | modified_time=$(stat -c %Y "$1" 2> /dev/null) || modified_time=0 549 | echo "${modified_time}" 550 | } 551 | fi 552 | 553 | # Lazy enough to not want to hit the shift key 554 | alias get-file-modification-time=get_file_modification_time 555 | 556 | # VSCode stuff 557 | alias cadd='code -a' 558 | alias cdiff='code -d' 559 | alias cnew='code -n' 560 | alias code-install='code --install-extension' 561 | alias code-uninstall='code --uninstall-extension' 562 | alias vs-code='code' 563 | alias vscode='code' 564 | 565 | # From https://github.com/jessfraz/dotfiles/blob/master/.functions 566 | # go to a folder easily in your gopath 567 | gogo(){ 568 | local d=$1 569 | 570 | if [[ -z $d ]]; then 571 | echo "You need to specify a project name." 572 | return 1 573 | fi 574 | 575 | if [[ "$d" = github* ]]; then 576 | d=$(echo $d | sed 's/.*\///') 577 | fi 578 | d=${d%/} 579 | 580 | # search for the project dir in the GOPATH 581 | local path=( `find "${GOPATH}/src" \( -type d -o -type l \) -iname "$d" | awk '{print length, $0;}' | sort -n | awk '{print $2}'` ) 582 | 583 | if [ "$path" == "" ] || [ "${path[*]}" == "" ]; then 584 | echo "Could not find a directory named $d in $GOPATH" 585 | echo "Maybe you need to 'go get' it ;)" 586 | return 1 587 | fi 588 | 589 | # enter the first path found 590 | cd "${path[0]}" 591 | } 592 | 593 | # build go static binary from root of project 594 | gostatic(){ 595 | local dir=$1 596 | local arg=$2 597 | 598 | if [[ -z $dir ]]; then 599 | dir=$(pwd) 600 | fi 601 | 602 | local name 603 | name=$(basename "$dir") 604 | ( 605 | cd "$dir" || exit 606 | export GOOS=${GOOS:-linux} 607 | 608 | echo "Building ${GOOS} static binary for $name in $dir" 609 | 610 | case $arg in 611 | "netgo") 612 | set -x 613 | go build -a \ 614 | -tags 'netgo static_build' \ 615 | -installsuffix netgo \ 616 | -ldflags "-w" \ 617 | -o "$name" . 618 | ;; 619 | "cgo") 620 | set -x 621 | CGO_ENABLED=1 go build -a \ 622 | -tags 'cgo static_build' \ 623 | -ldflags "-w -extldflags -static" \ 624 | -o "$name" . 625 | ;; 626 | *) 627 | set -x 628 | CGO_ENABLED=0 go build -a \ 629 | -installsuffix cgo \ 630 | -ldflags "-w" \ 631 | -o "$name" . 632 | ;; 633 | esac 634 | ) 635 | } 636 | 637 | golistdeps(){ 638 | ( 639 | if [[ -n "$1" ]]; then 640 | gogo "$@" 641 | fi 642 | 643 | go list -e -f '{{join .Deps "\n"}}' ./... | xargs go list -e -f '{{if not .Standard}}{{.ImportPath}}{{end}}' 644 | ) 645 | } 646 | 647 | alias snake_case='tr [A-Z\ ] [a-z_]' 648 | 649 | # Linux-specific stuff 650 | if on_linux; then 651 | # Add helper scripts with macOS tool names so I don't have to remember 652 | # the names of the linux-specific ones. 653 | if exists xdg-open; then 654 | if ! exists open; then 655 | alias open='xdg-open' 656 | fi 657 | fi 658 | 659 | alias time-in-ms=nanotime 660 | 661 | if exists xclip; then 662 | if ! exists pbcopy; then 663 | function pbcopy { 664 | if type xclip > /dev/null; then 665 | xclip -selection clipboard 666 | fi 667 | if type xsel > /dev/null; then 668 | xsel --clipboard --input 669 | fi 670 | } 671 | fi 672 | 673 | if ! exists pbpaste; then 674 | function pbpaste { 675 | if type xclip > /dev/null; then 676 | xclip -selection clipboard -o 677 | fi 678 | if type xsel > /dev/null; then 679 | xsel --clipboard --output 680 | fi 681 | } 682 | fi 683 | fi 684 | fi 685 | 686 | ssh-copy-key() { 687 | local destination=$1 688 | [ -z ${destination} ] && return 1 689 | cat ${HOME}/.ssh/id_rsa.pub | ssh "$destination" "mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys" 690 | } 691 | 692 | boring-prompt() { 693 | PROMPT_COMMAND='' PS0='' PS1='$ ' zsh 694 | } 695 | 696 | # Cloned from https://github.com/ag4ve/dotfiles/blob/master/dot_zsh/rc/650-functions.sh 697 | 698 | # Pertinent information from whois 699 | whoism () { 700 | whois "$1" | egrep '^(inetnum|netname|descr|address|CIDR|OrgName|OrgId|Address|City|StateProv|PostalCode|[Cc]ountry|inetrev|owner(id)?|responsible|nserver):' 701 | } 702 | compdef whoism=whois 703 | 704 | # short dig 705 | sdig () { 706 | dig "$@" +short 707 | } 708 | compdef sdig=dig 709 | 710 | # long dig 711 | 712 | ldig () { 713 | dig +trace +nocmd "$@" any +multiline +answer 714 | } 715 | compdef ldig=dig 716 | 717 | # Convert number to normal ip 718 | int2ip () { 719 | perl -e "print join '.', unpack 'C4', pack 'N', $@" 720 | } 721 | 722 | # Regex grep all ASCII files in a directory 723 | tgrep () { 724 | string="$1" 725 | local dir="$2" 726 | 727 | if [ -z "$dir" ] ; then 728 | dir="./" 729 | fi 730 | 731 | while read file ; do 732 | if [[ "$(file -b --mime-encoding "$file")" = *ascii* ]] ; then 733 | grep -HE "$string" "$file" 734 | fi 735 | done <<< "$(command find $dir -type f)" 736 | } 737 | compdef tgrep=egrep 738 | 739 | s_client () { 740 | command openssl s_client -connect "$1:443" -servername "$1" -showcerts -verify 10 <<<"QUIT" 2>&1 741 | } 742 | 743 | asn1parse () { 744 | command openssl asn1parse -in "$1" -dump -i 745 | } 746 | 747 | defstrace () { 748 | command strace -f -e open,access,connect,recvfrom,sendto,network $@ 749 | } 750 | compdef defstrace=strace 751 | 752 | if exists batcat; then 753 | alias bat="batcat --pager less" 754 | fi 755 | alias dicepass="perl -MCrypt::XkcdPassword -e 'print Crypt::XkcdPassword->make_password(\$_)'" 756 | --------------------------------------------------------------------------------