Short BASH Snippets

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.


# exit the script on command errors or unset variables
set -euo pipefail

# 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"

This snippet prints the command before running it. Stolen from StackOverflow. Great for debugging!

set -x
{ 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_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\"; }"

Generate pretty print functions and use them

make_print_color "red" "$(tput setaf 1)"
make_print_color "green" "$(tput setaf 2)"
make_print_color "yellow" "$(tput setaf 3)"

print_red "Always"
print_green "Seeing"
print_yellow "in Color!"

# print to stderr:
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"
        echo "Only files and directories supported"

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'

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
    elif [[ "$(uname)" == "Linux" ]]; then
    # Open a subshell in a fork
    (sleep 2 && "${open_command}" "") &
    # 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.

    cat << EOF
    $0 first|1
    $0 second|2

first() {
    echo "I'm first"

second() {
    echo "I'm second!"

set +u
if [ -z ${1+x} ]; then
set -u

case "$1" in
        echo "Unmatched command: $1"

Tee stderr and stdoutto files

Save both stderr and stdout to a file. Only works in Bash. From StackOverflow

{ { time ./ | 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"

Iterating inline arrays in Zsh

Not a Bash snippet, but useful nonetheless :) . From SuperUser

for d ( {
    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/