Bash tricks: Difference between revisions

From wikinotes
 
 
(21 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Parallelization with Xargs =
<blockquote>
<syntaxhighlight lang="bash">
# 8x parallel processes at once
echo "${git_repos[@]}" \
    | tr ' ' '\n' \
    | xargs -P 8 -I REPO  git pull REPO
</syntaxhighlight>
</blockquote><!-- Parallelization with Xargs -->
= Skip first N lines of output =
<blockquote>
<syntaxhighlight lang="bash">
cat << EOF | tail -n +2  # prints all lines, starting with #2 (fiona)
NAME
fiona
alex
katie
EOF
</syntaxhighlight>
</blockquote><!-- Skip first N lines of output -->
= Strip Leading/Trailing Whitespace =
<blockquote>
<syntaxhighlight lang="bash">
echo '  abc def hij  ' | awk '{$1=$1;print}'
</syntaxhighlight>
</blockquote><!-- Strip Leading/Trailing Whitespace -->
= raw output =
= raw output =
<blockquote>
<blockquote>
Line 25: Line 54:
</syntaxhighlight>
</syntaxhighlight>
</blockquote><!-- num tokens in a line -->
</blockquote><!-- num tokens in a line -->
= Repeat character N times =
<blockquote>
<syntaxhighlight lang="bash">
printf -- '=%.0s' {1..5}      # repeat '=' 5 times
printf -- '=%.0s' $(seq 1 5)  # repeat '=' 5 times
</syntaxhighlight>
Some useful applications
<syntaxhighlight lang="bash">
# 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
</syntaxhighlight>
</blockquote><!-- repeat character n times -->
= Iterate over tokenize string =
<blockquote>
Useful for iterating over <code>$PATH</code> style variable paths that may contain spaces.
<syntaxhighlight lang="bash">
echo "$PATH" | tr ':' '\n' | while read -r path; do
    echo "--$path"
done
</syntaxhighlight>
</blockquote><!-- Tokenize a string -->
= calculate sum of input-lines =
<blockquote>
<syntaxhighlight lang="bash">
# returns sum (15)
cat << EOF | awk "{ sum += \$1 } END { if (NR > 0) print sum }"
1
2
3
4
5
EOF
</syntaxhighlight>
</blockquote><!-- calculate sum of input-lines -->
= calculate average of input-lines =
<blockquote>
<syntaxhighlight lang="bash">
# returns avg (3)
cat << EOF | awk "{ sum += \$1 } END { if (NR > 0) print sum / NR }"
1
2
3
4
5
EOF
</syntaxhighlight>
</blockquote><!-- average of input-lines -->
= calculate p95 percentile of input-lines =
<blockquote>
n=4 divides data into 1/4s.<br>
we're choosing the 2nd index (3rd quartile or 75th percentile).
<syntaxhighlight lang="bash">
# 95th percentile
cat << EOF | python -c 'import sys; import numpy; q = list(map(lambda x: float(x), filter(lambda x: x.strip() != "",  sys.stdin.read().split("\n")))); print(numpy.quantile(q, 0.95))'
1
2
3
4
EOF
</syntaxhighlight>
</blockquote><!-- calculate 3rd quartile of input-lines -->
= UUIDs =
<blockquote>
* linux: e2fsprogs, libuuid
* freebsd: base
<syntaxhighlight lang="bash">
uuidgen  # generate a uuid
</syntaxhighlight>
</blockquote><!-- UUIDs -->


= Simple Menu =
= Simple Menu =
Line 32: Line 145:


<source lang="bash">
<source lang="bash">
options=(
# Format:  ${MENU_ITEM} ${COMMAND} ...
     "1- this"
CHOICES=(
    "2- is"
     "say hi"
     "3- a"
        "echo hi && sleep 1"
    "4- test"
 
     "say bye"
        "echo bye && sleep 1"
)
)




executeMenu() {
print_error() {
     # substitute with command to execute for each menu
     # """ prints an error with a sleep
     echo "ENTERING MENU $1"
     # """
    message="$1"
    echo
    echo
    echo $message
    sleep 2
}
}




selectionMenu() {
show_menu() {
     #create array from argument
     # """ prints menu w/ choices
     menu=("${@}")
    # """
    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


     #ensure argument is present
     # defaults to 0
     if [ "${menu[0]}" == "" ] ; then
    test -z "$index" && index=0
         echo "Error: selectionMenu() requires an array as an argument"
 
         exit 0
    # must be integer
     if ! [ "$index" -eq "$index" ] 2> /dev/null ; then
         print_error "Expected Integer. Received: ${index}"
         return 1
     fi
     fi


     # variables
     # reject numbers above $num_choices
     local menuSize=$(echo ${#menu[@]} - 1 | bc -l)  # size of menu array
     if test "$index" -gt "$num_choices" ; then
     local selLine=0                                # selected Menu Line
        print_error "Expected Integer <${num_choices}. Received: ${index}"
     selBttn=""                                     # button pushed from menu
        return 1
    initMenu=1                                      # first run of current menu=true
    fi
 
    # exec chosen menu, after newline
     local array_index=$((2*$(expr $index + 1)))
 
     eval "${CHOICES[$array_index]}"
}


    printMenu() {
        clear
        case $selBttn in
            q)
                quit=1
                exit 0
                ;;
            k|p)
                ((selLine--))
                ;;
            j|n)
                ((selLine++))
                ;;
            '' )
                #otherwise menu is entered on first load
                if [ "$initMenu" != "1" ] ; then
                    initMenu=1
                    executeMenu "$selLine"
                fi
                ;;
        esac
        initMenu=0
   
        #restrict menu selection to available menu options
        if [ "$selLine" -gt "$menuSize" ] ; then
            selLine=$menuSize
        elif [ "$selLine" -lt "0" ] ; then
            selLine=0
        fi


        #print menu to screen
mainloop() {
        for i in $( seq 0 $menuSize ) ; do
    if [[ $(tty) == /dev/tty1 ]] ; then
            if [ "$selLine" == "$i" ] ; then
        while true; do
                echo "* ${menu[$i]}"
             show_menu
             else
             choose_menu_index
                echo "  ${menu[$i]}"
             fi
         done
         done
        #read single char input
        read -n 1 selBttn
        printMenu
    }
    if [ "$quit" != "1" ] ; then
        printMenu
     fi
     fi
}  
}
 
selectionMenu "${options[@]}"
</source>
</source>
</blockquote><!-- simple menu -->
</blockquote><!-- simple menu -->
= Executable Archive =
<blockquote>
...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
</blockquote><!-- Executable Archive -->

Latest revision as of 01:53, 26 July 2023

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

calculate sum of input-lines

# returns sum (15)
cat << EOF | awk "{ sum += \$1 } END { if (NR > 0) print sum }"
1
2
3
4
5
EOF

calculate average of input-lines

# returns avg (3)
cat << EOF | awk "{ sum += \$1 } END { if (NR > 0) print sum / NR }"
1
2
3
4
5
EOF

calculate p95 percentile of input-lines

n=4 divides data into 1/4s.
we're choosing the 2nd index (3rd quartile or 75th percentile).

# 95th percentile
cat << EOF | python -c 'import sys; import numpy; q = list(map(lambda x: float(x), filter(lambda x: x.strip() != "",  sys.stdin.read().split("\n")))); print(numpy.quantile(q, 0.95))'
1
2
3
4
EOF

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