Bash tricks

From wikinotes

Parallelization with Xargs

# 8x parallel processes at once
echo "${git_repos[@]}" \
    | tr ' ' '\n' \
    | xargs -P 8 -I REPO   git pull REPO

Skip first N lines of output

cat << EOF | tail -n +2  # prints all lines, starting with #2 (fiona)
NAME
fiona
alex
katie
EOF

Strip Leading/Trailing Whitespace

echo '  abc def hij  ' | awk '{$1=$1;print}'

raw output

Sometimes when parsing shell output, it is useful to see the actual raw output before colours/escape sequences are applied in order to diagnose issues. In this instance, you can pipe a command to cat.

( ls -l  ;  ls -l --color=always  ) | cat -vet

total 41624$
drwxr-xr-x 2 will will     4096 Dec  2 08:01 new$
-rw-r--r-- 1 will will 42557440 Dec  2 08:01 old.tar$
total 41624$
drwxr-xr-x 2 will will     4096 Dec  2 08:01 ^[[01;34mnew^[[0m$
-rw-r--r-- 1 will will 42557440 Dec  2 08:01 old.tar$

Find the Number of tokens in a line:

VAR='some/string/with words'
echo $VAR | tr -cd  "/" | wc -c

Repeat character N times

printf -- '=%.0s' {1..5}       # repeat '=' 5 times
printf -- '=%.0s' $(seq 1 5)   # repeat '=' 5 times

Some useful applications

# print '-'s across the full width of terminal
printf "%$(tput cols)s" | tr ' ' '-'

# print '='s matching length of '$header'
header="hello my friend"
echo "$header"
printf "=%.0s" $(seq 1 ${#header}); echo

Iterate over tokenize string

Useful for iterating over $PATH style variable paths that may contain spaces.

echo "$PATH" | tr ':' '\n' | while read -r path; do
    echo "--$path"
done

UUIDs

  • linux: e2fsprogs, libuuid
  • freebsd: base
uuidgen  # generate a uuid

Simple Menu

Consider using ncurses or dialog instead before implementing your own menu.
The following should also work.

# Format:  ${MENU_ITEM} ${COMMAND} ...
CHOICES=(
    "say hi"
        "echo hi && sleep 1"

    "say bye"
        "echo bye && sleep 1"
)


print_error() {
    # """ prints an error with a sleep
    # """
    message="$1"
    echo
    echo
    echo $message
    sleep 2
}


show_menu() {
    # """ prints menu w/ choices
    # """
    clear
    echo "Choose Desktop:"
    for ((i=1; i<=${#CHOICES[@]}; i+=2)); do
        local index="$(expr $i / 2)"
        local name="${CHOICES[$i]}"
        echo "    ${index}) ${name}"
    done
}


choose_menu_index() {
    # """ requests an integer with the menu choice you'd like to load
    # """
    local num_choices="$(expr $(expr ${#CHOICES[@]} / 2) - 1)"
    echo
    echo -n "Press (0-${num_choices} [default:0]): " && read index

    # defaults to 0
    test -z "$index" && index=0

    # must be integer
    if ! [ "$index" -eq "$index" ] 2> /dev/null ; then
        print_error "Expected Integer. Received: ${index}"
        return 1
    fi

    # reject numbers above $num_choices
    if test "$index" -gt "$num_choices" ; then
        print_error "Expected Integer <${num_choices}. Received: ${index}"
        return 1
    fi

    # exec chosen menu, after newline
    local array_index=$((2*$(expr $index + 1)))

    eval "${CHOICES[$array_index]}"
}


mainloop() {
    if [[ $(tty) == /dev/tty1 ]] ; then
        while true; do
            show_menu
            choose_menu_index
        done
    fi
}

Executable Archive

...or rather, an executable with an embedded archive that can be unzipped.

https://stackoverflow.com/questions/49351437/combine-a-shell-script-and-a-zip-file-into-a-single-executable-for-deployment