===== 1. Shell Something Out =====
==== Printing in the terminal ====
* We cannot echo exclamation mark (!) in double quotes. But these will work:echo Hello world !
echo 'Hello world !'
echo "Hello world \!"
* We can use **printf** instead of **echo**
* No new line by default with printf, so we have to add it when needed:printf "%-5s %-10s %-4s\n" No Name Mark
printf "%-5s %-10s %-4.2f\n" 1 Sarath 80.3456
printf "%-5s %-10s %-4.2f\n" 2 James 90.9989
printf "%-5s %-10s %-4.2f\n" 3 Jeff 77.564
* In '%-5s' above, '-' is used to specify the left alignment, otherwise the text is right aligned.
* echo can be used with escape sequences when the flag -e is provided (with double-quoted strings):echo -e "1\t2\t3"
* We can change the output color with escaped codes: for instance: reset = 0, black = 30, red = 31, green = 32, yellow = 33, blue = 34, magenta = 35, cyan = 36, and white = 37:echo -e "\e[1;31m This is red text \e[0m"
* For colored background use: reset = 0, black = 40, red = 41, green = 42, yellow = 43, blue = 44, magenta = 45, cyan = 46, and white=47:echo -e "\e[1;42m Green Background \e[0m"
==== Playing with variables and environment variables ====
* Get process ID with pgrep, for instance:pgrep gedit
* List the environment variables of a process with:cat /proc/$PID/environ
Previous commands doesn't seem to work in cygwin.
* We can use **tr** to substitute characters:echo "Coucou manu." | tr 'o' '8'
* To find length of a string:length=${#var}
* To Identifying the current shell:echo $SHELL
* To check for super user:If [ $UID -ne 0 ]; then
echo Non root user. Please run as root.
else
echo Root user
fi
* To modify the Bash prompt string:PS1="PROMPT>"
* For the PS1 var there are also certain special characters that expand to system parameters. For example, \u expands to username, \h expands to hostname, and \w expands to the current working directory.
==== Function to prepend to environment variables ====
* We can add in .bashrc the method:prepend() { [ -d "$2" ] && eval $1=\"$2':'\$$1\" && export $1; }
* Or, to fix the issue of empty variable when predending:prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; }
* Note: **${parameter:+expression}** will expand to **expression** if **parameter** is set and not null.
* And then call:prepend PATH /opt/myapp/bin
prepend LD_LIBRARY_PATH /opt/myapp/lib
==== Math with the shell ====
* We mainly use **let**, **( ( ) )**, **[]**, **expr** and **bc**.
* Using **let** for basic operations:no1=4;
no2=5;
let result=no1+no2
echo $result
# incrementation:
let no1++
# decrementation:
let no1--
# shorthands:
let no+=6
let no-=6
* Using [] operator as alternate method:result=$[ no1 + no2 ]
# using the $ prefix inside [] is legal:
result=$[ $no1 + 5 ]
* Using ( () ) or expr:result=$(( no1 + 50 ))
result=`expr 3 + 4`
result=$(expr $no1 + 5)
* For floating point operations we can use bc:echo "4 * 0.56" | bc
no=54;
result=`echo "$no * 1.5" | bc`
echo $result
# passing additional parameters to bs with prefixes to the operation
# and semicolon delimiters:
# Decimal places scale with bc:
# Note that the default scale is 0 and bc is thus outputting integers.
echo "scale=2;3/8" | bc
# Base conversion with bc:
no=100
echo "obase=2;$no" | bc
no=1100100
echo "obase=10;ibase=2;$no" | bc
# Calculating square roots and pows:
echo "scale=3;sqrt(10)" | bc
echo "10^10" | bc
==== Playing with file descriptors and redirection ====
* Reserved file descriptors are:
* 0: stdin
* 1: stdout
* 2: stderr
* Saving to a file is done with:echo "This is a sample text 1" > temp.txt
* To append to a file we use:echo "This is a sample text 1" >> temp.txt
* To view file content:cat temp.txt
* To redirect stderr exclusively to a file and stdout to another file:cmd 2>stderr.txt 1>stdout.txt
* To redirect stderr and stdout to a single file by converting stderr to stdout:cmd 2>&1 >output.txt
* We can also redirect to /dev/null to discard an output stream:cmd 2>/dev/null
* We can use the tee command to make a copy of the output stream instead of redirecting:# Prototype: command | tee FILE1 FILE2
cat a* | tee out.txt | cat -n
# To append to a file we use the flag -a:
cat a* | tee -a out.txt | cat -n
* We can use stdin as a command argument. It can be done by using - as the filename argument for the command:cmd1 | cmd2 | cmd -
* Redirection from a file to a command:cmd < file
* Redirection froma text block within script:cat<log.txt
LOG FILE HEADER
This is a test log file
Function: System statistics
EOF
* To create custom file descriptors:exec 3 input.txt
exec 3output.txt
echo newline >&4
cat output.txt
# For write descriptor with append mode:
exec 5>>output.txt
echo newline >&5
cat output.txt
==== Arrays and associative arrays ====
* For associative arrays we need bash version 4 or higher.
* Array definitions:# Values are stored in consecutive locations starting
# from index 0:
array_var=(1 2 3 4 5 6)
# Definition with set of index-value pairs:
array_var[0]="test1"
array_var[1]="test2"
array_var[2]="test3"
array_var[3]="test4"
* To print array content:# Print content at given index:
echo ${array_var[0]}
index=5
echo ${array_var[$index]}
# Print all the values:
echo ${array_var[*]}
# or:
echo ${array_var[@]}
* To get array legnth:echo ${#array_var[*]}
* Using associative arrays:declare -A ass_array
# Then we add elements with:
ass_array=([index1]=val1 [index2]=val2)
# Or:
ass_array[index1]=val1
ass_array'index2]=val2
# For instance:
declare -A fruits_value
fruits_value=([apple]='100dollars' [orange]='150 dollars')
echo "Apple costs ${fruits_value[apple]}"
# To list array indexes:
echo ${!array_var[*]}
# Or:
echo ${!array_var[@]}
==== Visiting aliases ====
* Create an alias with:alias new_command='command sequence'
# For instance:
alias install='sudo apt-get install'
* To keep permanent aliases, we have to add them in .bashrc:echo 'alias cmd="command seq"' >> ~/.bashrc
* To Remove alias:unalias example
# or:
alias example=
* We can escape aliases with exacuting a command with:\command
==== Grabbing information about the terminal ====
* Using **tput** and **stty**:# Get num columns and rows:
tput cols
tput lines
# get current terminal name:
tput longname # not working on cygwin
# Move cursor to position 10,10:
tput cup 10 10
# set background color n=0 to 7:
tput setb n
# set foreground color n=0 to 7:
tput setf n
# show bold text:
tput bold
# start and end underlining:
tput smul
tput rmul
# delete from cursor to end of line:
tput ed
* To hide a password we use stty:echo -e "Enter password: "
stty -echo
read password
stty echo
echo
echo Password read.
==== Getting and setting dates and delays ====
* To Read date:date
# to print the epoch (num secs since 1970-01-01 00:00:00 UTC):
date +%s
# find out the epoch of a given date:
date --date "Thu Nov 18 08:07:21 IST 2010" +%s
# Retrieve the week day of a date:
date --date "Jan 20 2001" +%A
# Check time taken by a command:
start=$(date +%s)
commands;
statements;
end=$(date +%s)
difference=$(( end - start))
echo Time taken to execute commands is $difference seconds.
* We can delay execution in script with the **sleep** command:echo -n Count:
tput sc # store cursor position
count=0;
while true;
do
if [ $count -lt 40 ];
then
let count++;
sleep 1; # sleep a given number of seconds
tput rc # restore cursor position.
tput ed # clear until end of line.
echo -n $count;
else exit 0;
fi
done
==== Debugging the script ====
* Debug a complete script file:bash -x scripts.sh
* Debug only a portion of the script:for i in {1..6};
do
set -x
echo $i
set +x
done
echo "Script executed"
* We can provide our own formatting for instance with a _DEBUG environment variable:function DEBUG()
{
[ "$_DEBUG" == "on" ] && $@ || : # in bash ":" tells the script to do nothing.
}
for i in {1..10}
do
DEBUG echo $i
done
# Then we would run:
_DEBUG=on ./script.sh
==== Functions and arguments ====
* Functions can be defined with:function fname()
{
statements;
}
# Or alternately:
fname()
{
statements;
}
* In a function "$@" expands as "$1" "$2" "$3" and so on
* In a function "$*" expands as "$1c$2c$3", where c is the first character of IFS.
* Functions in bash support recursion:F()
{
echo $1; F hello; sleep 1;
}
* Functions can be exported:export -f fname
* Reading the return value (status) of a command:cmd;
echo $?;
# For instance:
CMD="command" #Substitute with command for which you need to test the
exit status
$CMD
if [ $? -eq 0 ];
then
echo "$CMD executed successfully"
else
echo "$CMD terminated unsuccessfully"
fi
==== Reading the output of a sequence of commands in a variable ====
* We can read the output of a sequence with a **subshell method**:cmd_output=$(COMMANDS)
# For instance:
cmd_output=$(ls | cat -n)
echo $cmd_output
* Or with a **back quotes**:cmd_output=`COMMANDS`
# For instance:
cmd_output=`ls | cat -n`
echo $cmd_output
* We can spwan a separate process with ( ):pwd; # current working directory
(cd /bin; ls);
pwd; # Still the same current working directory
* Subshell should be quoted to preserve the newline character:out="$(cat tex.txt)"
echo $out
==== Reading n characters without pressing the return key ====
* Read n chars:read -n number_of_chars variable_name
# For instance:
read -n 2 var
echo $var
# Read a password:
read -s var
# Display a message before reading:
read -p "Enter input:" var
# Read after timeout:
read -t timeout var # timeout is a number of seconds.
# Read until delimiter:
read -d delim_char var
# For instance:
read -d ":" var
==== Running a command until it succeeds ====
* We can define the repeat method as:repeat()
{
while true
do
$@ && return
done
}
* We can optimize by adding a delay and avoiding the spawn of the "true" binary with:repeat() { while :; do $@ && return; sleep 30; done }
==== Field separators and iterators ====
* Update IFS to read data:data="name,sex,rollno,location"
# To read each of the item in a variable, we can use IFS.
oldIFS=$IFS
IFS=, now,
for item in $data;
do
echo Item: $item
done
IFS=$oldIFS
* Receive some indexed values on a line with IFS:line="root:x:0:0:root:/root:/bin/bash"
oldIFS=$IFS;
IFS=":"
count=0
for item in $line;
do
[ $count -eq 0 ] && user=$item;
[ $count -eq 6 ] && shell=$item;
let count++
done;
IFS=$oldIFS
echo $user\'s shell is $shell;
* We can generate sequences with:echo {1..50}
echo {a..z}
echo {A..Z}
echo {a..h}
# for instance:
for i in {a..z};
do
echo $i;
done;
* For loop can also take the format of for in C:for((i=0;i<10;i++))
{
commands; # Use $i
}
* While loop format:while condition
do
commands;
done
* until format:x=0;
until [ $x -eq 9 ]; # [ $x -eq 9 ] is the condition
do
let x++; echo $x;
done
==== Comparisons and tests ====
* IF syntax:if condition;
then
commands;
fi
* else if syntax:if condition;
then
commands;
else if condition; then
commands;
else
commands;
fi
* To make if conditions shorter we can use logical operators:[ condition ] && action; # action executes if the condition is true
[ condition ] || action; # action executes if the condition is false
* Performing mathematical conditions is done with:[ $var -eq 0 ] # It returns true when $var equal to 0.
[ $var -ne 0 ] # It returns true when $var is not equal to 0
* Available mathematical operators for conditions are:
* -gt : greater than
* -lt : less than
* -ge : greater or equal
* -le : less than or equal
* Combining multiple test conditions is done with:[ $var1 -ne 0 -a $var2 -gt 2 ] # using AND -a
[ $var1 -ne 0 -o var2 -gt 2 ] # OR -o
* Filesystem related tests:[ -f $var ] # This returns true if the given variable holds a regular file path or filename
[ -x $var ] # This returns true if the given variable holds a file path or filename that is executable
[ -d $var ] # This returns true if the given variable holds a directory path or directory name
[ -e $var ] # This returns true if the given variable holds an existing file
[ -c $var ] # This returns true if the given variable holds the path of a character device file
[ -b $var ] # This returns true if the given variable holds the path of a block device file
[ -w $var ] # This returns true if the given variable holds the path of a file that is writable
[ -r $var ] # This returns true if the given variable holds the path of a file that is readable
[ -L $var ] # This returns true if the given variable holds the path of a symlink
* String comparaisons:[[ $str1 = $str2 ]] # This returns true when str1 equals str2
# that is, the text contents of str1 and str2 are the same.
[[ $str1 == $str2 ]] # It is an alternative method for string equality check
# We can check whether two strings are not the same as follows:
[[ $str1 != $str2 ]] # This returns true when str1 and str2 mismatch
# We can find out the alphabetically smaller or larger string as follows:
[[ $str1 > $str2 ]] # This returns true when str1 is alphabetically greater than str2
[[ $str1 < $str2 ]] # This returns true when str1 is alphabetically lesser than str2
[[ -z $str1 ]] # This returns true if str1 holds an empty string
[[ -n $str1 ]] # This returns true if str1 holds a nonempty string
* It is sometimes easier to combine multiple conditions using logical operators:if [[ -n $str1 ]] && [[ -z $str2 ]] ;
then
commands;
fi
* We can use the test command isntead of the [] operator:if [ $var -eq 0 ]; then echo "True"; fi
# can be written as:
if test $var -eq 0 ; then echo "True"; fi