Bash Scripting


Most important module


What is a script

  • contains a series of commands
  • commands are interpreted by an interpreter
  • anything you can do in terminal you can do in a script
  • great for automating tasks

eg.

#!/bin/bash
echo "Scripting is fun!"
> chmod +x script.sh
> ./script.sh

Shebang

the #! in #!/bin/bash can be read as sharp bang which is weirdly abbreviated to shebang

the remaining gives the path to interpreter used to interpret the script

basically what happens is

> /bin/bash ./script.sh

or

> /bin/ksh ./script.sh

or whatever interpreter you use is being called to execute your script

you can check this with ps -fp <pid> and go deeper with pstree -p <pid>

command interpreters can be bash, python, zsh, fish or whatever


Variables

Syntax:

VARIABLE_NAME="Value"

no space before and after the ”=”

to use the variable do either $MY_VARIABLE or ${MY_VARIABLE}

eg

#!/bin/bash
MY_SHELL="bash"
echo "I am ${MY_SHELL}ing on my keyboard" # here you need to put it in curly braces
>> I am bashing on my keyboard

you can assign the output of a command to a variable

eg

#!/bin/bash
SERVER_NAME=$(hostname)
echo "You are running this sript on ${SERVER_NAME}"

enclose the command in parantheses or backticks

i.e. SERVER_NAME=$(hostname) or SERVER_NAME=`hostname`

names can not

  • start with numbers
  • contain symbols except for ’_‘

Tests

Syntax:

[ condition-to-test-for ]

example:

[ -e /etc/passwd ] # checks if /etc/passwd exists

go through the man page for test for more info

> man test

common tests are

testdesc
-d FILEtrue if file is a directory
-e FILEtrue if file exists
-f FILEtrue if file exists and is a regular file
-r FILEtrue if file is readable by user
-s FILEtrue if file exists and is not empty
-w FILEtrue if the file is writeable
-x FILEtrue if the file is executable by you
-z STRINGtrue if string is empty
-n STRINGtrue if string is not empty
STRING1 = STRING2true if strings are not equal
STRING1 != STRING2true if strings are not equal
arg1 -eq arg2arg1 = arg2
arg1 -ne arg2arg1 != arg2
arg1 -lt arg2arg1 < arg2
arg1 -le arg2arg1 arg2
arg1 -gt arg2arg1 > arg2
arg1 -ge arg2arg1 >= arg2

Decisions

the if statement

if [ condition-is-true ]
then 
	command 1
	command 2
fi
#!/bin/bash
MY_SHELL="bash"
 
if [ "$MY_SHELL" = "bash" ]
then 
	echo "why don't you use fish???"
fi

if-else statements

if [ condition-is-true ]
then 
	command N
else 
	command M
fi

eg

#!/bin/bash
MY_SHELL="fish"
 
if [ "$MY_SHELL" = "fish" ]
then 
	echo "good choice!!!"
else 
	echo "use fish bruh"
fi

if-elif-else

if [ condition-is-true ]
then
	command N
elif [ condition-two ]
then
	command M
else 
	command X
fi

For Loops

Syntax:

for VARIABLE_NAME in ITEM_1 ITEM_2
do 
	command 1
	command 2
	command N
done

eg

#!/bin/bash
for COLOR in red green blue
do
	echo "$COLOR"
done
>> red
>> green
>> blue

items can be in a list

eg

#!/usr/bin/bash
COLORS="red green blue"
 
for COLOR in $COLORS
do
	echo "$COLOR"
done
>> red
>> green
>> blue

script to rename files

eg

#!/usr/bin/bash
PICTURES=$(ls *jpg)
DATE=$(date +%F)
 
for PICTURE in $PICTURES
do
	echo "Renaming ${PICTURE} to ${DATE}-${PICTURE}"
	mv $PICTURE ${DATE}-${PICTURE}
done
> ls *jpg
>> bear.jpg man.jpg pig.jpg
> ./rename-pics.sh
>> Renaming bear.jpg to 2024-09-24-bear.jpg
>> Renaming man.jpg to 2024-09-24-man.jpg
>> Renaming pig.jpg to 2024-09-24-pig.jpg

Positional Parameters

> ./script.sh parameter1 parameter2 parameter3
$0: "./script.sh"
$1: "parameter1"
$2: "parameter2"
$3: "parameter3"

up $9

eg

#!/bin/bash
 
USER=$1
 
echo "Executing script: $0"
echo "Archiving user: $USER"
 
# Lock the account
passwd -l $USER
 
# Create an archive for ~/
tar cf /archives/${USER}.tar.gz /home/${USER}

$@ accesses all parameters from $1 to the end

eg

#!/bin/bash
 
echo "Executing script: $0"
 
for USER in $@
do 
	echo "Archiving user: $USER"
	passwd -l $USER
	tar cf /archive/${USER}.tar.gz /home/${USER}
done

Accepting Input

Syntax:

read -p "PROMPT" VARIABLE

accepts STDIN - Standard Input

eg

#!/bin/bash
 
read -p "Enter a user name: " USER
 
echo "Archiving user: $USER"
 
# Lock the account
passwd -l $USER
 
# Create an archive for ~/
tar cf /archives/${USER}.tar.gz /home/${USER}

Reading File Contents in Bash

read command

read: read [-Eers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
    Read a line from the standard input and split it into fields.
 
    Reads a single line from the standard input, or from file descriptor FD. if the -u option is supplied.  The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on, with any leftover words assigned to the last NAME.  Only the characters found in $IFS are recognized as word delimiters. By default, the backslash character escapes delimiter characters and newline.
 
    If no NAMEs are supplied, the line read is stored in the REPLY variable.
 
    Options:
      -r	do not allow backslashes to escape any characters

Simple Example:

# Read from the file file.txt and output the tenth line to stdout.
i=0
while read -r line; do
    (( i++ ))
    if [ $i -eq 10 ]; then
        echo $line
    fi
done < "file.txt"

my solution to Leetcode 195