Bash
by Juan Manuel González Garzón
- I. Basic Linux Commands
- II. Redirect IOStreams
- III. Pipes and pipelines
- IV. Signalling Processes
- V. Creating Bash Scripts
- VI. Using Variables, user imput and Globs
- VII. expr & test
- VIII. Conditional Execution
- IX. While Loops in Bash Scripts
- X. For Loops in Bash Scripts
- XI. Advanced Command Interaction
- XII. ~/.bashrc
- XIII. Bash Template
I. Basic Linux Commands
A. Managing files and directories
# ls: lists the contents of the current directory, lists the contents of the received directory
ls [-la] $dirname
# chmod modifiers files: changes the permissions for the files according to the provided modifiers; we've seen +x to make the file executable
chmod
# chown user files: changes the owner of the files to the given user
chown
# chgrp group files: changes the group of the files to the given group
chgrp
# mkdir directory: creates the directory with the received name
mkdir $dirname
# cd directory: changes the current working directory to the specified one
cd $dirname
# pwd: prints the current working directory
pwd
# cp old_name new_name: copies old_name into new_name
cp
# touch file_name: creates an empty file or updates the modified time if it exists
touch
# mv old_name new_name: moves old_name into new_name
mv
# rmdir directory: deletes the directory with the received name (if empty)
rmdir $emptydir
B. Operating with the content of files
# cat file: shows the content of the file through standard output
cat $file
# wc file: counts the amount of characters, words, and lines in the given file; can also count the same values of whatever it receives via stdin
wc $file
# file file: prints the type of the given file, as recognized by the operating system
file $file
# head file: shows the first 10 lines of the given file
head $file
# tail file: shows the last 10 lines of the given file
tail $file
# less file: scrolls through the contents of the given file (press "q" to quit)
less $file
# sort file: sorts the lines of the file alphabetically
sort $file
# cut -dseparator -ffields file: for each line in the given file, splits the line according to the given separator and prints the given fields (starting from 1)
cut [options] [file]
cut -d [delimiter] -f [field number]
grep [pattern] [file-location]
C. Additional Commands
# echo "message": prints the message to standard output
echo "message"
# date: prints the current date
date +'%F'
# who: prints the list of users currently logged into the computer
who
# man command: shows the manual page of the given command; manual pages contain a lot of information explaining how to use each command (press "q" to quit)
man
# uptime: shows how long the computer has been running
uptime
# free: shows the amount of unused memory on the current system
free
# ps: lists the processes executing in the current terminal for the current user
# ps ax: lists all processes currently executing for all users
# ps e: shows the environment for the processes listed
ps [-ax] [e]
# kill PID: sends the SIGTERM signal to the process identified by PID
kill
# fg: causes a job that was stopped or in the background to return to the foreground
fg
# bg: causes a job that was stopped to go to the background
bg
# jobs: lists the jobs currently running or stopped
jobs
# top: shows the processes currently using the most CPU time (press "q" to quit)
top
grep
ping
II. Redirect IOStreams
# command > file: redirects standard output, overwrites file
# command >> file: redirects standard output, appends to file
# command < file: redirects standard input from file
# command 2> file: redirects standard error to file
# Redirection of STDOUT
echo 'lol1' > new_file.txt
cat > [file]
# Append of STDOUT
echo 'lol2' >> new_file.txt
cat >> [file]
# Redirection of STDIN
filter.py < new_file.txt
# Redirect STDERR to a separate files
filter.py < new_file.txt 2> error_file.txt
# Redirect STDERR to STDOUT and redirection of STDOUT
filter.py > /dev/null 2>&1
III. Pipes and pipelines
# command1 | command2: connects the output of command1 to the input of command2
# IO stream redirection using pipes
ls | less
cat sprider.txt | tr ' ' '\n' | sort | unique -c | sort -nr | head
cat kaiku.txt | ./capitalize.py
./capitalize.py < haiku.txt
grep
capitalize.py
#!/usr/bin/env python3
import sys
for line in sys.stdin:
print(line.strip().capitalize())
IV. Signalling Processes
ping
# Ctrl-c SIGINT
# Ctrl-z SIGSTOP
fg # Restart stoped process
kill # SIGTERM
ps ax | grep 'ping'
kill #PID
V. Creating Bash Scripts
gather-information.sh
#!/bin/bash
line="---------------------------------"
echo "Starting at: $(date)"; echo $line
echo "UPTIME"; uptime; echo $line
echo "FREE"; free; echo $line
echo "WHO"; who; echo $line
echo "Finishing at: $(date)"
VI. Using Variables, user imput and Globs
#!/bin/bash
HERE=$PWD
function confirm() {
read -p "Continue? (Y/N): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 0
}
if [ echo '=== 1. EMPAQUETANDO FRONT-END' && confirm ]; then
cd Angular-Frontend
ng build
cd $HERE
fi
if [ echo '=== 2. VACIANDO LA CARPETA ESTATICA DE SPRING-BOOT' && confirm ]; then
rm -rf "$HERE/Spring-Backend/src/main/resources/static/*"
cd $HERE
fi
if [ echo '=== 3. MOVIENDO LA APLICACION DE ANGULAR DENTRO DE LA CARPETA ESTATICA DE SPRING-BOOT' && confirm ]; then
mv $HERE/Angular-Frontend/dist/Angular-Frontend/* "$HERE/Spring-Backend/src/main/resources/static/"
cd $HERE
fi
# Globs
echo *.py
echo ?????.py
VII. expr & test
Los comandos expr y test se utilizan para realizar operaciones y comparaciones respectivamente
expr
Operador | Descripción |
---|---|
+ | Suma |
- | Resta |
\* | Multiplicación |
/ | Division |
% | Modulo |
Operador | Descripción |
---|---|
= | Igualdad |
!= | Diferente |
> | Mayor |
>= | Mayor o igual |
< | Menor |
<= | Menor o igual |
Operador | Descripción |
---|---|
| | Or |
& | And |
#!/bin/bash
read -p 'Introduce un número ' value1
read -p 'Introduce otro número ' value2
answer1=$(expr $value1 \* $value2)
echo "Multiplican: $answer1"
read -p 'Introduce un número ' value1
read -p 'Introduce otro número ' value2
answer2=$(expr $value1 = $value2)
echo "Resultado: $answer2"
rdm=$(expr $RANDOM % 100)
echo $rdm"
test
Opción | Descripción |
---|---|
-f | 0 si el archivo existe y es regular |
-s | 0 si el archivo existe y su tamaño es mayor a 0 |
-r | 0 si el archivo existe y tiene permisos de lectura |
-w | 0 si el archivo existe y tiene permisos de escritura |
-x | 0 si el archivo existe y tiene permisos de ejecución |
-d | 0 si el directorio existe |
Opción | Descripción |
---|---|
-lt | Menor que |
-le | Menor o igual que |
-qt | Mayor que |
-qe | Mayor o igual que |
-qa | Igual a |
-ne | No igual a |
-n | True si el tamaño del string es non-zero. |
Opción | Descripción |
---|---|
-o | Or |
-a | And |
! | Not |
#!/bin/bash
test -f /etc/passwd # 0
test [string1 = string2] # 1
test [string1 != string2] # 0
test 20 -lt 40 # 0
VIII. Conditional Execution
#!/bin/bash
# $? # exit status of command
read -p 'Introduce un número ' value
if (( value < 10 )); then
echo 'número menor a 10'
else
echo 'número mayor o igual a 10'
fi
if [[ grep "127.0.0.1" /etc/hosts ]]; then
echo "OK"
else
echo "Error! 127.0.0.1 is not in /etc/hosts"
fi
if test -n "$PATH"; then echo "Your path is not empty"; fi
# [] = alias of test command
if [ -n "$PATH" ]; then echo "Your path is not empty"; fi
IX. While Loops in Bash Scripts
#!/bin/bash
n=1
while [ $n -le 5 ]; do
echo "Iteration number $n"
((n+=1))
done
retry.sh
#!/bin/bash
n=0
commands=$1
while ! command && [ $n -le 5 ]; do
sleep $n
((n+=1))
echo "Retry #$n"
done
# ./retry.sh ./random-exit.py
random_exit.py
#!/usr/bin/env python3
import system
import random
value=randomint(0, 3)
print("Returning: "+ str(value))
sys.exit(value)
X. For Loops in Bash Scripts
#!/bin/bash
PROCESS_DATES=(
'2016-04-11'
'2016-04-12'
'2016-04-13'
'2016-04-14'
'2016-04-15'
)
for PROCESS_DATE in "${PROCESS_DATES[@]}"; do
echo "$PROCESS_DATE"
done
# Rename al the .HTM files into .html files.
for file in *.HTM; do
name=$(basename "$file" .HTM)
# echo is for precautions
echo mv "$file" "$name.html"
done
for (( n=0 ; n < 5 ; n++ )) ; do echo "n: $n" ; done
XI. Advanced Command Interaction
tail /var/log/syslog | cut -d ' ' -f5-
cut -d ' ' -f5- /var/log/syslog | tr ' ' '\n' | sort | unique -c | sort -nr | head
tail /var/log/syslog | cut -d' ' -f3-
changeJane.py
#!/usr/bin/env python3
import sys
for line in sys.stdin:
print(line.strip().replace('jane', 'jdoe'))
findJane.sh
#!/bin/bash
grep ' jane ' ../data/list.txt | cut -d ' ' -f3 > oldFiles.txt
changeFiles.sh
#!/bin/bash
# No hay gawk en esa maquina
cat oldFiles.txt | gawk -v dest=$(./changeJane.py < $0)'{print "mv "$0" "dest}'
XII. ~/.bashrc
# User specific aliases and functions
# ====================================================================================
# alias unity3d='~/Downloads/Unity3D/UnityHub.AppImage & disown'
# Enable npm -g
export PATH=~/.npm-global/bin:$PATH
# DOTNET_ROOT
export DOTNET_ROOT=/snap/dotnet-sdk/current
export DOTNET_CLI_TELEMETRY_OPTOUT=1
# Install Ruby Gems to ~/gems
export GEM_HOME="$HOME/gems"
export PATH="$HOME/gems/bin:$PATH"
alias postman='/home/usuario/Postman-linux-x64-8.8.0/Postman/Postman & disown'
#COLORS
CDEF=" \033[0m" # default color
CCIN=" \033[0;36m" # info color
CGSC=" \033[0;32m" # success color
CRER=" \033[0;31m" # error color
CWAR=" \033[0;33m" # waring color
b_CDEF=" \033[1;37m" # bold default color
b_CCIN=" \033[1;36m" # bold info color
b_CGSC=" \033[1;32m" # bold success color
b_CRER=" \033[1;31m" # bold error color
b_CWAR=" \033[1;33m" # bold warning color
BINARY_FILE_MATCHES='Coincidencia en el fichero binario'
function lookf() {
grep -rni --exclude-dir={node_modules,.git,dist,testreports,build} "$@" ./ \
grep -v "${BINARY_FILE_MATCHES}" \
sort -u \
grep "$@"
}
function lookfn() {
grep -rli --exclude-dir={node_modules,.git,dist,testreports,build} "$@" ./ \
grep -v "${BINARY_FILE_MATCHES}" \
sort -u
}
function lookn() {
find . \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" \
-not -path "*/dist/*" \
-not -path "*/testreports/*" \
-not -path "*/build/*" \
-type f | grep -i "$@"
}
function lookna() {
find "$(pwd -P)" \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" \
-not -path "*/dist/*" \
-not -path "*/testreports/*" \
-not -path "*/build/*" \
-type f | grep -i "$@"
}
# echo like ... with flag type and display message colors
prompt () {
case ${1} in
"-s"|"--success")
echo -e "${b_CGSC}${@/-s/}${CDEF}";; # print success message
"-e"|"--error")
echo -e "${b_CRER}${@/-e/}${CDEF}";; # print error message
"-w"|"--warning")
echo -e "${b_CWAR}${@/-w/}${CDEF}";; # print warning message
"-i"|"--info")
echo -e "${b_CCIN}${@/-i/}${CDEF}";; # print info message
*)
echo -e "$@"
;;
esac
}
# Check command avalibility
function has_command() {
command -v $1 > /dev/null
}
ACCIO_FOLDER='/home/usuario/accio-folder/'
ACCIO_SCRIPT='/home/usuario/accio-script.sh'
ACCIO_FILE='/home/usuario/accio-file.txt'
function accio() {
if [ -d ${ACCIO_FOLDER} ]; then
rm -rf ${ACCIO_FOLDER}
mkdir ${ACCIO_FOLDER}
else
mkdir ${ACCIO_FOLDER}
fi
> "${ACCIO_FILE}"
lookna $@ | \
tee "${ACCIO_FILE}" | \
gawk -v folder=${ACCIO_FOLDER} '{print "cp --backup=t \x27"$0"\x27 "folder}' | \
tee "${ACCIO_SCRIPT}"
sh "${ACCIO_SCRIPT}"
}
# latest: Lista los archivos en orden de creación o modificación mas reciente de manera global.
function latest() {
find . \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" \
-not -path "*/dist/*" \
-not -path "*/testreports/*" \
-not -path "*/build/*" \
-mtime 0
}
function prop {
grep "^${1}=" ~/.workdir.jmgg | cut -d'=' -f2
}
function workdir() {
if [ ! -z "$1" ]; then
local work_dir=$(prop "${1}")
cd ${work_dir}
else
local work_dir=$(prop 'work_dir')
cd ${work_dir}
fi
}
function workdirs() {
cat ~/.workdir.jmgg
}
function setworkdir() {
if [ ! -z "$1" ]; then
# Erase last entry of the parameter on the file
grep "${1}=" ~/.workdir.jmgg 2>&1 >/dev/null && sed -i "/${1}=/d" ~/.workdir.jmgg
echo "${1}=${PWD}" >> ~/.workdir.jmgg
fi
# Erase again
grep "work_dir=" ~/.workdir.jmgg 2>&1 >/dev/null && sed -i "/work_dir=/d" ~/.workdir.jmgg
echo "work_dir=${PWD}" >> ~/.workdir.jmgg
}
function gadog() {
git log --all --decorate --oneline --graph
}
function gdog() {
git log --graph --oneline --decorate
}
function tree() {
find . \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" \
-not -path "*/dist/*" \
-not -path "*/testreports/*" \
-not -path "*/build/*" \
| sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
}
# ========== PS1 ==========
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
YELLOW="$(tput setaf 3)"
BLUE="$(tput setaf 4)"
PURPLE="$(tput setaf 5)"
CYAN="$(tput setaf 14)"
RESET="$(tput sgr0)"
exitstatus()
{
local exitcode=$?
if [[ $exitcode == 0 ]]; then
echo ${GREEN}
else
printf '\a'
echo ${RED}
fi
echo '('${exitcode}')'
}
thebranch()
{
local branchname="$(__git_ps1)"
if [[ ${branchname} != '' ]]; then printf "\n${CYAN}Branch:${branchname}${RESET}"; fi
}
PS1='$(exitstatus)'" [\A] \u${RESET}:${BLUE}\w${RESET} ["'$(ls -1 | wc -l | sed '"'s: ::g'"') elements, $(ls -lah | grep -m 1 total | sed '"'s/total //'"'), Jobs:\j, Users:$(who | wc -l)]$(thebranch)\n$'
# Busca ejecutables sospechosos:
# find /sbin /usr/sbin -executable \! -readable -print
# Search for files which are executable but not readable.
# zip old log files
# find $LOG_FOLDER -not -name "*.gz" -and -name "*.log" -mtime +90 -exec gzip {}
XIII. Bash Template
#!/bin/bash
USAGE="Usage: $0 [-a -u USER -H HOST ] [-b <parameter>] [-h] \n
-a Execute action a \n
-u user \n
-H host ip \n
-b Excecute action b, expect <parameter> \n
-h Show this help \n
"
# ============================== CONFIGURABLE ===========================
MAIN_ENV=true
# ============================== ATTRIBUTES =============================
EXECUTE_ACTION_A=false
VALUE_PARAMETER_B=
USER=
HOST=
# ============================== FUNCTIONS ==============================
function processInvocation () {
# Check if there are options present
while getopts "ab:u:H:h" opt; do
case $opt in
a) EXECUTE_ACTION_A=true;;
b) VALUE_PARAMETER_B=${OPTARG};;
u) USER=${OPTARG};;
H) HOST=${OPTARG};;
h) becho -e $USAGE
exit 0
;;
*) recho -e $USAGE 1>&2
exit 1
;;
esac
done
if [ $EXECUTE_ACTION_A = false ] || [ -z $USER ] || [ -z $HOST ] && [ -z $VALUE_PARAMETER_B ]; then
recho -e $USAGE 1>&2
exit 1
fi
# Example of parameters manipulation in a bash script, in function or global
# becho '$@ Print all the parameters: '"$@"
# becho '$0 Print the script name: :'"$0"
# becho '$1 Print the first parameter: '"$1"
# becho '$2 Print the second parameter: '"$2"
# becho '$# Print the number of parameterS: '"$#"
}
function recho () {
tput setaf 1;echo $@;tput sgr0
}
function gecho () {
tput setaf 2;echo $@;tput sgr0
}
function becho () {
tput setaf 6;echo $@;tput sgr0
}
function yecho () {
tput setaf 3;echo $@;tput sgr0
}
function actionA () {
yecho "Action A | Parameters: $@"
}
function debugInstruction () {
set -x
eval "$@"
set +x
}
function main () {
[ "$MAIN_ENV" = true ] && becho '========================================'
if [ $EXECUTE_ACTION_A = true ]; then
gecho 'Executing action A'
gecho "USER=$USER"
gecho "HOST=$HOST"
debugInstruction actionA $USER $HOST
fi
if [ ! -z $VALUE_PARAMETER_B ]; then
gecho "Executing action B with parameter: $VALUE_PARAMETER_B"
fi
[ "$MAIN_ENV" = true ] && becho '========================================'
}
# ============================== SCRIPT ==============================
processInvocation $@
main
# ./bash-template.sh -a -u Juan -H localhost
#!/bin/bash
# cProfile count how many times functions are called, and how long they run.
cProfile
# Copy files and directories recursively locally:
rsync -zrvh [Source-Files-Dir] [Destination]
List contents of directories in a tree-like format.
List with max display depth of the directory tree.
tree -L 2
List ignoring some folders
tree -I 'folder1|folder2'
List showing long name
tree -f
Search one word in a folder of documents
#!/bin/bash
TEXT_TO_SEARCH='TODO'
FOLDERS=(
/PROJECTS/JAVA/LIB/ProyectName/
/PROJECTS/JAVA/CLI/ProyectName/
/PROJECTS/JAVA/WEB/ProyectName/
)
grep --include=\*.{java,sql,xml} -rnw -e "${TEXT_TO_SEARCH}" --exclude-dir={build,dist,.svn,node_modules,.git} "${FOLDERS[@]}"
List the heaviest 30 files in folder:
find . -type f -print0 | xargs -0 du | sort -n | tail -30 | cut -f2 | xargs -I{} du -sh {}