Low Orbit Flux Logo 2 F

BASH - Ninja

Notable Special Variables

$IFS list of characters that separate fields for word splitting
$PPID shell’s PPID
$@ Positional parameters, starting from one.
$# Number of positional parameters
$? Exit status of most recent executed foreground pipeline
$$ PID of Shell
$! PID of most recent executed background (asynchronous) command
$0 Name of shell or script

Brace expansion



echo f{oo,ee,a}d                 # food feed fad
echo {000..2}                    # new in Bash 4, 000 001 002
echo {00..8..2}                  # 00 02 04 06 08
echo {D..T..4}                   # D to T increment by 4

echo start{a,b{1,2,3},c}end                 # Nested brace expansion

for num in {000..2}; do echo "$num"; done   # in a for loop

Arithmetic Expansion



$(( 5 + 5 ))
$[ 5 + 5 ]   # older

Command Substitution, Subshell



(ls -l)      # subshell
x=$(ls -l)      # newer, easier to nest, POSIX
x=`ls -l`       # older, can have some escape chars?

Word splitting



IFS=$'\n'               # set IFS to just new lines

for i in $(ps -ef ); do echo $i; done    # now it splits by line

File name expansion ( globbing )



ls -l *
ls -l test?.txt
ls -l test[1-5].txt
ls -l test[a-z].txt

Character classes:



ls -ld [[:digit:]]*
ls -ld [[:upper:]]*

# "alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word" or "xdigit"

Aliases



alias ll='ls -l'    # create alias


xargs

The xargs command is used to send batches of arguments to a command. The command is run for each batch. Number of args is limted and running process per arg is slow.



find /data1 | xargs rm                       # remove all files listed
find /data1 | xargs grep "Error"             # grep for a string inside all files found
find /data1 -print0 | xargs -0 grep "Error"  # same but handle files with whitespace in name
ls *.txt | xargs gzip                        # gzip all files listed
find /data1 | xargs gzip                     # gzip all files found


Conditionals - if/else

If/else syntax:




if [ $x -eq "abc" ]; then
  echo "match 1"
elif [ $x -eq "xyz ]; then
    echo "match 2"
else
  echo "no match"
fi


if [ $x -eq "abc" ]; then echo "match 1"; fi  # one liner

Common tests:



if [ -z $x ];                          # check if string is: unset, empty, or all spaces
if [ -e "$filename" ];                 # check if file exists  ( -f regular file, -d dir, -e exists ), quotes to avoid word splitting
if [[ -e $filename ]];                 # [[ to avoid word splitting
if [ $a == "test" ];                   # strings equal
if [ $a != "test" ];                   # strings not equal
if [ $x -eq 5 ];                       # equal, can also use  -eq, -ne, -lt, le, -gt, ge
if [ $? -eq 0 ];                       # check exit status OK
if ! grep $extra_user /etc/passwd;     # not found
if [ "$(whoami)" != 'root' ]; then     # not root
if [[ "$x" == t* ]];                   # use a regex
if [ ! $# == 2 ];            # check number of args

if ls test1.txt; then echo asdf; else echo xxxxx; fi   # one liner, directly use command

Expressions, Logic, and checks:




((a++))                     # increment variable
((x = 42))                  # assign variable, arithmetic
((x=5+4))                   # assign variable, arithmetic
x=$((5+4))                  # assign variable, arithmetic
for ((i=0; i<10; i++))      # multiple operations used for for loop
echo $((a + b + (14 * c)))


if (( x = "5" )); ...         # test assignment
if ((5 == 5)); ...            # arrithmetic
if [ 5 -eq 5 ]; ...           # arrithmetic
if [ "5" -eq "5" ]; ...       # arrithmetic, string treated as integer
if [ "abc" -eq "xyz" ]; ...   # FAIL - expects integer
if [ "abc" == "xyz" ]; ...    # string compare
if [ 5 == 5 ]; ...            # works but is it comaring as string or int????
if [ 5 = 5 ]; ...             # works, aparently not assignment outside of arithmetic expansion
if [[ 5 -eq 5 || 1 -eq 3 ]]  ...   # logical or
if [[ 5 -eq 5 && 1 -eq 1 ]]; ...   # logical and
if [[ (5 -eq 5 ) || (1 -eq 1 && 2 -eq 2) ]];        # group for logic
if [[ $x =~ ab* ]];    # regex ( * still matches multiple chars ????)
if [[ $x =~ a.* ]];    # regex

Redirection, Background, and Pipes



# Redirection

ls > test1.txt            # redirect command output ( STDOUT ) to a file and **OVERWRITE** file
ls >> test1               # redirect command output ( STDOUT ) to a file and **APPEND** to file
ls asdf 2> test1.txt      # redirect command output ( STDERR ) to a file and **OVERWRITE** file
ls asdf 2>> test1.txt     # redirect command output ( STDERR ) to a file and **APPEND** to file
ls asdf &> test1.txt      # redirect command output ( STDOUT and STDERR ) to a file ( since Bash 4 )
ls asdf >> output.log 2>&1   # append STDOUT to file, also redirect STDERR to STDOUT ( both go to file )
ls asdf >> output.log 2>&1 &  # also run in background with extra &


# Background

./server1 &
./process_data.sh &

# Pipes

cat data1.csv | sort
ps -ef | grep -i nginx

Loops

For Loops



x="a b c"


for i in `cat list`; do
    echo $i ;
done


for i in `cat list`; do echo $i ; done
for i in `ls`; do echo $i ; done
for i in a b c; do echo $i; done
for i in {1..10}; do echo $i; done
for i in `seq 1 10`; do echo $i; done
for i in $x; do echo $i; done            # loop over string ( word splitting )
for i in /opt/*; do echo $i; done        # loop over globbed file list

for (( i=0; i<10; i++)); do echo $i; done


IFS=$'\n'                             # split on new line, not space or tab
for i in `ps -ef`; do echo $i; done

While Loops



i=0; while [ $i -lt 4 ]; do echo $i; i=$[$i+1];done


i=0
while [ $i -lt 4 ]; do
    echo $i
    i=$[$i+1]
done


while true; do      # infinite loop
    echo "."
    sleep 5
done

Arrays



declare -a ARRAYNAME

a[3]=5      # assign element, will declare even if not first element

a=(1 2 3)   # declare, initialize - no commas, just spaces
a=(a b c)   # declare, initialize - no commas, just spaces


echo ${a[3]}       # specific element
echo ${a[$x]}      # variable as an index
echo ${a[*]}       # entire array
echo ${a[@]}       # entire array
echo ${a[@]:2:4}   # slice array (index, len )

${#x[@]}           # array length ( differnt from string len )

unset a[3]         # remove element
unset a            # remove array

for i in ${a[@]}; do echo $i; done   # loop over array

a+=("test")        # append element to end of array
a+="test"          # append value to first element

Associative Arrays / Hashs ( BASH 4+ )



# Declare and initialize:

declare -A a=(
    [color]="red"
    [size]="5"
    [shape]="square"
)


# Declare then initialize

declare -A a

a[color]="red"
a[size]="5"
a[shape]="square"


echo ${a[color]}   # access element


unset a[color]     # remove an element

echo ${a[@]}       # all values
echo ${!a[@]}      # all keys
echo ${#a[@]}      # length


# Loop over hash:

for i in "${!a[@]}"; do
    echo $i ": " ${a[$i]}
done

Functions



test1 () {                         # totally valid, just need that space before {
    echo "Values: " $1 $2 $3 $#
    return 0
}

test1 ()
{
    local x='abc'                      # local variable
    y='xyz'                            # not local, survives outside the funciton even if first used here
    echo "Values: " $1 $2 $3 $#
    return 123                         # between 0-255
}


test1 a b c
echo $?

Signals

Signal name Signal value Effect
SIGHUP 1 Hangup
SIGINT 2 Interrupt from keyboard ctrl-c
SIGKILL 9 Kill signal, can’t block or ignore
SIGTERM 15 Termination signal
SIGSTOP 17,19,23 Stop the process, 19 is ctrl-z, can’t block or ignore

Send signals to processes:



kill    5607    #  SIGTERM 15 - kill process
kill -9 5607    #  SIGKILL 9 - force kill process
kill -HUP 5607  #  SIGHUP 1 - usually reload configs if supported

Dir Stack - Pushd / Popd

Navigate quickly with these:



dirs          # show dir stack
dirs -p       # show dir stack one entry per line
dirs -v       # same but with numbers


pushd /etc    # add this dir to the stack and switch to it
pushd         # swap top two dirs,
pushd +2      # dir 2 and below shifted to top

popd          # remove top dir from stack and change to that dir
popd +2       # remove dir 2 from stack

References