Open solomem opened 1 year ago
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.
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.
echo -n xxx
# no new-line when printing
var="value"
read only variable:
declare -r var_readonly="value2"
variable with lower case only
declare -l var_l="value3"
variable with upper case only
declare -u var_u="value4"
To see all variables:
declare -p
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.
Arithmetic evaluation:
((...))
Arithmetic expansion: $((...))
bash treats number using $var
as a string, then cannot be used for arithmetic calculation
change the value of variable:
declare the variable to integer, then do the calculation like:
Alternatively, declare the variable to an integer first, then do the calculation!
use bc (basic calculation)
generate random 1 - N value:
echo $(( 1 + RANDOM % N ))
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.
see what sting can be tested
help test
string compare
numerical compare
Extended test gives the same results as the test, and adds a few other helpful features.
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
echo -e "\a"
\t
\n
\033
: text format instructions
[xx;xxm
: start with opening square bracket, test and background colors, and ends with m
.
#!/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"
#!/usr/bin/env bash
ulinered="\033[4;31;40m"
red="\033[31;40m"
none="\033[0m"
echo -e $ulinered"ERROR:"$none$red" Something went wrong."$none
echo "The results are: $(( 2 + 2 )) and $(( 3 / 1 ))"
printf "The results are: %d and %d\n" $(( 2 + 2 )) $(( 3 / 1 ))
#!/usr/bin/env bash
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
printf "%(%Y-%m-%d %H:%M:%S)T\n" 1658179558
date +%s
date +%Y-%m-%d\ %H:%M:%S
printf "%(%Y-%m-%d %H:%M:%S)T\n" $(date +%s)
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
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.
help declare
to find out its usage.
$ 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
-A to make NAMEs associative arrays (if supported)
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.
#!/usr/bin/env Bash
This is a more portable way of writing the shebang because the location of Bash on different systems can vary.
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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"
#!/usr/bin/env bash
var1="out"
myfunction() { var2="in" local var3="local" }
myfunction
echo $var1 echo $var2 echo $var3
>
and >>
#!/usr/bin/env bash
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
- `<`
while read f do echo "I read a line it says: $f" done < textfilea.txt
#!/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
#!/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."
#!/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"
read
#!/usr/bin/env bash
echo "What is your name?" read name
echo "What is your password?" read -s pass
read -p "What is your favorite animal? " animal
echo "Name: $name / pass: $pass / animal: $animal"
`help read`
- `select`
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
# Ensuring a response
read -ep "Favorite color? " -i "Blue" favcolor
echo "$favcolor"
- checking the number of input values
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
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
read -p "Favorite animal? [cat] " fav if [[ -z $fav ]]; then fav="cat" fi echo "$fav was selected."
- use regular expression
read -p "What year? [nnnn] " year until [[ $year =~ [0-9]{4} ]]; do read -p "What year? [nnnn] " year done echo "Selected year: $year"
#!/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
set -x
to display command while running
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.
>
, >&
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:
>
name means 1>
name -- redirect stdout to the file name
eg: grep "evn" * 1> file1
# this redirect the stdout to file1
eg: grep "evn" * 2> file2
# this redirect the stderr to file2
&>
name is like 1>name 2>name -- redirect stdout and stderr to the file name (however name is only opened once; if you actually wrote 1>name 2>name it'd try to open name twice and perhaps malfunction).
So when you write git status 2&>1, it is therefore like git status 2 1>1 2>1 , i.e.
the first 2 actually gets passed as an argument to git status.
stdout is redirected to the file named 1 (not the file descriptor 1)
stderr is redirected to the file named 1
This command should actually create a file called 1 with the contents being the result of git status 2 -- i.e. the status of the file called 2 which is probably "Your branch is upto-date, nothing to commit, working directory clean", presuming you do not actually track a file called 2.
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.
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
df -h
$(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.
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
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[@]}"
trap
commandTrap 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:
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.
trap
on exit:trap
to ensure service availabilityhttps://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
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
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
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
Exp 3: pkill
command
kill process by the process name
https://www.learnlinux.org.za/courses/build/shell-scripting/index.html
test "$PWD" == "$INFRA_PATH" && echo "true" || echo "false"
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".
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
cat something.txt | wc
ls > list.txt
# overwrite the filels >> list.txt
# append the fileredirect the stream to different places:
ls /notreal 1>stdout.txt 2>stderr.txt
#stdout.txt will be blank, and stderr.txt will save the error stream.input redirection
cat < output.txt
Head documents
Bash built-ins and other commands
echo
ends with line with the new line characterprintf
ends with nothingbuilt-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.
Showing the builtin are disabled:
enable -n
To enable the builtin:
enable echo
Another important distinction to make here is that builtins use a different documentation system than the regular man pages.
help echo
orhelp
to display all builtinsBrackets and braces in Bash
Bash expansions and substitutions
~
, tilde character represents the value of the user's $HOME variable.~-
: 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.Brace expansion
{}
echo {00..100}
echo {100..00}
#padded versionParameter expansion
${...}
Retrieving a full or partial value from a parameter allows us to work with variables.![image](https://user-images.githubusercontent.com/44691256/208281382-e5a259c5-edcf-4b0e-8f31-1ccb3d093864.png)
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.
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.
tr
: transformArithmetic expansion