Short BASH Snippets
- BASH script starter
- Get the full path to a file
- Print a BASH command
- Generate and use colored print commands
- Simple but effective backup command.
- Run a shell command on file change
- Run a Server and open a browser with the link
- Simple Task Runner
- Tee stderr and stdoutto files
- Process each line on a file
- Iterating inline arrays in Zsh
- Diff everything in a directory!
- Cross-platform colored diff
- Customize dig
- Search and replace across files
- Clean unwanted Homebrew formulas
- List images in the terminal
- Curl tips
- Copy to Clipboard From Tmux
- Search file types with ripgrep
BASH script starter
I put this at the top of all my scripts because most of the time I want scripts to fail on errors, and half the time I want the script to run in the directory it's in.
#!/bin/bash
# exit the script on command errors or unset variables
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
# https://stackoverflow.com/a/246128/295807
# readonly script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# cd "${script_dir}"
Get the full path to a file
This is perl wrapped in Bash, but it's cross-platform and works on Mac and Linux. The alternative, readlink -f
doesn't work on Mac.
fullpath() {
local -r full=$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$1")
echo "$full"
}
Print a BASH command
This snippet prints the command before running it. Stolen from StackOverflow. Great for debugging!
set -x
command
{ set +x; } 2>/dev/null
Generate and use colored print commands
Running scripts with colored output can make them much friendlier. Consider taking out the newlines if you want nested color prints. I almost never do, so I'm leaving them in...
Define the function factory
make_print_color() {
color_name="$1"
color_code="$2"
color_reset="$(tput sgr0)"
if [ -t 1 ] ; then
eval "print_${color_name}() { printf \"${color_code}%s${color_reset}\\n\" \"\$1\"; }"
else # Don't print colors on pipes
eval "print_${color_name}() { printf \"%s\\n\" \"\$1\"; }"
fi
}
Generate pretty print functions and use them
# https://unix.stackexchange.com/a/269085/185953
make_print_color "red" "$(tput setaf 1)"
make_print_color "green" "$(tput setaf 2)"
make_print_color "yellow" "$(tput setaf 3)"
make_print_color "blue" "$(tput setaf 4)"
print_red "Always"
print_green "Seeing"
print_yellow "in"
print_blue "Color!"
# print to stderr: https://stackoverflow.com/a/2990533/2958070
print_red "Error!" >&2
Simple but effective backup command.
bak() {
date_string="$(date +'%Y-%m-%d.%H.%M.%S')"
if [[ -d "$1" ]]; then
local -r no_slash="${1%/}"
cp -r "${no_slash}" "${no_slash}.${date_string}.bak"
elif [[ -f "$1" ]]; then
cp "$1" "${1}.${date_string}.bak"
else
echo "Only files and directories supported"
fi
}
Run a shell command on file change
I like to use entr
for this. Generate some filenames and pipe them to entr
. The -c
option clears the screen and the -s
option means use the shell.
ls log.txt | entr -c -s 'date && tail log.txt'
Run a Server and open a browser with the link
I use a snippet similar to this when I want to open a browser after I run a blocking command (usually starting a server). I use this particular example to learn Elm. I have something similar to run Jekyll for my blog.
learn_elm() {
cd ~/Code/Elm || echo "Non-existant dir"
code .
if [[ "$(uname)" == "Darwin" ]]; then
open_command=open
elif [[ "$(uname)" == "Linux" ]]; then
open_command=xdg-open
fi
# Open a subshell in a fork
(sleep 2 && "${open_command}" "http://127.0.0.1:8000") &
# Run the blocking command
elm reactor
}
Simple Task Runner
For when you want to run some long commands with a shortcut. It does very limited arg parsing.
print_help(){
cat << EOF
Workflow:
$0 first|1
$0 second|2
EOF
}
first() {
echo "I'm first"
}
second() {
echo "I'm second!"
}
set +u
if [ -z ${1+x} ]; then
print_help
fi
set -u
case "$1" in
first|1)
first
;;
second|2)
second
;;
*)
echo "Unmatched command: $1"
print_help
;;
esac
Tee stderr
and stdout
to files
Save both stderr
and stdout
to a file. Only works in Bash. From StackOverflow
# https://stackoverflow.com/a/59435204
{ { time ./tmp_import.sh | tee tmp_import_log.stdout;} 3>&1 1>&2 2>&3- | tee tmp_import_log.stderr;} 3>&1 1>&2 2>&3-
Process each line on a file
From Unix StackExchange. I like to combine it with printing the command used.
while IFS='' read -r line || [ -n "${line}" ]; do
set -x
echo "$line"
{ set +x; } 2>/dev/null
done < ./file.txt
You can also pipe lines to the while loop:
pbpaste | while IFS='' read -r line || [ -n "${line}" ]; do
echo "line: $line"
done
Iterating inline arrays in Zsh
Not a Bash snippet, but useful nonetheless :) . From SuperUser
for d (www.linkedin.com www.reddit.com www.google.com) {
dig +short +noshort "$d"
}
Diff everything in a directory!
Consider doing a git clean before this:
git clean -xd --dry-run
git clean -xd --force
diff -qr -x '.git' folder1/ folder2/
Meld is also a FANTASTIC GUI app for graphically diffing directory contents.
Cross-platform colored diff
A colleague got this from somewhere on StackOverflow:
function vdiff() {
# colored diff
diff $@ | sed 's/^-\([^-]*\)/\x1b[31;1m-\1/;s/^+\([^+]*\)/\x1b[32;1m+\1/;s/^@/\x1b[36;1m@/;s/$/\x1b[0m/'
}
Customize dig
Unfortunately, there's no way to use multiple name servers
dig +noall +answer +question +identify @dns2.p09.nsone.net. -q linkedin.com -t ns -q linkedin.com -t a
Search and replace across files
Most people use sed
for this, but sed
differs between MacOS and Linux. Taken from StackOverflow:
perl -pi -w -e 's/search/replace/g;' *.php
- -e means execute the following line of code.
- -i means edit in-place
- -w write warnings
- -p loop through lins and print
See Perl 101 - Command-line Switches for other useful Perl switches.
This can be combined with find
to run recursively:
find . -name '*.py' -print0 | xargs -0 perl -pi -w -e 's/"2022-04-01"/"2022-04-01-preview"/g;'
It's also possible to ignore files (NOTE: this works on MacOS)
find . -type f -not -path '*/\.git\/*' -not -path '\./rename.sh' -print0 \
| xargs -0 perl -pi -w -e 's/example-python-cli/new-project-name/g;'
Here's another find
example to format YAML files:
find . \( -name '*.yaml' -o -name '*.yml' \) -exec yq -i -P 'sort_keys(..)' {} \;
Clean unwanted Homebrew formulas
Show dependency graph (optional)
brew deps --installed --graph
Show formulas that nothing depends on (and how many dependencies they have) - https://stackoverflow.com/a/55445034/2958070
brew leaves | xargs brew deps --formula --for-each | sed "s/^.*:/$(tput setaf 4)&$(tput sgr0)/"
Then uninstall something:
brew uninstall [thing]
brew will refuse to uninstall the formula if another formula depends on it.
Run https://docs.brew.sh/Manpage#autoremove---dry-run to uninstall dependencies that are no longer needed.
brew autoremove
List images in the terminal
Expecially useful for getting the right images in blog Markdown image links. imgcat
comes from these iTerm2 docs.
imgcat -H 400px -r -p index.assets/*
Curl tips
Mastering curl: interactive text guide is an EXCELLENT blog post, but my current favorite tip is how to send an HTTP request to another IP (the below example also only shows the headers).
curl \
--head \
--request GET \
--connect-to www.linkedin.com:443:www.linkedin.cn:443 \
https://www.linkedin.com/
If you're only connecting to one domain and you're not changing ports, you can also leave things blank...
curl \
--head \
--request GET \
--connect-to ::www.linkedin.com: \
https://www.linkedin.com/
Copy to Clipboard From Tmux
- Enter command mode:
Ctrl
+b
,:
- Type:
set -g mouse off
- This will break scrolling
- Select the text to copy it
- Paste it in notes or wherever
- Enter command mode:
Ctrl
+b
,:
- Type:
set -g mouse on
- Scrolling works again
Search file types with ripgrep
From https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#manual-filtering-file-types :
Search for C file extensions with regex:
rg 'int main' -g '*.{c,h}'
Search for C file extension with --type
:
rg 'int main' --type c