solomem / DevOps

0 stars 0 forks source link

bash tutorial (LkL) #6

Open solomem opened 1 year ago

solomem commented 1 year ago

Bourne Again Shell

Bash is a shell, and the commands it provides can be used either interactively at the command line, or composed into scripts, which can be run like regular programs.

Pipe and redirections

image image ls > list.txt # overwrite the file ls >> list.txt # append the file

Bash built-ins and other commands

image

built-in:

A Bash builtin is a command that's part of Bash, which you can rely on being present wherever Bash is running.

program: /usr/bin/df

The shell will use the builtin version of the command over the existing program, even there is a command of the same name available on the system.

$ enable -n echo #This enables the program
$ command -V echo
echo is /usr/bin/echo

Brackets and braces in Bash

image

Bash expansions and substitutions

image image ~, tilde character represents the value of the user's $HOME variable. image ~-: Another useful tilde expansion trick is that the tilde followed by a dash or minus represents a Bash variable called OLDPWD. Which is the directory that you were just in, if you've recently changed directories. image

Brace expansion {}

image echo {00..100} echo {100..00} #padded version

echo {a..z}
echo {Z..A}
echo {1..30.3}

image

image

Parameter expansion ${...}

Retrieving a full or partial value from a parameter allows us to work with variables. image

image image check the bash man page for the use of parameter expension

Command substitution

run a command or command pipeline, and then return the output of that to Bash, as a string of text or as a variable. image Command substitution is a kind of special case expansion which allows us to use the output of a command within another command. This is represented by a dollar sign and a set of parentheses enclosing a command.

image

tr: transform image

Arithmetic expansion

image

solomem commented 1 year ago

Understanding Bash script syntax

There's two ways to consolidate individual commands and start to build distributable repeatable ways of running Bash commands. The first of these are what are called one-liners and they're just what they sound like, a command or series of commands presented as one line of text. These are usually command pipelines or a series of separate Bash commands separated by semicolons. Often these single lines of Bash run for hundreds of characters so sometimes when we write or see a one-liner it'll wrap around in a terminal, but that's okay. It's still a one-liner because it doesn't contain any line breaks to separate the script multiple lines. This makes one-liners suitable for copying and pasting into a terminal. Many developers and CIS admins keep a collection of useful one-liners in a notes document for taking care of frequent tasks or to give themselves the opportunity to customize something they do for frequently. One-liners are also useful because they can be used with the alias built in, providing a way to make a short command that runs with some Bash commands.

image The other common way of combining Bash commands together in a way that's easier to work with and distribute is to make Bash scripts. A Bash script is a text file that contains one or more Bash commands intended to be run like a program. There's two ways of running a Bash script. The first is to use the Bash command and the name of the script. That tells Bash to run the contents of the text file. When we do this, it's common to add a .sh extension on the end of the file name, so we can tell at a glance that a file is a shell script, but that's optional. The other way, which is generally more common is to make an executable script which can be run just like any other program. The first step here is to add a line at the top of the script called the shebang line which tells the shell what program to use when the script is run. A shebang line starts with a pound or hash sign and an exclamation mark, and then the full path to whatever program should run the script. In this case, we'll use Bash but this can be anything like Python or Ruby or whatever interpreted scripting language you need to make into a program. It's common to see the absolute path to Bash in a shebang line in older script like, slash bin slash Bash but it's often considered a best practice to write the shebang line in a way that uses the shell's environment to locate the Bash executable. Not all systems have Bash in the same place though almost all of them do. And so instead of asking for bin, Bash and not finding it in that file path, we ask the environment to give us Bash which will work wherever Bash is installed. If we leave off a shebang, the contents of the script file will be passed to the current shell on my Linux system that would be Bash anyway but we don't know what shell other people are using at any given time, so it makes sense to include the shebang. So we guarantee that our Bash script is run by Bash. image

image

Displaying text with "echo"

image

echo -n xxx # no new-line when printing

Working with variables

image

var="value"

solomem commented 1 year ago

Working with numbers

image Remember that an expansion replaces itself with the result of a calculation, and an evaluation only returns success or failure. An evaluation operates on the stored value, and an expansion replaces itself with the result of a calculation.

image Arithmetic evaluation: ((...)) Arithmetic expansion: $((...))

bash treats number using $var as a string, then cannot be used for arithmetic calculation image

Limitation of the bash: integer only.

Alternatively, declare the variable to an integer first, then do the calculation! image

Using bc and awk (install using distribution package manager)

image


Comparing values with test

As compared with the regular test notation, the extended test notation allows us to have a single test with more than one condition. Multiple conditions allow us to use more complex logic. image

image

Comparing values with extended test

image Extended test gives the same results as the test, and adds a few other helpful features.

image

check if variable myvar is empty:

[ -z $myvar ]

printf

Using printf instead of echo allows us to tokenize values used in the string for easier management and editing, and provide format strings to control the display of text

solomem commented 1 year ago

Formatting and styling text output

image

\t
\n

\033: text format instructions [xx;xxm: start with opening square bracket, test and background colors, and ends with m.

image

#!/usr/bin/env bash
echo -e "\033[33;44mColor Text\033[0m"
echo -e "\033[30;42mColor Text\033[0m"
echo -e "\033[41;105mColor Text"
echo "some text that shouldn't have styling"
echo -e "\033[0m"
echo "some text that shouldn't have styling"
echo -e "\033[4;31;40mERROR:\033[0m\033[31;40m Something went wrong.\033[0m"

echo -e $ulinered"ERROR:"$none$red" Something went wrong."$none

solomem commented 1 year ago

Formatting output with printf

image

image

echo "The results are: $(( 2 + 2 )) and $(( 3 / 1 ))"
printf "The results are: %d and %d\n" $(( 2 + 2 )) $(( 3 / 1 ))

echo "----10----| --5--"

echo "Right-aligned text and digits" printf "%10s: %5d\n" "A Label" 123 "B Label" 456

echo "Left-aligned text, right-aligned digits" printf "%-10s: %5d\n" "A Label" 123 "B Label" 456

echo "Left-aligned text and digits" printf "%-10s: %-5d\n" "A Label" 123 "B Label" 456

echo "Left-aligned text, right-aligned and padded digits" printf "%-10s: %05d\n" "A Label" 123 "B Label" 456

echo "----10----| --5--"


- Working with dates

!/usr/bin/env bash

printf "%(%Y-%m-%d %H:%M:%S)T\n" 1658179558

current datatime

date +%s

get a formatted datatime

date +%Y-%m-%d\ %H:%M:%S

printf "%(%Y-%m-%d %H:%M:%S)T\n" $(date +%s)

the printf can handle missing datetime value

printf "%(%Y-%m-%d %H:%M:%S)T\n" printf "%(%Y-%m-%d %H:%M:%S)T is %s\n" -1 "the time"

![image](https://user-images.githubusercontent.com/44691256/208297846-492ecaa8-9143-4179-9a62-53e011cb1e43.png)

## placeholder for xxx in a printf statement:

%t %d %s

solomem commented 1 year ago

Working with arrays

Indexed arrays allow us to access array elements by their index number, and associative arrays allow us to access array elements by an alphanumeric key.

image

$ declare -a snacks=("apple" "banana" "orange")
$ echo ${snacks[1]}
banana
$ snacks[5]="grapes"
$ snacks+=("mango") # have to use parenthsis, otherwise it will append to the first value
$ echo ${snacks[@]}
apple banana orange grapes mango
$ for i in {0..6}; do echo "$i: ${snacks[i]}"; done
0: apple
1: banana
2: orange
3: 
4: 
5: grapes
6: mango

# to produce the error if not using ()
$ snacks+="Chilly" # <<<
$ for i in {0..6}; do echo "$i: ${snacks[i]}"; done
0: appleChilly #<<< 
1: banana
2: orange
3: 
4: 
5: grapes
6: mango

Associated array (dictionary)

-A to make NAMEs associative arrays (if supported) image

solomem commented 1 year ago

print system information

I create a variable called freespace, and I set that equal to a command substitution that uses the df command, which reports file system disk space, with a -h option for human readable output. And then as an argument, I'm providing the root of the file system. I pipe that into awk and I use a small awk program to get field number four on line 2. I do something similar for memory with a variable freemem using the command free. Then, I define a few format strings. Then, I use printf to create the variable logdate with a formatted version of today's date. After that, I output some text with color formatting, then I use printf to format the output that I want the user to see including some commands, some Bash variables, variables that I created, and other commands. All right, let's run this. All right, ./Solutions/sysreport, which is the name of my file. And here I see the output. Scripts like this can be extended in many ways and I recommend that you keep a few scripts around as projects to tinker with when you feel like it. image image

solomem commented 1 year ago

best-practice shebang line

#!/usr/bin/env Bash

This is a more portable way of writing the shebang because the location of Bash on different systems can vary.

solomem commented 1 year ago

Conditional statements with the "if" keyword

image

#!/usr/bin/env bash
declare -i a=3

if [[ $a -gt 4 ]]
then
    echo "$a is greater than 4!"
elif (( $a > 2))
then
    echo "$a is greater than 2!"
else
    echo "$a is not greater than 4!"
fi
solomem commented 1 year ago

Working with 'while' and 'untill'

image

#!/usr/bin/env bash
echo "While Loop"
declare -i n=0

while ((n < 10))
do
    echo "n:$n"
    ((n++))
done

echo -e "\n Until Loop"

declear -i m=0
until ((m==10)); do
    echo m:$m
    ((m++))
done
solomem commented 1 year ago

Introducing "for" loops

image

#!/usr/bin/env bash

for i in {1..10}
do
    echo $i
done

for i in 11 12 13
do
    echo $i
done

for ((i=1; i<10; i++))
do
    echo $i
done
#!/usr/bin/env bash

# Array
declare -a fruit=("apple" "banana" "cheery")
for i in ${fruit[@]}
do
echo $i; done

# Associated Array
declare -A arr
arr["name"]="scott"
arr["id"]="1234"
# use ! to access the keys of the array
# keys are strings, can could have space in them, so use "" to make sure
# it does not cause any problems when expand them
for i in "${!arr[@]}"
do
    echo $i: "${arr[$i]}"
done
#!/usr/bin/env bash

# use files
for i in $(ls)
do
    echo "Found a file: $i"
done

echo

# the * approach preserves the spaces in file names
for i in *
do
    echo "Found a file: $i"
done
solomem commented 1 year ago

Selecting behavior using "case"

image

#!/usr/bin/env bash

# case statement,
# ) means the end of test
# ;; tells bash we are done with this condition
# *: catch nothing matches
# esac: close the case statement
animal="dog"
case $animal in
    bird) echo "Avian";;
    dog|puppy) echo "Canine";;
    *) echo "No Match!";;
esac
solomem commented 1 year ago

Using functions

image

#!/usr/bin/env bash

# Using functions
# function needs to be declared before being used

greet() {
    echo "Hello there, $1! What a nice $2!"
}

echo "And now, a greeting!"
greet Scott Morning
greet Mick Everning

image

#!/usr/bin/env bash

# Using functions
# function needs to be declared before being used

numberthings() {
    i=1
    for f in "$@"; do
        echo $i: "$f"
        ((i++))
    done
    echo "This counting is from function $FUNCNAME"
}

numberthings /*
echo
numberthings pine birch maple "spruce 1"

Using functions

function needs to be declared before being used

var1="out"

myfunction() { var2="in" local var3="local" }

myfunction

echo $var1 echo $var2 echo $var3

solomem commented 1 year ago

Reading and writing text files

image

Read and write text files

for i in {1..5} do echo "This is line $i" > textfile.txt echo "This is line $i" >> textfilea.txt done

cat textfile.txt echo cat textfilea.txt

- `<`

!/usr/bin/env bash

Read and write text files

each time through the loopm bash reads one line from the file, and set the value to variable f

when read has nothing to consume, if returns false, and the read ends

while read f do echo "I read a line it says: $f" done < textfilea.txt

solomem commented 1 year ago

A fortune-telling game

#!/usr/bin/env bash

# A fortune-telling game

echo -e "\t\t   Welcome  to  the "
echo -e "\t\t🔮 \033[5mMYSTICAL  SPHERE\033[0m 🔮"
echo

waitingnumber=$(( 0 + RANDOM % 3 ))
mysterynumber=$(( 1 + RANDOM % 10 ))

declare -a fortunes=(
    "You are likely to achieve the outcome you seek."
    "Today is not a good day to do that."
    "While it might seem unlikely, your chances are good."
    "If you believe you will be successful, that's half the battle."
    "If you cared enough to ask, you care enough to make it happen."
    "I think you already know the answer to that."
    "Stop wondering and start doing!"
    "Yes, sure, whatever, I'm busy."
    "Next Thursday might be a better day to do that."
    "Sure, but what will the neighbors think?"
)

case $waitingnumber in
    0) sleep 1; echo "One moment please..."; sleep 1;;
    1) sleep 1; echo "Your fortune will be along shortly..."; sleep 2;;
    2) sleep 1; echo "Preparing your fate..."; sleep 1;;
    3) sleep 1; echo "The veil of mystery is dark today..."; sleep 3;;
esac

echo
echo "${fortunes[mysterynumber]}"
echo
solomem commented 1 year ago

Working with arguments

image

#!/usr/bin/env bash

# Args position explicit

echo "The $0 script got the argument: $1."
echo "Args2 is $2"
#!/usr/bin/env bash

# Args with all inputs
# "" making sure space is captured in the arguments
for i in "$@"
do
    echo "$i"
done
echo "There are $# arguments."
solomem commented 1 year ago

Working with options

image

#!/usr/bin/env bash

# options
# opt: this is the opt + arg
# opt  this is opt only without arg
# :    at the beginning tells the script to capture unknown options
while getopts :u:p:ab option; do
    case $option in
        u) user=$OPTARG;;
        p) pass=$OPTARG;;
        a) echo "got the 'a' flag";;
        b) echo "got the 'b' flag";;
        ?) echo "Option $OPTARG is not defined."
    esac
done

echo "user: $user / pass: $pass"
solomem commented 1 year ago

Getting input during execution

image

echo "What is your name?" read name

echo "What is your password?" read -s pass

pass to variable

read -p "What is your favorite animal? " animal

echo "Name: $name / pass: $pass / animal: $animal"


`help read`

- `select`

!/usr/bin/env bash

select

because the command is seperated by ;

that is why we use ;; to tell this is the end of the option

select animal in "bird" "dog" "fish" "quit" do case $animal in bird) echo "Birds like to fly";; dog) echo "Dog like to play catch";; fish) echo "Fish like to swim";; quit) break;; *) "Not defined." esac done

solomem commented 1 year ago

Ensuring a response

echo "$favcolor"


- checking the number of input values

!/usr/bin/env bash

Ensuring a response

if (($#<3)); then echo "THis command requires three arguments:" echo "Username, userid, and favorite number" else echo "username: $1" echo "userid: $2" echo "favorite number: $3" fi


- check for empty 

!/usr/bin/env bash

check for empty input

read -p "Favorite animal? " fav while [[ -z $fav ]] do read -p "Favorite animal? " fav done echo "$fav was selected."


- provide default value fashion in bash

!/usr/bin/env bash

check for empty input

square bracket used in the read prompt, kinda convention to say this is the default value if no input value is given

read -p "Favorite animal? [cat] " fav if [[ -z $fav ]]; then fav="cat" fi echo "$fav was selected."


- use regular expression

!/usr/bin/env bash

use regular expression to check the input values

read -p "What year? [nnnn] " year until [[ $year =~ [0-9]{4} ]]; do read -p "What year? [nnnn] " year done echo "Selected year: $year"

solomem commented 1 year ago

regular expression place holder

solomem commented 1 year ago

Multi functional game

#!/usr/bin/env bash

# A three-in-one game app

# The game definitions
guess() {
    local -i mynumber=$(( 1 + RANDOM % 10 ))
    read -rp "I'm thinking of a number between 1 and 10. What do you think it is? " theguess
    if (( theguess == mynumber )); then
        echo "You got it! Great Job!"; echo
    else
        echo "Nope. I was thinking of $mynumber. Try again!"; echo
    fi
    sleep 1
    choosegame
}

flip() {
    local -i mynumber=$(( 1 + RANDOM % 2 ))
    if (( mynumber == 1 )); then
        local face="HEADS"
    else
        local face="TAILS"
    fi
    printf "I flipped a coin and it was: %s\n\n" $face
    sleep 1
    choosegame
}

dice() {
    local -i mynumber=$(( 1 + RANDOM % 6 ))
    local -i mynumber2=$(( 1 + RANDOM % 6 ))
    printf "I rolled two dice and the results are: %s and %s.\n\n" $mynumber $mynumber2
    sleep 1
    choosegame
}

# The game chooser
choosegame() {
    select game in "Guessing Game" "Flip a Coin" "Dice Roll" "Exit"
    do
        case $game in
            "Guessing Game") guess;;
            "Flip a Coin") flip;;
            "Dice Roll") dice;;
            "Exit") exit;;
            *) echo "Please choose from the menu.";;
        esac
    done
}

# If there's an argument provided, jump right to that game. Otherwise, show the game chooser.
case $1 in
    "guess") guess;;
    "flip") flip;;
    "dice") dice;;
    *) choosegame;;
esac
solomem commented 1 year ago

Troubleshooting tips

image

image

set -x to display command while running

solomem commented 1 year ago

help set

$ help set
set: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
    Set or unset values of shell options and positional parameters.

    Change the value of shell attributes and positional parameters, or
    display the names and values of shell variables.

    Options:
      -a  Mark variables which are modified or created for export.
      -b  Notify of job termination immediately.
      -e  Exit immediately if a command exits with a non-zero status.
      -f  Disable file name generation (globbing).
      -h  Remember the location of commands as they are looked up.
      -k  All assignment arguments are placed in the environment for a
          command, not just those that precede the command name.
      -m  Job control is enabled.
      -n  Read commands but do not execute them.
      -o option-name
          Set the variable corresponding to option-name:
              allexport    same as -a
              braceexpand  same as -B
              emacs        use an emacs-style line editing interface
              errexit      same as -e
              errtrace     same as -E
              functrace    same as -T
              hashall      same as -h
              histexpand   same as -H
              history      enable command history
              ignoreeof    the shell will not exit upon reading EOF
              interactive-comments
                           allow comments to appear in interactive commands
              keyword      same as -k
              monitor      same as -m
              noclobber    same as -C
              noexec       same as -n
              noglob       same as -f
              nolog        currently accepted but ignored
              notify       same as -b
              nounset      same as -u
              onecmd       same as -t
              physical     same as -P
              pipefail     the return value of a pipeline is the status of
                           the last command to exit with a non-zero status,
                           or zero if no command exited with a non-zero status
              posix        change the behavior of bash where the default
                           operation differs from the Posix standard to
                           match the standard
              privileged   same as -p
              verbose      same as -v
              vi           use a vi-style line editing interface
              xtrace       same as -x
      -p  Turned on whenever the real and effective user ids do not match.
          Disables processing of the $ENV file and importing of shell
          functions.  Turning this option off causes the effective uid and
          gid to be set to the real uid and gid.
      -t  Exit after reading and executing one command.
      -u  Treat unset variables as an error when substituting.
      -v  Print shell input lines as they are read.
      -x  Print commands and their arguments as they are executed.
      -B  the shell will perform brace expansion
      -C  If set, disallow existing regular files to be overwritten
          by redirection of output.
      -E  If set, the ERR trap is inherited by shell functions.
      -H  Enable ! style history substitution.  This flag is on
          by default when the shell is interactive.
      -P  If set, do not resolve symbolic links when executing commands
          such as cd which change the current directory.
      -T  If set, the DEBUG and RETURN traps are inherited by shell functions.
      --  Assign any remaining arguments to the positional parameters.
          If there are no remaining arguments, the positional parameters
          are unset.
      -   Assign any remaining arguments to the positional parameters.
          The -x and -v options are turned off.

    Using + rather than - causes these flags to be turned off.  The
    flags can also be used upon invocation of the shell.  The current
    set of flags may be found in $-.  The remaining n ARGs are positional
    parameters and are assigned, in order, to $1, $2, .. $n.  If no
    ARGs are given, all shell variables are printed.

    Exit Status:
    Returns success unless an invalid option is given.
solomem commented 1 year ago

>, >& and &>

The operators we are using here are:

> Syntax: file_descriptoropt > file_name eg: grep "env" * 1>file.txt grep "env" * 2>file.txt

>& Syntax: file_descriptoropt >& file_descriptor eg: >file.txt 2>&1

&> Syntax: &> file_name If the file descriptor is omitted, the default is 0 (stdin) for input, or 1 (stdout) for output. 2 means stderr.

So we have:


Example: git status 2&>1 > /dev/null

there are 3 separate parts that break out like this (even though this is not intuitive nor obvious, especially considering that there is no space between the 2 and &>1 in 2&>1):

git status 2 # statement 1 &>1 # statement 2 > /dev/null # statement 3 If you had the following, however, they are all very different than the above. We'll talk about these later in this answer:

separates into two separate statements: 2 and &>1 2&>1 # 2 is NOT part of &>1

these are all single statements, but mean very different things (more on these below) 2>&1 2>1 Statement 1 (git status 2) runs git status 2. Here, 2 is a parameter passed to git status. Since it's not a valid option, git status assumes it to be a path, which can be a file or directory. So, git returns the status of all files named 2, or of all files within a directory named 2. As man git status shows, a better way to specify paths is to separate options from paths with --, like this:

git status -- 2 # 2 is a file or folder here Obviously, that's not what you're trying to do. :)

Statement 2 (&>1) redirects stdout and stderr to a file named 1. This is also obviously not what you are trying to do. :) The &>file syntax redirects all stdout and stderr to file. You can read about this in the bash manual online here, or by running man bash and and searching for the section named "Redirecting Standard Output and Standard Error". We'll go over this section of the manual more later.

Note that &>1 (written as 2&>1--two separate statements: 2 and &>1, in your question) is not the same syntax nor concept as 2>&1. The latter, 2>&1, redirects file descriptor 2 (stderr) to file descriptor 1 (stdout), where the 3 primary file descriptors are:

0 = stdin 1 = stdout 2 = stderr Statement 3 (> /dev/null): this is where it starts to get tricky. This can also be written without the space, as >/dev/null. It redirects stdout (file descriptor 1) to the /dev/null Linux pseudofile, which discards any output written to it. Writing >/dev/null is exactly identical to 1>/dev/null, as file descriptor 1 (stdout) is implied as the default option if not specified, as the bash manual states. Read more about it in the "Redirecting Output" section of the bash manual here.

It starts to get tricky here for two reasons:

First, because the 1 means two different things: in &>1 the one is a file named "1". This is likely a mistake on the user's part, but that's what it is. In >/dev/null there is an implied 1 as in 1>/dev/null, and that refers to file descriptor 1, which is stdout. Second, it is tricky because you have redirection overrides going on, where the last one sticks. &>1 redirects both stdout and stderr to a file named "1", but 1>/dev/null then redirects stdout to the "/dev/null" file. The latter redirection of stdout overrides the former, so the end result is that stderr is redirected to a file named "1" and stdout is redirected to the "/dev/null" file. This could have also been written as: redirect stderr to a file named "1", and redirect stdout to the file named "/dev/null" to discard it. 2>1 1>/dev/null

same thing &>1 > /dev/null The above information is begging for more explanation, so let's explain &> more and then give a detailed summary of how to correctly do redirection in bash.

solomem commented 1 year ago

Redirection the standard output and standard error into standard output.

Bash's man page mentions there's two ways to redirect stderr and stdout: &> file and >& file

>file 2>&1: we are doing redirection of stdout (1) to file, but then also telling stderr(2) to be redirected to the same place as stdout ! same as &>file

grep "xxx" * &> /dev/null

\where the 3 primary file descriptors are:

0 = stdin 1 = stdout 2 = stderr

solomem commented 1 year ago

df

df -h

solomem commented 1 year ago

$(dirname "$0")

The dirname $0 command returns the directory where the Bash script file is saved.  We can return a relative path or an absolute path. This all depends on how the bash script is called.

solomem commented 1 year ago

pushd and popd

The pushd command is used to save the current directory into a stack and move to a new directory. Furthermore, popd can be used to return back to the previous directory that is on top of the stack. It is very useful when we have to switch between two directories frequently

https://opensource.com/article/19/8/navigating-bash-shell-pushd-popd

solomem commented 1 year ago

multi line bash

PARAMS=(
    -cvpzf /share/Recovery/Snapshots/$HOSTNAME_$DATE.tar.gz 
    --exclude=`enter code here`/proc 
    --exclude=/lost+found 
    --exclude=/sys 
    --exclude=/mnt
    # this is a comment 
    --exclude=/media 
    --exclude=/dev 
    # --exclude=/something
    --exclude=/share/Archive 
    /
)
# the quotes are needed to preserve params with spaces
tar "${PARAMS[@]}"
solomem commented 1 year ago

trap command

- WHAT IS TRAP?

Trap is a built-in command used to respond to the process signals. A signal is a notification send to the process to notify of an event. Like SIGINT to interrup the command in the command line.

Trap can be used to catch all the signals. For all intentions and purposes, if you trap EXIT, the specificed command will be executed when the shell process terminates

Use Case: image If the program creates temp files, and remove them. What if it fails to remove the files? > security concerns Multiple exit point, make sure the program will clean them up.

- Use trap on exit:

image

image

Use trap to ensure service availability

image

- Ensure Ports Are Closed After Script Exit

image

- Using A Function In a Trap Call

image


https://www.linuxjournal.com/content/bash-trap-command

Trap allows you to catch signals and execute code when they occur. Signals are asynchronous notifications that are sent to your script when certain events occur. Most of these notifications are for events that you hope never happen, such as an invalid memory access or a bad system call. However, there are one or two events that you might reasonably want to deal with. There are also "user" events available that are never generated by the system that you can generate to signal your script. Bash also provides a psuedo-signal called "EXIT", which is executed when your script exits; this can be used to make sure that your script executes some cleanup on exit.

The man page for signal(7) describes all the available signals. The Wikipedia page for signal (IPC) has a bit more detail. As I mentioned, most of them are of little interest in scripts.

The SIGINT signal is perhaps the only one that might be of interest in a script. SIGINT is generated when you type Ctrl-C at the keyboard to interrupt a running script. If you don't want your script to be stopped like this, you can trap the signal and remind yourself that interrupting the script is to be avoided. Although, as you'll see, this is less useful than one might hope.

The SIGUSR1 signal is a "user"-defined signal that you can use however you like. it is never generated by the system.

EXIT: The most common use of the trap command though is to trap the bash-generated psuedo-signal named EXIT. Say, for example, that you have a script that creates a temporary file. Rather than deleting it at each place where you exit your script, you just put a trap command at the start of your script that deletes the file on exit:

tempfile=/tmp/tmpdata
trap "rm -f $tempfile" EXIT

Now whenever your script exits, it deletes your temporary file.

The syntax for the trap command is trap COMMAND SIGNALS..., so unless the command you want to execute is a single word, the "command" part should be quoted.

If your cleanup needs are complex, you don't have to try to jam it all into a string with semicolons, just write a function:

function cleanup()
{
    # ...
}

trap cleanup EXIT

Note that if you send a kill -9 to your script, it will not execute the EXIT trap before exiting.

The other possible thing that you might like to use the trap command for is to catch Ctrl-C so that your script can't be interrupted or perhaps so you can ask if the user really wants to interrupt the process. As an example, I'll use the following handler function, which warns the user on the first two Ctrl-Cs and then exits on the third:

ctrlc_count=0

function no_ctrlc()
{
    let ctrlc_count++
    echo
    if [[ $ctrlc_count == 1 ]]; then
        echo "Stop that."
    elif [[ $ctrlc_count == 2 ]]; then
        echo "Once more and I quit."
    else
        echo "That's it.  I quit."
        exit
    fi
}

Use the following to test the handler:

trap no_ctrlc SIGINT

while true
do
    echo Sleeping
    sleep 10
done

If you run that and type Ctrl-C three times, you'll get the following output:

noctrlc.sh


#!/usr/bin/env bash

ctrlc_count=0

function no_ctrlc()
{
    let ctrlc_count++
    echo
    if [[ $ctrlc_count == 1 ]]; then
        echo "Stop that."
    elif [[ $ctrlc_count == 2 ]]; then
        echo "Once more and I quit."
    else
        echo "That's it.  I quit."
        exit
    fi
}

trap no_ctrlc SIGINT

while true
do
    echo Sleeping
    sleep 10
done
$ bash noctrlc.sh
Sleeping
^C
Stop that.
Sleeping
^C
Once more and I quit.
Sleeping
^C
That's it.  I quit.
$

SIGNUP: What is SIGHUP in Linux? The SIGHUP (“hang-up”) signal is used to report that the user's terminal is disconnected, perhaps because a network or telephone connection was broken

solomem commented 1 year ago

signals

Signals

A signal is the means Linux uses for sending information between processes or between the kernel and a process.

Simply put, it's a way of communicating between disparate daemons or processes on the system - a little like in the days of old, where train drivers used battens to relay signals to the station master. There are many signal types. Try:

kill -l --
$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGEMT       8) SIGFPE       9) SIGKILL     10) SIGBUS   
11) SIGSEGV     12) SIGSYS      13) SIGPIPE     14) SIGALRM     15) SIGTERM  
16) SIGURG      17) SIGSTOP     18) SIGTSTP     19) SIGCONT     20) SIGCHLD  
21) SIGTTIN     22) SIGTTOU     23) SIGIO       24) SIGXCPU     25) SIGXFSZ  
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGPWR      30) SIGUSR1  
31) SIGUSR2     32) SIGRTMIN    33) SIGRTMIN+1  34) SIGRTMIN+2  35) SIGRTMIN+3
36) SIGRTMIN+4  37) SIGRTMIN+5  38) SIGRTMIN+6  39) SIGRTMIN+7  40) SIGRTMIN+8
41) SIGRTMIN+9  42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13
46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMIN+16 49) SIGRTMAX-15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4
61) SIGRTMAX-3  62) SIGRTMAX-2  63) SIGRTMAX-1  64) SIGRTMAX
  1. Exp 1: kill process

    sleep 666 & # to keep the job running in the background
    ps -C sleep # list all sleep jobs (process IDs)
    kill -SIGTSTP <PID>
    jobs # list all running jobs
  2. Exp 2: SIGHUP The SIGHUP signal is sent to a process when its controlling terminal is closed. All attached process will be closed (gnome-terminal for example) will be closed. nohup prevents the process getting SIGHUP signal image

  3. Exp 3: pkill command kill process by the process name image

solomem commented 1 year ago

Shell Scripting (12 capters to read)

https://www.learnlinux.org.za/courses/build/shell-scripting/index.html

solomem commented 1 year ago

test

test "$PWD" == "$INFRA_PATH" && echo "true" || echo "false"

solomem commented 9 months ago

Set variable using colon : the no-op command : "${POSTGRES_HOST_AUTH_METHOD:=md5}"

The syntax : "${POSTGRES_HOST_AUTH_METHOD:=md5}" is used in shell scripts to set a default value for a variable if it is not already set. In this case, it sets the POSTGRES_HOST_AUTH_METHOD variable to "md5" if it is not already set.

Here's a breakdown of the syntax:

: is a shell built-in command that does nothing. It is used here as a placeholder to allow the variable assignment to happen without any side effects. ${VAR_NAME:=default_value} is a parameter expansion syntax that assigns a default value to a variable if it is not set or is empty. In this case, VAR_NAME is POSTGRES_HOST_AUTH_METHOD, and the default value is "md5". So, this line of code ensures that the POSTGRES_HOST_AUTH_METHOD variable has a value, either the one provided by the user or the default value "md5".

image

image