KEMBAR78
Linux Command-line Tips and Tricks - ATO 2025 | PDF
Linux command-line tips & tricks
Vladimir Levijev (aka dimir)
October 13, 2025
1
Outline
Before we start
Introduction
What is Linux?
Some history
What is a Shell?
Basics about Shell
Shell environment
Bash commands
What is a script?
One-liners
The Bash script
Some more tricks?
Some tricks
2
Before we start
About myself
My name is Vladimir Levijev (aka dimir) I’ve been in IT for 24 years.
3
About myself
My name is Vladimir Levijev (aka dimir) I’ve been in IT for 24 years.
• Born in Tallinn, Estonia.
3
About myself
My name is Vladimir Levijev (aka dimir) I’ve been in IT for 24 years.
• Born in Tallinn, Estonia.
• Joined IT in 2001 (24 years), working only on GNU/Linux since.
3
About myself
My name is Vladimir Levijev (aka dimir) I’ve been in IT for 24 years.
• Born in Tallinn, Estonia.
• Joined IT in 2001 (24 years), working only on GNU/Linux since.
• Working as a C developer since 2003.
3
About myself
My name is Vladimir Levijev (aka dimir) I’ve been in IT for 24 years.
• Born in Tallinn, Estonia.
• Joined IT in 2001 (24 years), working only on GNU/Linux since.
• Working as a C developer since 2003.
• At the age of 29 moved to Latvia.
3
About myself
My name is Vladimir Levijev (aka dimir) I’ve been in IT for 24 years.
• Born in Tallinn, Estonia.
• Joined IT in 2001 (24 years), working only on GNU/Linux since.
• Working as a C developer since 2003.
• At the age of 29 moved to Latvia.
• Working at Zabbix as a C developer for the last 14 years.
3
Location
Straight-line distance: 7,500 km (4,600 mi)
4
Land sizes
5
Population
6
Land area per person
7
Some facts
Estonia:
• Estonia has produced global tech companies like Skype, TransferWise
(Wise), and Bolt.
• Alfred Neuland won a gold medal (1920 Antwerp Olympics, Belgium)
in Men’s lightweight (67.5 kg) weightifting.
• First nation to offer Internet Voting (2005).
8
Some facts
Estonia:
• Estonia has produced global tech companies like Skype, TransferWise
(Wise), and Bolt.
• Alfred Neuland won a gold medal (1920 Antwerp Olympics, Belgium)
in Men’s lightweight (67.5 kg) weightifting.
• First nation to offer Internet Voting (2005).
Latvia:
• First Olympic Gold in Men’s BMX in 2008 in Beijing, Māris
Štrombergs.
• First Olympic Gold in 3x3 Basketball in 2024, Paris.
• First Latvian Oscar Winner (2025) for the animated film Flow
(Latvian: Straume).
8
About Zabbix
• 100% Open Source monitoring solution.
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
• Features:
• Agent-based and Agentless monitoring
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
• Features:
• Agent-based and Agentless monitoring
• Distributed
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
• Features:
• Agent-based and Agentless monitoring
• Distributed
• Automation (auto-discovery)
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
• Features:
• Agent-based and Agentless monitoring
• Distributed
• Automation (auto-discovery)
• Visualization
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
• Features:
• Agent-based and Agentless monitoring
• Distributed
• Automation (auto-discovery)
• Visualization
• Highly customizable
9
About Zabbix
• 100% Open Source monitoring solution.
• The initial version 0.1 was released in 2001 under GPLv2.
• The company with the same name was founded in 2005.
• Has grown to 150 employees and 5 locations since including Latvia, Japan,
Brazil, Mexico, France.
• GPLv2 -> AGPLv3 in 2024.
• Features:
• Agent-based and Agentless monitoring
• Distributed
• Automation (auto-discovery)
• Visualization
• Highly customizable
• Global Footprint: Used by organizations in more than 160 countries,
serving both small businesses and global enterprises.
9
Zabbix company services
10
Zabbix company services
• Technical support
10
Zabbix company services
• Technical support
• Consulting
10
Zabbix company services
• Technical support
• Consulting
• Training and certification
10
Zabbix company services
• Technical support
• Consulting
• Training and certification
• Turnkey solutions and implementation
10
Zabbix company services
• Technical support
• Consulting
• Training and certification
• Turnkey solutions and implementation
• Integration and custom development
10
Zabbix company services
• Technical support
• Consulting
• Training and certification
• Turnkey solutions and implementation
• Integration and custom development
• Zabbix Cloud
10
What is Linux?
Linux documentaries
Recommended documentary movies about Linux:
The Code (2001)
Revolution OS (2001)
11
What is a Shell?
Without operating system
• Accessing the SATA controller
• Communicate with the controller via MMIO or I/O ports (usually protected
by the OS).
• Obtain access to hardware registers that control the SATA interface.
• Sending commands
• Construct and send ATA/ATAPI commands such as READ SECTOR(S).
• Place command parameters into the controller’s command registers.
• Handling DMA or PIO transfers
• Use PIO to read/write data through CPU-accessible registers, or
• Set up DMA so the controller moves data directly into memory buffers.
• Waiting for status
• Poll or check status registers to determine readiness, completion, or errors.
• Handle and interpret controller/drive error bits if they appear.
• Reading the data
• Once transfer completes, access the memory buffer (or controller data
registers) to obtain raw sector bytes.
• Verify data integrity (e.g., via status or checksums if available).
• No filesystem support
• This method reads raw sectors — the program must implement filesystem
parsing (FAT, NTFS, ext, etc.) to locate a file’s sectors.
• There is no fopen/OS-level abstraction in this approach.
12
What is a Shell?
Operating systems provide various services to their users, including file
management, process management (running and terminating applications),
batch processing, and operating system monitoring and configuration.
The term shell in computing refers to an interface that allows a user to interact
with an operating system. There are two main types: command-line shells and
graphical shells.
13
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
14
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
• It was simple, mainly for running commands and basic scripting.
14
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
• It was simple, mainly for running commands and basic scripting.
• In 1977 at AT&T Bell Labs Stephen Bourne developed much more
powerful Bourne shell (also sh).
14
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
• It was simple, mainly for running commands and basic scripting.
• In 1977 at AT&T Bell Labs Stephen Bourne developed much more
powerful Bourne shell (also sh).
• 1978 - C Shell (csh), C-like syntax
14
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
• It was simple, mainly for running commands and basic scripting.
• In 1977 at AT&T Bell Labs Stephen Bourne developed much more
powerful Bourne shell (also sh).
• 1978 - C Shell (csh), C-like syntax
• 1980s - Korn Shell (ksh), TENEX C Shell (tcsh), Almquist Shell (ash)
• 1980s - Z Shell (zsh), combining features from sh, ksh, and csh
14
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
• It was simple, mainly for running commands and basic scripting.
• In 1977 at AT&T Bell Labs Stephen Bourne developed much more
powerful Bourne shell (also sh).
• 1978 - C Shell (csh), C-like syntax
• 1980s - Korn Shell (ksh), TENEX C Shell (tcsh), Almquist Shell (ash)
• 1980s - Z Shell (zsh), combining features from sh, ksh, and csh
• 1989 - Brian Fox developed Bourne Again Shell (bash), combines
features of sh and csh.
14
Fist shells
• The first widely used UNIX shell was the Thompson shell (sh), created by
Ken Thompson around 1971 at Bell Labs.
• It was simple, mainly for running commands and basic scripting.
• In 1977 at AT&T Bell Labs Stephen Bourne developed much more
powerful Bourne shell (also sh).
• 1978 - C Shell (csh), C-like syntax
• 1980s - Korn Shell (ksh), TENEX C Shell (tcsh), Almquist Shell (ash)
• 1980s - Z Shell (zsh), combining features from sh, ksh, and csh
• 1989 - Brian Fox developed Bourne Again Shell (bash), combines
features of sh and csh.
• Modern shells (bash, ksh, dash etc.) still maintain backwards compatibility
with sh.
14
Graphical shells
Graphical shells provide means for manipulating programs based on graphical
user interface (GUI).
Graphical shells are typically build on top of a windowing system. In case of
Linux it is usually X Window System and the shell consists of an X window
manager.
Some examples:
• KDE
• GNOME
• Xfce
15
Bash configuration files
Global:
• RedHat: /etc/bashrc
• Debian: /etc/bash.bashrc
Personal:
• ~/.bashrc
16
Controlling command history
$ ls
$ ls
$ history | tail -3
498 ls
499 ls
500 history | tail -3
17
Controlling command history
$ ls
$ ls
$ history | tail -3
498 ls
499 ls
500 history | tail -3
HISTCONTROL=ignoredups # ignore consecutive duplicates
HISTCONTROL=ignorespace # ignore commands that start with spaces
HISTCONTROL=ignoreboth # ignore both above mentioned
17
Controlling command history
$ ls
$ ls
$ history | tail -3
498 ls
499 ls
500 history | tail -3
HISTCONTROL=ignoredups # ignore consecutive duplicates
HISTCONTROL=ignorespace # ignore commands that start with spaces
HISTCONTROL=ignoreboth # ignore both above mentioned
With HISTCONTROL=ignoredups:
$ ls
$ ls
$ history | tail -3
498 cd
499 ls
500 history | tail -3
17
Controlling command history
With HISTCONTROL=ignorespace
$ ls
$ pwd
$ history | tail -3
498 cd
499 ls
500 history | tail -3
18
Controlling command history
With HISTCONTROL=ignorespace
$ ls
$ pwd
$ history | tail -3
498 cd
499 ls
500 history | tail -3
$ grep HIST ~/.bashrc
HISTCONTROL=ignoredups
18
Controlling command history
With HISTCONTROL=ignorespace
$ ls
$ pwd
$ history | tail -3
498 cd
499 ls
500 history | tail -3
$ grep HIST ~/.bashrc
HISTCONTROL=ignoredups
Some other options:
HISTSIZE=500 # default
HISTFILE=~/.bash_history # default
HISTFILESIZE=500 # by default, the value of HISTSIZE
18
Bash aliases
$ alias
alias grep='grep --color=always'
alias less='less -R'
19
Bash aliases
$ alias
alias grep='grep --color=always'
alias less='less -R'
$ git diff --color linux-cmd.tex | less
ESC[1;33m--- a/linux-cmd.texESC[m
ESC[1;33m+++ b/linux-cmd.texESC[m
ESC[1;35m@@ -1,153 +1,321 @@ESC[m
ESC[1;31m-documentclass{beamer}ESC[m
19
Bash aliases
$ alias
alias grep='grep --color=always'
alias less='less -R'
$ git diff --color linux-cmd.tex | less
ESC[1;33m--- a/linux-cmd.texESC[m
ESC[1;33m+++ b/linux-cmd.texESC[m
ESC[1;35m@@ -1,153 +1,321 @@ESC[m
ESC[1;31m-documentclass{beamer}ESC[m
$ git diff --color linux-cmd.tex | less -R
--- a/linux-cmd.tex
+++ b/linux-cmd.tex
@@ -1,153 +1,321 @@
-documentclass{beamer}
19
Me listing files
$ alias l='ls -lA --color'
-l use a long listing format
-A do not list implied . and ..
20
Me listing files
$ alias l='ls -lA --color'
-l use a long listing format
-A do not list implied . and ..
$ l -tr
-t sort by modification time, newest first
-r reverse order while sorting
20
Me listing files
$ alias l='ls -lA --color'
-l use a long listing format
-A do not list implied . and ..
$ l -tr
-t sort by modification time, newest first
-r reverse order while sorting
$ l -tr ~/Downloads
[...]
linux-cmd.pdf
$
20
Me listing files
$ alias l='ls -lA --color'
-l use a long listing format
-A do not list implied . and ..
$ l -tr
-t sort by modification time, newest first
-r reverse order while sorting
$ l -tr ~/Downloads
[...]
linux-cmd.pdf
$
$ alias cl="find . -name '.#*' -o -name '*~' -o -name '#*#'

,
→
-o -name '*.rej' -o -name '*.orig' | xargs rm -fv"
$ cl
removed './.#linux-cmd.tex.bak'
removed './#linux-cmd.tex.bak#'
20
Searching through history
$ history | tail -3
592 l -tr ~/Downloads
593 cd -
594 locate -i --regex 'zbxnext.*.patch'
21
Searching through history
$ history | tail -3
592 l -tr ~/Downloads
593 cd -
594 locate -i --regex 'zbxnext.*.patch'
For searching a command you typed some time ago hit Ctrl+r and type part of
it that you remember:
Ctrl+r <PATTERN>
21
Searching through history
$ history | tail -3
592 l -tr ~/Downloads
593 cd -
594 locate -i --regex 'zbxnext.*.patch'
For searching a command you typed some time ago hit Ctrl+r and type part of
it that you remember:
Ctrl+r <PATTERN>
More Ctrl+r to go to next match
21
Searching through history
$ history | tail -3
592 l -tr ~/Downloads
593 cd -
594 locate -i --regex 'zbxnext.*.patch'
For searching a command you typed some time ago hit Ctrl+r and type part of
it that you remember:
Ctrl+r <PATTERN>
More Ctrl+r to go to next match
E. g.: searching for command that contained "loca":
(reverse-i-search)`loca': locate -i --regex 'zbxnext.*.patch'
21
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
2. Tells the terminal to pause sending data to the screen
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
2. Tells the terminal to pause sending data to the screen
3. Useful if a program is spamming output too quickly (when you don’t want
to Ctrl+C)
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
2. Tells the terminal to pause sending data to the screen
3. Useful if a program is spamming output too quickly (when you don’t want
to Ctrl+C)
4. Ctrl+Q (XON) → "resume output"
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
2. Tells the terminal to pause sending data to the screen
3. Useful if a program is spamming output too quickly (when you don’t want
to Ctrl+C)
4. Ctrl+Q (XON) → "resume output"
5. Tells the terminal to continue sending data after a pause
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
2. Tells the terminal to pause sending data to the screen
3. Useful if a program is spamming output too quickly (when you don’t want
to Ctrl+C)
4. Ctrl+Q (XON) → "resume output"
5. Tells the terminal to continue sending data after a pause
• If you accidentally hit Ctrl+S, the terminal looks "frozen" but the program
is still running. Pressing Ctrl+Q unfreezes it.
22
XON/XOFF flow control
In Unix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF
flow control.
1. Ctrl+S (XOFF) → "stop output"
2. Tells the terminal to pause sending data to the screen
3. Useful if a program is spamming output too quickly (when you don’t want
to Ctrl+C)
4. Ctrl+Q (XON) → "resume output"
5. Tells the terminal to continue sending data after a pause
• If you accidentally hit Ctrl+S, the terminal looks "frozen" but the program
is still running. Pressing Ctrl+Q unfreezes it.
• You can disable this behavior with:
$ stty -ixon
then Ctrl+S and Ctrl+Q won’t pause/resume output anymore
22
Clearing the terminal screen
Clearing the terminal in Linux can be done in several ways, depending on what
you want to achieve.
23
Clearing the terminal screen
Clearing the terminal in Linux can be done in several ways, depending on what
you want to achieve.
• Purpose: clears the visible terminal screen
• How it works: moves all previous output off-screen but doesn’t remove it
from the terminal’s scrollback buffer so you can still scroll up to see it
• Usage:
$ clear
• Shortcult: Ctrl+L
23
Clearing the terminal screen
Clearing the terminal in Linux can be done in several ways, depending on what
you want to achieve.
• Purpose: clears the visible terminal screen
• How it works: moves all previous output off-screen but doesn’t remove it
from the terminal’s scrollback buffer so you can still scroll up to see it
• Usage:
$ clear
• Shortcult: Ctrl+L
Sometimes you get garbled text on the screen and clearing doesn’t help. In such
case you need to fully reset the terminal.
23
Clearing the terminal screen
Clearing the terminal in Linux can be done in several ways, depending on what
you want to achieve.
• Purpose: clears the visible terminal screen
• How it works: moves all previous output off-screen but doesn’t remove it
from the terminal’s scrollback buffer so you can still scroll up to see it
• Usage:
$ clear
• Shortcult: Ctrl+L
Sometimes you get garbled text on the screen and clearing doesn’t help. In such
case you need to fully reset the terminal.
• Purpose: reset the terminal session
• How it works: resets the terminal modes, colors and settings, clears
garbled text
• Usage:
$ cat /dev/random | head
[...]
• �*��+��,� reset
23
Who am I?
$ whoami
vl
24
Who am I?
$ whoami
vl
$ echo $USER
vl
24
Who am I?
$ whoami
vl
$ echo $USER
vl
$ id
uid=1000(vl) gid=1000(vl) groups=1000(vl),4(adm),999(docker)
24
Who am I?
$ whoami
vl
$ echo $USER
vl
$ id
uid=1000(vl) gid=1000(vl) groups=1000(vl),4(adm),999(docker)
$ id -u
1000
24
Who am I?
$ whoami
vl
$ echo $USER
vl
$ id
uid=1000(vl) gid=1000(vl) groups=1000(vl),4(adm),999(docker)
$ id -u
1000
$ lastlog -u vl
Username Port From Latest
vl pts/4 192.168.3.68 Tue Jan 16 09:40:05
24
What is my Shell?
$ echo $SHELL
/bin/bash
25
What is my Shell?
$ echo $SHELL
/bin/bash
$ grep vl /etc/passwd
vl:x:1000:1000:Vladimir Levijev,,,:/home/vl:/bin/bash
25
What is my Shell?
$ echo $SHELL
/bin/bash
$ grep vl /etc/passwd
vl:x:1000:1000:Vladimir Levijev,,,:/home/vl:/bin/bash
$ chsh
Password:
Changing the login shell for vl
Enter the new value, or press ENTER for the default
Login Shell [/bin/bash]:
25
Where am I?
$ pwd
/tmp
26
Where am I?
$ pwd
/tmp
$ echo $PWD
/tmp
26
Where am I?
$ pwd
/tmp
$ echo $PWD
/tmp
$ export PWD=/tmp/non-existent
26
Where am I?
$ pwd
/tmp
$ echo $PWD
/tmp
$ export PWD=/tmp/non-existent
$ pwd
/home/vl/git/linux-cmd
$ echo $PWD
/tmp/non-existent
26
Where am I?
$ pwd
/tmp
$ echo $PWD
/tmp
$ export PWD=/tmp/non-existent
$ pwd
/home/vl/git/linux-cmd
$ echo $PWD
/tmp/non-existent
$ export PWD=$(pwd)
26
Where am I?
$ pwd
/tmp
$ echo $PWD
/tmp
$ export PWD=/tmp/non-existent
$ pwd
/home/vl/git/linux-cmd
$ echo $PWD
/tmp/non-existent
$ export PWD=$(pwd)
$ export PWD=`pwd`
26
Where am I?
Debian:
$ cat /etc/timezone
Europe/Riga
RedHat:
$ readlink /etc/localtime
../usr/share/zoneinfo/Europe/Riga
27
Where am I?
Debian:
$ cat /etc/timezone
Europe/Riga
RedHat:
$ readlink /etc/localtime
../usr/share/zoneinfo/Europe/Riga
$ timedatectl
Local time: Sun 2025-10-12 20:43:49 EEST
Universal time: Sun 2025-10-12 17:43:49 UTC
RTC time: Sun 2025-10-12 17:43:49
Time zone: Europe/Riga (EEST, +0300)
System clock synchronized: yes
27
Where I just was?
$ echo $OLDPWD
/etc
28
Where I just was?
$ echo $OLDPWD
/etc
$ env | grep WD
OLDPWD=/etc
PWD=/home/vl
28
Where I just was?
$ echo $OLDPWD
/etc
$ env | grep WD
OLDPWD=/etc
PWD=/home/vl
$ cd -
- is not a regular argument — it’s a special option/operand to cd
that means "previous working directory" ($OLDPWD)
28
Where I just was?
$ echo $OLDPWD
/etc
$ env | grep WD
OLDPWD=/etc
PWD=/home/vl
$ cd -
- is not a regular argument — it’s a special option/operand to cd
that means "previous working directory" ($OLDPWD)
$ ls -
ls: cannot access '-': No such file or directory
28
Where I just was?
$ echo $OLDPWD
/etc
$ env | grep WD
OLDPWD=/etc
PWD=/home/vl
$ cd -
- is not a regular argument — it’s a special option/operand to cd
that means "previous working directory" ($OLDPWD)
$ ls -
ls: cannot access '-': No such file or directory
$ export OLDPWD=/tmp/non-existent
$ cd -
-bash: cd: /tmp/non-existent: No such file or directory
28
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
$ USERNAME=john; echo "USERNAME=$USERNAME"
USERNAME=john
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
$ USERNAME=john; echo "USERNAME=$USERNAME"
USERNAME=john
• USERNAME=john in the first case sets environment variable for echo
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
$ USERNAME=john; echo "USERNAME=$USERNAME"
USERNAME=john
• USERNAME=john in the first case sets environment variable for echo
• But before launching the command, the shell (your running shell) first
parses and expands arguments USERNAME=$USERNAME.
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
$ USERNAME=john; echo "USERNAME=$USERNAME"
USERNAME=john
• USERNAME=john in the first case sets environment variable for echo
• But before launching the command, the shell (your running shell) first
parses and expands arguments USERNAME=$USERNAME.
• Since current shell session has no idea about this variable it expands it to
USERNAME=.
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
$ USERNAME=john; echo "USERNAME=$USERNAME"
USERNAME=john
• USERNAME=john in the first case sets environment variable for echo
• But before launching the command, the shell (your running shell) first
parses and expands arguments USERNAME=$USERNAME.
• Since current shell session has no idea about this variable it expands it to
USERNAME=.
After expanding the arguments shell calls:
$ USERNAME=john echo "USERNAME="
29
Environment variable glitch
$ USERNAME=john echo "USERNAME=$USERNAME"
USERNAME= ???
$ USERNAME=john; echo "USERNAME=$USERNAME"
USERNAME=john
• USERNAME=john in the first case sets environment variable for echo
• But before launching the command, the shell (your running shell) first
parses and expands arguments USERNAME=$USERNAME.
• Since current shell session has no idea about this variable it expands it to
USERNAME=.
After expanding the arguments shell calls:
$ USERNAME=john echo "USERNAME="
$ USERNAME=john sh -c 'echo "USERNAME=$USERNAME"'
USERNAME=john
29
Where can I go?
Home!
$ cd
~$
30
Where can I go?
Home!
$ cd
~$
Alternatives:
$ cd $HOME
$ cd ~
the tilde ~ is not specific to cd, it’s handled by the shell itself
30
Where can I go?
Home!
$ cd
~$
Alternatives:
$ cd $HOME
$ cd ~
the tilde ~ is not specific to cd, it’s handled by the shell itself
Parent directory!
~$ cd ..
/home
30
Where can I go?
Home!
$ cd
~$
Alternatives:
$ cd $HOME
$ cd ~
the tilde ~ is not specific to cd, it’s handled by the shell itself
Parent directory!
~$ cd ..
/home
Stay where you are.
$ cd .
/home
30
Where is that file?
$ locate zbxnext-3106.patch
31
Where is that file?
$ locate zbxnext-3106.patch
Debian:
$ sudo apt install plocate
RedHat:
$ sudo yum install mlocate
31
Where is that file?
$ locate zbxnext-3106.patch
Debian:
$ sudo apt install plocate
RedHat:
$ sudo yum install mlocate
$ sudo updatedb
31
Where is that file?
$ locate zbxnext-3106.patch
Debian:
$ sudo apt install plocate
RedHat:
$ sudo yum install mlocate
$ sudo updatedb
/etc/cron.daily/plocate
31
Where is that file?
$ locate zbxnext-3106.patch
Debian:
$ sudo apt install plocate
RedHat:
$ sudo yum install mlocate
$ sudo updatedb
/etc/cron.daily/plocate
$ locate -i zbxnext-3106.patch
/home/vl/work/ZBXNEXT-3106/zabbix-2.2.14-ZBXNEXT-3106.patch
31
Where is that file?
$ locate zbxnext-3106.patch
Debian:
$ sudo apt install plocate
RedHat:
$ sudo yum install mlocate
$ sudo updatedb
/etc/cron.daily/plocate
$ locate -i zbxnext-3106.patch
/home/vl/work/ZBXNEXT-3106/zabbix-2.2.14-ZBXNEXT-3106.patch
$ locate -i –regex ’zbxnext.*ṗatch’
/home/vl/work/zabbix-2.0.10-zbxnext-300-zbx-8992.patch
/home/vl/work/ZBXNEXT-3106/zabbix-2.2.14-ZBXNEXT-3106.patch
31
How locate works
/var/lib/plocate/plocate.db
• specific binary format
• zstd compressed
• faster than mlocate, might be notable on big systems with millions of files
32
Listing directory permissions
$ ls -l /var/lib/mysql
33
Listing directory permissions
$ ls -l /var/lib/mysql
drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0
drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0
33
Listing directory permissions
$ ls -l /var/lib/mysql
drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0
drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0
$ ls -l /var/lib | grep mysql
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files
-rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool
-rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1
33
Listing directory permissions
$ ls -l /var/lib/mysql
drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0
drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0
$ ls -l /var/lib | grep mysql
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files
-rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool
-rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1
$ ls -l /var/lib | grep 'mysql$'
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
33
Listing directory permissions
$ ls -l /var/lib/mysql
drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0
drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0
$ ls -l /var/lib | grep mysql
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files
-rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool
-rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1
$ ls -l /var/lib | grep 'mysql$'
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
$ ls --color -l /var/lib | grep 'mysql$'
$
33
Listing directory permissions
$ ls -l /var/lib/mysql
drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0
drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0
$ ls -l /var/lib | grep mysql
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files
-rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool
-rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1
$ ls -l /var/lib | grep 'mysql$'
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql
$ ls --color -l /var/lib | grep 'mysql$'
$
$ ls -l -d --color /var/lib/mysql
drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 /var/lib/mysql
-d, --directory list directories themselves, not their contents
33
Securing files
$ echo pass123 > secret.pw
-rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw
34
Securing files
$ echo pass123 > secret.pw
-rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw
$ umask
0022
34
Securing files
$ echo pass123 > secret.pw
-rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw
$ umask
0022
New files : 666 – 022 = 644
New directories : 777 – 022 = 755
34
Securing files
$ echo pass123 > secret.pw
-rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw
$ umask
0022
New files : 666 – 022 = 644
New directories : 777 – 022 = 755
The first digit in a umask value (if present) controls special permission bits:
• 0 — no special restrictions
• 1 — disables the sticky bit
• 2 — disables the setgid bit
• 4 — disables the setuid bit
These bits are rarely masked, so the first digit is usually 0.
34
Securing files
$ umask 0266
35
Securing files
$ umask 0266
New files : 666 – 266 = 400
$ touch /tmp/foo
$ l /tmp/foo
-r-------- 1 root root 0 Oct 11 23:30 /tmp/foo
35
Securing files
$ umask 0266
New files : 666 – 266 = 400
$ touch /tmp/foo
$ l /tmp/foo
-r-------- 1 root root 0 Oct 11 23:30 /tmp/foo
$ chmod 600 secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
35
Securing files
$ umask 0266
New files : 666 – 266 = 400
$ touch /tmp/foo
$ l /tmp/foo
-r-------- 1 root root 0 Oct 11 23:30 /tmp/foo
$ chmod 600 secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
$ chmod u=rw,g=,o= secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
35
Securing files
$ umask 0266
New files : 666 – 266 = 400
$ touch /tmp/foo
$ l /tmp/foo
-r-------- 1 root root 0 Oct 11 23:30 /tmp/foo
$ chmod 600 secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
$ chmod u=rw,g=,o= secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
$ chmod g+r secret.pw
-rw-r----- 1 vl adm 8 Jan 25 16:35 secret.pw
35
Securing files
$ umask 0266
New files : 666 – 266 = 400
$ touch /tmp/foo
$ l /tmp/foo
-r-------- 1 root root 0 Oct 11 23:30 /tmp/foo
$ chmod 600 secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
$ chmod u=rw,g=,o= secret.pw
-rw------- 1 vl adm 8 Jan 25 16:35 secret.pw
$ chmod g+r secret.pw
-rw-r----- 1 vl adm 8 Jan 25 16:35 secret.pw
$ chmod g-r secret.pw
-rw------- 1 vl adm 8 Jan 25 18:47 secret.pw
35
Appending to a file
$ l secret.pw
-rw------- 1 root vl 8 Oct 1 18:47 secret.pw
36
Appending to a file
$ l secret.pw
-rw------- 1 root vl 8 Oct 1 18:47 secret.pw
$ echo foobar >> secret.pw
-bash: secret.pw: Permission denied
36
Appending to a file
$ l secret.pw
-rw------- 1 root vl 8 Oct 1 18:47 secret.pw
$ echo foobar >> secret.pw
-bash: secret.pw: Permission denied
$ sudo echo foobar >> secret.pw
-bash: secret.pw: Permission denied
36
Appending to a file
$ l secret.pw
-rw------- 1 root vl 8 Oct 1 18:47 secret.pw
$ echo foobar >> secret.pw
-bash: secret.pw: Permission denied
$ sudo echo foobar >> secret.pw
-bash: secret.pw: Permission denied
Who handles the redirection?
• The >> secret.pw redirection is handled by your shell, before sudo is
applied.
• sudo only applies to echo, not to the redirection.
36
Appending to a file
$ echo foobar | sudo tee -a secret.pw
foobar
-a, --append
append to the given FILEs, do not overwrite
37
Appending to a file
$ echo foobar | sudo tee -a secret.pw
foobar
-a, --append
append to the given FILEs, do not overwrite
$ sudo cat secret.pw
pass123
foobar
37
File attributes
In Linux, file attributes are special flags that control additional behaviors of
files and directories beyond the normal read, write, and execute permissions.
These are supported on ext2, ext3, ext4, and some other file systems.
Key points:
• Independent of permissions
• Persistent at the filesystem level
• Attributes control:
• Modification
• Access time updates
• Deletion behavior
• Compression
• Analogy:
• Permissions = keys to the cabinet with documents
• Attributes = seals, instructions on the document
38
Securing a file
$ lsattr /etc/passwd
--------------e------- /etc/passwd
39
Securing a file
$ lsattr /etc/passwd
--------------e------- /etc/passwd
$ echo -n --------------e------- | wc -c
22
39
Securing a file
$ lsattr /etc/passwd
--------------e------- /etc/passwd
$ echo -n --------------e------- | wc -c
22
A file with the ’i’ attribute cannot be modified: it cannot be deleted or re-
named, no link can be created to this file.
39
Securing a file
$ lsattr /etc/passwd
--------------e------- /etc/passwd
$ echo -n --------------e------- | wc -c
22
A file with the ’i’ attribute cannot be modified: it cannot be deleted or re-
named, no link can be created to this file.
$ sudo chattr +i /etc/passwd
$ lsattr /etc/passwd
----i---------e------- /etc/passwd
39
Securing a file
$ lsattr /etc/passwd
--------------e------- /etc/passwd
$ echo -n --------------e------- | wc -c
22
A file with the ’i’ attribute cannot be modified: it cannot be deleted or re-
named, no link can be created to this file.
$ sudo chattr +i /etc/passwd
$ lsattr /etc/passwd
----i---------e------- /etc/passwd
$ sudo useradd -M -r -s /usr/sbin/nologin tempuser
useradd: cannot open /etc/passwd
39
Securing a file
$ lsattr /etc/passwd
--------------e------- /etc/passwd
$ echo -n --------------e------- | wc -c
22
A file with the ’i’ attribute cannot be modified: it cannot be deleted or re-
named, no link can be created to this file.
$ sudo chattr +i /etc/passwd
$ lsattr /etc/passwd
----i---------e------- /etc/passwd
$ sudo useradd -M -r -s /usr/sbin/nologin tempuser
useradd: cannot open /etc/passwd
$ sudo chattr -i /etc/passwd
39
Securing a file
$ whoami
vl
$ touch /tmp/foo
$ chattr +i /tmp/foo
Pchattr: Operation not permitted while setting flags on /tmp/foo
40
Common filesystem attributes
Attribute Effect
i Immutable: cannot modify, delete, or rename
a Append-only: can only add data
A No atime updates: read does not change access time
c Compressed: filesystem handles compression
s Secure deletion: wiped securely when deleted
41
Common filesystem attributes
Attribute Effect
i Immutable: cannot modify, delete, or rename
a Append-only: can only add data
A No atime updates: read does not change access time
c Compressed: filesystem handles compression
s Secure deletion: wiped securely when deleted
$ sudo tune2fs -l /dev/sda3 | grep 'Filesystem features'
Filesystem features: has_journal ext_attr resize_inode
dir_index filetype needs_recovery extent 64bit flex_bg
sparse_super large_file huge_file dir_nlink extra_isize
metadata_csum
,
→
,
→
,
→
$ sudo tune2fs -l /dev/sda3 | grep 'Filesystem features' |
grep compress
,
→
$
41
What is a script?
Long commands
Sometimes the command may become really long...
$ while true; do output="$(pgrep -lf zabbix_server)"; if
[ -n "$output" ]; then echo Zabbix server is
running...; fi; sleep 3; done
,
→
,
→
Zabbix server is running...
Zabbix server is running...
42
Long commands
So you need to start writing scripts!
43
A shebang
#!/bin/bash
echo "Cleaning /tmp..."
rm -rfv /tmp/*
44
A shebang
#!/bin/bash
echo "Cleaning /tmp..."
rm -rfv /tmp/*
A shebang is the special character sequence #! at the very beginning of a script
file, followed by the path to the interpreter that should be used to execute the
script.
44
A shebang
#!/bin/bash
echo "Cleaning /tmp..."
rm -rfv /tmp/*
A shebang is the special character sequence #! at the very beginning of a script
file, followed by the path to the interpreter that should be used to execute the
script.
It’s also known as a hashbang or hash-bang, and it allows a script to be run
directly as an executable in Unix-like systems by telling the system which
program (like a shell or Python interpreter) to use.
44
A bash shebang
echo "Cleaning /tmp..."
rm -rf /tmp/*
45
A bash shebang
echo "Cleaning /tmp..."
rm -rf /tmp/*
$ bash cleanup.sh
45
A bash shebang
echo "Cleaning /tmp..."
rm -rf /tmp/*
$ bash cleanup.sh
$ ./cleanup.sh
-bash: ./cleanup.sh: Permission denied
45
A bash shebang
echo "Cleaning /tmp..."
rm -rf /tmp/*
$ bash cleanup.sh
$ ./cleanup.sh
-bash: ./cleanup.sh: Permission denied
$ chmod +x cleanup.sh
45
A bash shebang
echo "Cleaning /tmp..."
rm -rf /tmp/*
$ bash cleanup.sh
$ ./cleanup.sh
-bash: ./cleanup.sh: Permission denied
$ chmod +x cleanup.sh
$ ./cleanup.sh
Cleaning /tmp...
$
45
A bash shebang
echo "Cleaning /tmp..."
rm -rf /tmp/*
$ bash cleanup.sh
$ ./cleanup.sh
-bash: ./cleanup.sh: Permission denied
$ chmod +x cleanup.sh
$ ./cleanup.sh
Cleaning /tmp...
$
When a script without a shebang is executed directly (e.g.,
./cleanup.sh) and the current shell is Bash, the kernel will attempt to
execute the file using the current shell.
45
A bash shebang
Consider a simple perl script.
print("I am $ENV{USER}n");
46
A bash shebang
Consider a simple perl script.
print("I am $ENV{USER}n");
$ /tmp/echo.pl
-bash: /tmp/echo.pl: Permission denied
46
A bash shebang
Consider a simple perl script.
print("I am $ENV{USER}n");
$ /tmp/echo.pl
-bash: /tmp/echo.pl: Permission denied
$ chmod +x /tmp/echo.pl
46
A bash shebang
Consider a simple perl script.
print("I am $ENV{USER}n");
$ /tmp/echo.pl
-bash: /tmp/echo.pl: Permission denied
$ chmod +x /tmp/echo.pl
$ /tmp/echo.pl
/tmp/echo.pl: line 1: syntax error near unexpected token
`"I am $ENV{USER}n"'
46
A bash shebang
Consider a simple perl script.
print("I am $ENV{USER}n");
$ /tmp/echo.pl
-bash: /tmp/echo.pl: Permission denied
$ chmod +x /tmp/echo.pl
$ /tmp/echo.pl
/tmp/echo.pl: line 1: syntax error near unexpected token
`"I am $ENV{USER}n"'
$ perl /tmp/echo.pl
I am vl
46
A shebang
Bash scripts commonly start with #!/bin/bash
47
A shebang
Bash scripts commonly start with #!/bin/bash
On many systems (Linux, macOS, BSDs, NixOS, embedded systems, custom
setups), bash might be installed elsewhere (e.g. /usr/local/bin/bash,
/opt/homebrew/bin/bash). If bash isn’t at /bin/bash, your script won’t run.
47
A shebang
Bash scripts commonly start with #!/bin/bash
On many systems (Linux, macOS, BSDs, NixOS, embedded systems, custom
setups), bash might be installed elsewhere (e.g. /usr/local/bin/bash,
/opt/homebrew/bin/bash). If bash isn’t at /bin/bash, your script won’t run.
Start you bash code with:
#!/usr/bin/env bash
47
A shebang
Bash scripts commonly start with #!/bin/bash
On many systems (Linux, macOS, BSDs, NixOS, embedded systems, custom
setups), bash might be installed elsewhere (e.g. /usr/local/bin/bash,
/opt/homebrew/bin/bash). If bash isn’t at /bin/bash, your script won’t run.
Start you bash code with:
#!/usr/bin/env bash
• On almost all modern Unix-like systems (Linux, macOS, BSD, etc.), env
is indeed located at /usr/bin/env
• That’s why it became the de facto portable way of locating interpreters
(e.g. bash, python3, perl)
• The POSIX standard requires env, but it does not require it to live in
/usr/bin — only that it be somewhere in the user’s $PATH
47
Hello, world!
#!/usr/bin/env bash
echo 'Hello, world!'
$ ./hello.sh
Hello, world!
48
Hello, world!
#!/usr/bin/env bash
echo 'Hello, world!'
$ ./hello.sh
Hello, world!
Create a backup script that:
1. Takes 2 arguments: file to back up and target directory
2. Target directory is optional, default $HOME/backup
3. If target directory does not exist - create it.
48
Reading command-line arguments
#!/usr/bin/env bash
49
Reading command-line arguments
#!/usr/bin/env bash
if [ $1 == "" ]; then
49
Reading command-line arguments
#!/usr/bin/env bash
if [ $1 == "" ]; then
# ./backup.sh /etc/resolv.conf
if [ /etc/resolv.conf == "" ]; then
49
Reading command-line arguments
#!/usr/bin/env bash
if [ $1 == "" ]; then
# ./backup.sh /etc/resolv.conf
if [ /etc/resolv.conf == "" ]; then
# ./backup.sh
if [ == "" ]; then
./backup.sh: line 4: [: =: unary operator expected
49
Reading command-line arguments
#!/usr/bin/env bash
if [ $1 == "" ]; then
# ./backup.sh /etc/resolv.conf
if [ /etc/resolv.conf == "" ]; then
# ./backup.sh
if [ == "" ]; then
./backup.sh: line 4: [: =: unary operator expected
Variant What it does Safe?
[ -z $1 ] Works, but may break with special characters. ✗
[ -z "$1" ] Correct, safe way to check for an empty argument. ✓
49
Reading command-line arguments
#!/usr/bin/env bash
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
exit 1
fi
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
exit 1
fi
FILE="$1"
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
exit 1
fi
FILE="$1"
BACKUP_DIR="$2"
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
exit 1
fi
FILE="$1"
BACKUP_DIR="$2"
if [ -z "$BACKUP_DIR" ]; then
# default backup directory
BACKUP_DIR="$HOME/backup"
fi
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
exit 1
fi
FILE="$1"
BACKUP_DIR="$2"
if [ -z "$BACKUP_DIR" ]; then
# default backup directory
BACKUP_DIR="$HOME/backup"
fi
# create backup directory if it doesn’t exist
mkdir -p "$BACKUP_DIR"
50
Reading command-line arguments
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <file> [backup directory]"
echo "default backup directory - $HOME/backup"
exit 1
fi
FILE="$1"
BACKUP_DIR="$2"
if [ -z "$BACKUP_DIR" ]; then
# default backup directory
BACKUP_DIR="$HOME/backup"
fi
# create backup directory if it doesn’t exist
mkdir -p "$BACKUP_DIR"
cp "$FILE" "$BACKUP_DIR/"
echo "Backup of ’$FILE’ saved to ’$BACKUP_DIR/’"
50
Reading command-line arguments
$ ./backup.sh
Usage: ./backup.sh <file> [backup directory]
default backup directory - /home/vl/backup
51
Reading command-line arguments
$ ./backup.sh
Usage: ./backup.sh <file> [backup directory]
default backup directory - /home/vl/backup
$ echo $?
$ 1
51
Reading command-line arguments
$ ./backup.sh
Usage: ./backup.sh <file> [backup directory]
default backup directory - /home/vl/backup
$ echo $?
$ 1
$ ./backup.sh 2> /dev/null || echo "Backup failed on $(date
--rfc-3339=date)"'!'
,
→
Backup failed on 2025-09-30!
51
Reading command-line arguments
$ ./backup.sh
Usage: ./backup.sh <file> [backup directory]
default backup directory - /home/vl/backup
$ echo $?
$ 1
$ ./backup.sh 2> /dev/null || echo "Backup failed on $(date
--rfc-3339=date)"'!'
,
→
Backup failed on 2025-09-30!
$ ./backup.sh backup.sh
Backup of 'backup.sh' saved to '/home/vl/backup/'
51
Reading command-line arguments
$ ./backup.sh
Usage: ./backup.sh <file> [backup directory]
default backup directory - /home/vl/backup
$ echo $?
$ 1
$ ./backup.sh 2> /dev/null || echo "Backup failed on $(date
--rfc-3339=date)"'!'
,
→
Backup failed on 2025-09-30!
$ ./backup.sh backup.sh
Backup of 'backup.sh' saved to '/home/vl/backup/'
$ ./backup.sh backup.sh /tmp
Backup of 'backup.sh' saved to '/tmp/'
$ ls ~/backup/backup.sh /tmp/backup.sh
/home/vl/backup/backup.sh /tmp/backup.sh
51
Error handling
$ ./backup.sh non-existent.txt
cp: cannot stat 'non-existent.txt': No such file or directory
Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’
52
Error handling
$ ./backup.sh non-existent.txt
cp: cannot stat 'non-existent.txt': No such file or directory
Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’
cp "$FILE" "$BACKUP_DIR/"
if [ $? -ne 0 ]; then
echo "An error occured, see above."
exit 1
fi
52
Error handling
$ ./backup.sh non-existent.txt
cp: cannot stat 'non-existent.txt': No such file or directory
Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’
cp "$FILE" "$BACKUP_DIR/"
if [ $? -ne 0 ]; then
echo "An error occured, see above."
exit 1
fi
cp: cannot stat 'non-existent.txt': No such file or
directory
,
→
An error occured, see above.
52
Error handling
$ ./backup.sh non-existent.txt
cp: cannot stat 'non-existent.txt': No such file or directory
Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’
cp "$FILE" "$BACKUP_DIR/"
if [ $? -ne 0 ]; then
echo "An error occured, see above."
exit 1
fi
cp: cannot stat 'non-existent.txt': No such file or
directory
,
→
An error occured, see above.
if ! cp "$FILE" "$BACKUP_DIR/"; then
exit 1
fi
52
Error handling
$ ./backup.sh non-existent.txt
cp: cannot stat 'non-existent.txt': No such file or directory
Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’
cp "$FILE" "$BACKUP_DIR/"
if [ $? -ne 0 ]; then
echo "An error occured, see above."
exit 1
fi
cp: cannot stat 'non-existent.txt': No such file or
directory
,
→
An error occured, see above.
if ! cp "$FILE" "$BACKUP_DIR/"; then
exit 1
fi
cp: cannot stat 'non-existent.txt': No such file or
directory
,
→
52
Error handling
if ! mkdir -p "$BACKUP_DIR/"; then
exit 1
fi
if ! cp "$FILE" "$BACKUP_DIR/"; then
exit 1
fi
if ! ls "$BACKUP_DIR/$(basename $FILE)" > /dev/null;
then
,
→
exit 1
fi
53
Error handling
if ! mkdir -p "$BACKUP_DIR/"; then
exit 1
fi
if ! cp "$FILE" "$BACKUP_DIR/"; then
exit 1
fi
if ! ls "$BACKUP_DIR/$(basename $FILE)" > /dev/null;
then
,
→
exit 1
fi
mkdir -p "$BACKUP_DIR/" || exit 1
cp "$FILE" "$BACKUP_DIR/" || exit 1
ls "$BACKUP_DIR/$(basename FILE)" > /dev/null || exit 1
53
Error handling
#!/usr/bin/env bash
set -e
54
Error handling
#!/usr/bin/env bash
set -e
#!/usr/bin/env bash
set -e
mkdir -p "$BACKUP_DIR/"
cp "$FILE" "$BACKUP_DIR/"
ls "$BACKUP_DIR/$(basename FILE)"
54
Error handling
#!/usr/bin/env bash
set -e
#!/usr/bin/env bash
set -e
mkdir -p "$BACKUP_DIR/"
cp "$FILE" "$BACKUP_DIR/"
ls "$BACKUP_DIR/$(basename FILE)"
$ ./backup.sh backup.sh
mkdir: cannot create directory ‘/home/vl/backup/’:
Permission denied
,
→
$ echo $?
1
54
Error handling
#!/usr/bin/env bash
set -e
false | true
echo "continue with the script"
55
Error handling
#!/usr/bin/env bash
set -e
false | true
echo "continue with the script"
$ ./test.sh
continue with the script
55
Error handling
#!/usr/bin/env bash
set -e
false | true
echo "continue with the script"
$ ./test.sh
continue with the script
#!/usr/bin/env bash
set -e
set -o pipefail
false | true
echo "continue with the script"
55
Error handling
#!/usr/bin/env bash
set -e
false | true
echo "continue with the script"
$ ./test.sh
continue with the script
#!/usr/bin/env bash
set -e
set -o pipefail
false | true
echo "continue with the script"
$ ./test.sh
$ echo $?
1
55
Debugging
$ ./backup.sh backup.sh "/opt/backup dir"
./backup.sh: line 12: [: /opt/backup: binary operator
expected
,
→
56
Debugging
$ ./backup.sh backup.sh "/opt/backup dir"
./backup.sh: line 12: [: /opt/backup: binary operator
expected
,
→
12: if [ -z $BACKUP_DIR ]; then
13: BACKUP_DIR="$HOME/backup"
14: fi
56
Debugging
$ ./backup.sh backup.sh "/opt/backup dir"
./backup.sh: line 12: [: /opt/backup: binary operator
expected
,
→
12: if [ -z $BACKUP_DIR ]; then
13: BACKUP_DIR="$HOME/backup"
14: fi
set -x
if [ -z $BACKUP_DIR ]; then
BACKUP_DIR="$HOME/backup"
fi
set +x
56
Debugging
$ ./backup.sh backup.sh "/opt/backup dir"
./backup.sh: line 12: [: /opt/backup: binary operator
expected
,
→
12: if [ -z $BACKUP_DIR ]; then
13: BACKUP_DIR="$HOME/backup"
14: fi
set -x
if [ -z $BACKUP_DIR ]; then
BACKUP_DIR="$HOME/backup"
fi
set +x
$ ./backup.sh backup.sh "/opt/backup dir"
+ '[' -z /opt/backup dir ']'
./backup.sh: line 12: [: /opt/backup: binary operator expected
+ set +x
56
Functions
dbg()
{
[ "$DEBUG" = "1" ] || return
echo "DBG: $*"
}
msg()
{
echo "MSG: $*"
}
err()
{
echo "ERR: $*"
exit 1
}
57
Functions
dbg()
{
[ "$DEBUG" = "1" ] || return
echo "DBG: $*"
}
msg()
{
echo "MSG: $*"
}
err()
{
echo "ERR: $*"
exit 1
}
• Less lines
• Consistency
57
Catching signals
#!/usr/bin/env bash
TMP_FILE="$(mktemp)" || exit 1
cleanup()
{
echo "Cleaning up..."
rm -fv "$TMP_FILE"
exit $1
}
trap "cleanup 1" INT TERM
[...]
cleanup
58
Built-in string operations
If you use bash 4 or later, don’t bother with ’tr’ and ’expr’:
$ str=Shell; echo $str | tr [a-z] [A-Z]
SHELL
59
Built-in string operations
If you use bash 4 or later, don’t bother with ’tr’ and ’expr’:
$ str=Shell; echo $str | tr [a-z] [A-Z]
SHELL
Use bash built-in string operations power:
str=shell; echo ${str^}; echo ${str^^}; echo ${str„}
Shell
SHELL
shell
59
Built-in string operations
${variable#pattern}
Removes the shortest match of pattern from the beginning.
${variable##pattern}
Removes the longest match of pattern from the beginning.
${variable%pattern}
Removes the shortest match of pattern from the end.
${variable%%pattern}
Removes the longest match of pattern from the end.
60
Built-in string operations
${variable#pattern}
Removes the shortest match of pattern from the beginning.
${variable##pattern}
Removes the longest match of pattern from the beginning.
${variable%pattern}
Removes the shortest match of pattern from the end.
${variable%%pattern}
Removes the longest match of pattern from the end.
Example:
filename="report.2025.txt"
echo "${filename%.*}" # Output: report.2025
echo "${filename##*.}" # Output: txt
60
Brace expansion
Brace expansion in Bash is a feature that generates strings from patterns
without loops or external commands.
61
Brace expansion
Brace expansion in Bash is a feature that generates strings from patterns
without loops or external commands.
Ranges:
$ echo file{1..3}.txt
file1.txt file2.txt file3.txt
61
Brace expansion
Brace expansion in Bash is a feature that generates strings from patterns
without loops or external commands.
Ranges:
$ echo file{1..3}.txt
file1.txt file2.txt file3.txt
Lists:
$ echo {A,B,C}
A B C
61
Brace expansion
Brace expansion in Bash is a feature that generates strings from patterns
without loops or external commands.
Ranges:
$ echo file{1..3}.txt
file1.txt file2.txt file3.txt
Lists:
$ echo {A,B,C}
A B C
$ rm -fv server.c.{orig,rej,bak}
# translates to -> rm -fv server.c.orig server.c.rej server.c.bak
removed 'server.c.bak'
61
Some more tricks?
Randomize
$ echo $[RANDOM]
11419
62
Randomize
$ echo $[RANDOM]
11419
$ [ $[RANDOM % 2] -eq 0 ] && echo -n Gleb || echo -n dimir; echo will go
to India'!'
,
→
dimir will go to India!
62
Randomize
$ echo $[RANDOM]
11419
$ [ $[RANDOM % 2] -eq 0 ] && echo -n Gleb || echo -n dimir; echo will go
to India'!'
,
→
dimir will go to India!
$ sleep $(( RANDOM % 5 + 1 )) # sleep for 1–5 seconds
62
Randomize
$ echo $[RANDOM]
11419
$ [ $[RANDOM % 2] -eq 0 ] && echo -n Gleb || echo -n dimir; echo will go
to India'!'
,
→
dimir will go to India!
$ sleep $(( RANDOM % 5 + 1 )) # sleep for 1–5 seconds
This is called bash arithmetic evaluation.
• (( )) evaluates arithmetic expressions
• $(( )) expands to the numeric result
• Works with standard arithmetic operators (+, -, *, /, %, ++, --, etc.)
• No need for $ before variable names inside (( ))
62
Arithmetic evaluation
$ (( 2 + 3 ))
$ echo $?
0
63
Arithmetic evaluation
$ (( 2 + 3 ))
$ echo $?
0
$ (( 2 - 2 ))
$ echo $?
1
63
Arithmetic evaluation
$ (( 2 + 3 ))
$ echo $?
0
$ (( 2 - 2 ))
$ echo $?
1
$ a=5
$ (( a = a + 2 ))
$ echo $a
7
63
Arithmetic evaluation
$ (( 2 + 3 ))
$ echo $?
0
$ (( 2 - 2 ))
$ echo $?
1
$ a=5
$ (( a = a + 2 ))
$ echo $a
7
$ rv=$(( 5 + 5 ))
$ echo $rv
10
63
Wildcard expansion
$ ls * | grep bashrc
$ ls .* | grep bashrc
.bashrc
64
Wildcard expansion
$ ls * | grep bashrc
$ ls .* | grep bashrc
.bashrc
Make sure you never delete hidden files like this:
$ rm -rf .*
because ".*" matches ".." (higher-level directory)!
$ ls -R .* | grep bashrc
bash.bashrc
bash.bashrc
bash.bashrc
bash.bashrc
64
Wildcard expansion
$ ls * | grep bashrc
$ ls .* | grep bashrc
.bashrc
Make sure you never delete hidden files like this:
$ rm -rf .*
because ".*" matches ".." (higher-level directory)!
$ ls -R .* | grep bashrc
bash.bashrc
bash.bashrc
bash.bashrc
bash.bashrc
Correct way of doing this:
$ rm -rfv .[a-zA-Z]*
64
Following the log file
Log file rotated by logrotate.
$ tail -f /var/log/zabbix_server.log
29631:20160927:152455.721 In substitute_functions()
29631:20160927:152455.721 In zbx_extract_functionids()
tr_num:1
,
→
tail: /tmp/foo.log: file truncated
65
Following the log file
Log file rotated by logrotate.
$ tail -f /var/log/zabbix_server.log
29631:20160927:152455.721 In substitute_functions()
29631:20160927:152455.721 In zbx_extract_functionids()
tr_num:1
,
→
tail: /tmp/foo.log: file truncated
$ tail -F /var/log/zabbix_server.log
29631:20160927:152455.721 In substitute_functions()
29631:20160927:152455.721 In zbx_extract_functionids() tr_num:1
tail: /tmp/foo.log: file truncated
29631:20160927:152455.721 End of zbx_extract_functionids() functionids_num:1
29631:20160927:152455.721 In zbx_populate_function_items() functionids_num:1
29631:20160927:152455.721 End of zbx_populate_function_items() ifuncs_num:1
29631:20160927:152455.721 In zbx_evaluate_item_functions() ifuncs_num:1
29631:20160927:152455.721 In evaluate_function() function:'Zabbix server:test.strle
29631:20160927:152455.721 In evaluate_STRLEN()
29631:20160927:152455.721 In evaluate_LAST()
65
History expansion
The History library provides a history expansion feature that is similar to the
history expansion provided by csh. It is used to manipulate the history
information. The following are event designators. It is a reference to a
command line entry in the history list
!^ first argument
!$ last argument
!* all arguments
!:2 second argument
!:2-3 second to third arguments
!:2-$ second to last arguments
!:2* second to last arguments
!:2- second to next to last arguments
!:0 the command itself
!! repeat the previous line
66
History expansion
$ mkdir -p /usr/local/bin
67
History expansion
$ mkdir -p /usr/local/bin
$ cp backup.sh !$
cp backup.sh /usr/local/bin
67
History expansion
$ mkdir -p /usr/local/bin
$ cp backup.sh !$
cp backup.sh /usr/local/bin
$ chmod +x !$/backup.sh
chmod +x /usr/local/bin/backup.sh
67
History expansion
$ mkdir -p /usr/local/bin
$ cp backup.sh !$
cp backup.sh /usr/local/bin
$ chmod +x !$/backup.sh
chmod +x /usr/local/bin/backup.sh
$ !$ list.pl
/usr/local/bin/backup.sh list.pl
Backup of 'list.pl' saved to '/home/vl/backup/'
67
History expansion
$ ls /var/log/mysql/error.log /var/log/mysql.err
/var/log/mysql.err /var/log/mysql/error.log
68
History expansion
$ ls /var/log/mysql/error.log /var/log/mysql.err
/var/log/mysql.err /var/log/mysql/error.log
$ cp !:1 /backup/
cp /var/log/mysql/error.log /backup/
68
History expansion
$ ls /var/log/mysql/error.log /var/log/mysql.err
/var/log/mysql.err /var/log/mysql/error.log
$ cp !:1 /backup/
cp /var/log/mysql/error.log /backup/
$ apt install openssh-server build-essential binutils make
pkg-config libldns-dev automake libssl-dev emacs-nox
php-mbstring php-bcmath php-gd php-ldap php-mysql
php-curl locales python3 faketime libnet-dns-perl less
wget curl tmux git jq
,
→
,
→
,
→
,
→
E: Could not open [...] (13: Permission denied)
68
History expansion
$ ls /var/log/mysql/error.log /var/log/mysql.err
/var/log/mysql.err /var/log/mysql/error.log
$ cp !:1 /backup/
cp /var/log/mysql/error.log /backup/
$ apt install openssh-server build-essential binutils make
pkg-config libldns-dev automake libssl-dev emacs-nox
php-mbstring php-bcmath php-gd php-ldap php-mysql
php-curl locales python3 faketime libnet-dns-perl less
wget curl tmux git jq
,
→
,
→
,
→
,
→
E: Could not open [...] (13: Permission denied)
$ sudo !!
sudo apt install [...]
68
End of command-line options
$ diff -ur master master.new | grep ---
69
End of command-line options
$ diff -ur master master.new | grep ---
grep: unrecognized option '---'
Try 'grep --help' for more information.
69
End of command-line options
$ diff -ur master master.new | grep ---
grep: unrecognized option '---'
Try 'grep --help' for more information.
$ diff -ur master master.new | grep '---'
--- master/src/libs/zbxservice/service.c 2025-10-10
08:01:16.942685012 +0000
,
→
--- master/src/zabbix_server/server.c 2025-10-10
08:01:16.944685012 +0000
,
→
69
End of command-line options
$ diff -ur master master.new | grep ---
grep: unrecognized option '---'
Try 'grep --help' for more information.
$ diff -ur master master.new | grep '---'
--- master/src/libs/zbxservice/service.c 2025-10-10
08:01:16.942685012 +0000
,
→
--- master/src/zabbix_server/server.c 2025-10-10
08:01:16.944685012 +0000
,
→
$ diff -ur master master.new | grep –– ---
--- a/src/libs/zbxservice/service.c
--- a/src/zabbix_server/server.c
-- stop reading command-line options
69
End of command-line options
$ diff -ur master master.new | grep ---
grep: unrecognized option '---'
Try 'grep --help' for more information.
$ diff -ur master master.new | grep '---'
--- master/src/libs/zbxservice/service.c 2025-10-10
08:01:16.942685012 +0000
,
→
--- master/src/zabbix_server/server.c 2025-10-10
08:01:16.944685012 +0000
,
→
$ diff -ur master master.new | grep –– ---
--- a/src/libs/zbxservice/service.c
--- a/src/zabbix_server/server.c
-- stop reading command-line options
$ man grep | grep -- -R
-R, --dereference-recursive
69
Viewing diff
70
Viewing diff
71
Viewing diff
$ apt install dwdiff
72
Viewing diff
$ apt install dwdiff
$ cat /usr/local/bin/vldiff
#!/usr/bin/env bash
dwdiff --color=bred,bgreen -d ',;&*()[]{}=".' --diff-input $@
72
Viewing diff
$ apt install dwdiff
$ cat /usr/local/bin/vldiff
#!/usr/bin/env bash
dwdiff --color=bred,bgreen -d ',;&*()[]{}=".' --diff-input $@
$ git diff | vldiff
72
grep not working
$ ssh | grep --color bind_address
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b
bind_address]
,
→
[-c cipher_spec] [-D [bind_address:]port] [-E log_file]
[-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]
[-J destination] [-L address] [-l login_name] [-m mac_spec]
[-O ctl_cmd] [-o option] [-P tag] [-p port] [-R address]
[-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
destination [command [argument ...]] ssh [-Q query_option]
73
grep not working
$ ssh | grep --color bind_address
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b
bind_address]
,
→
[-c cipher_spec] [-D [bind_address:]port] [-E log_file]
[-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]
[-J destination] [-L address] [-l login_name] [-m mac_spec]
[-O ctl_cmd] [-o option] [-P tag] [-p port] [-R address]
[-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
destination [command [argument ...]] ssh [-Q query_option]
$ ssh 2>&1 | grep --color bind_address
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address]
73
Working with processes
$ ps ax | grep zabbix_proxy
28559 ? S 0:00 sbin/zabbix_proxy
28560 ? S 0:00 sbin/zabbix_proxy
74
Working with processes
$ ps ax | grep zabbix_proxy
28559 ? S 0:00 sbin/zabbix_proxy
28560 ? S 0:00 sbin/zabbix_proxy
$ pgrep zabbix_proxy
28559
28560
74
Working with processes
$ ps ax | grep zabbix_proxy
28559 ? S 0:00 sbin/zabbix_proxy
28560 ? S 0:00 sbin/zabbix_proxy
$ pgrep zabbix_proxy
28559
28560
$ pgrep -la zabbix_proxy
28559 sbin/zabbix_proxy -c /etc/zabbix/zabbix_proxy.conf
28560 sbin/zabbix_proxy: configuration syncer [syncing configuration]
-l, --list-name
List the process name as well as the process ID (pgrep only)
-a, --list-full
List the full command line as well as the process ID (pgrep only)
74
Working with processes
$ pgrep -lfa poller
28564 sbin/zabbix_proxy: browser poller
28573 sbin/zabbix_proxy: agent poller
28575 sbin/zabbix_proxy: snmp poller
-f, --full
The pattern is matched against the full command line
75
Working with processes
$ pgrep -lfa poller
28564 sbin/zabbix_proxy: browser poller
28573 sbin/zabbix_proxy: agent poller
28575 sbin/zabbix_proxy: snmp poller
-f, --full
The pattern is matched against the full command line
$ pkill -e zabbix_proxy
zabbix_proxy killed (pid 31152)
zabbix_proxy killed (pid 31153)
75
Working with processes
$ pgrep -lfa poller
28564 sbin/zabbix_proxy: browser poller
28573 sbin/zabbix_proxy: agent poller
28575 sbin/zabbix_proxy: snmp poller
-f, --full
The pattern is matched against the full command line
$ pkill -e zabbix_proxy
zabbix_proxy killed (pid 31152)
zabbix_proxy killed (pid 31153)
-e, --echo
Display name and PID of the process being killed (pkill only)
75
Working with processes
$ pgrep -lfa poller
28564 sbin/zabbix_proxy: browser poller
28573 sbin/zabbix_proxy: agent poller
28575 sbin/zabbix_proxy: snmp poller
-f, --full
The pattern is matched against the full command line
$ pkill -e zabbix_proxy
zabbix_proxy killed (pid 31152)
zabbix_proxy killed (pid 31153)
-e, --echo
Display name and PID of the process being killed (pkill only)
$ pkill -fe poller
zabbix_proxy killed (pid 31656)
zabbix_proxy killed (pid 31661)
75
Thank you
Thank you for listening!
Questions?
76

Linux Command-line Tips and Tricks - ATO 2025

  • 1.
    Linux command-line tips& tricks Vladimir Levijev (aka dimir) October 13, 2025 1
  • 2.
    Outline Before we start Introduction Whatis Linux? Some history What is a Shell? Basics about Shell Shell environment Bash commands What is a script? One-liners The Bash script Some more tricks? Some tricks 2
  • 3.
  • 4.
    About myself My nameis Vladimir Levijev (aka dimir) I’ve been in IT for 24 years. 3
  • 5.
    About myself My nameis Vladimir Levijev (aka dimir) I’ve been in IT for 24 years. • Born in Tallinn, Estonia. 3
  • 6.
    About myself My nameis Vladimir Levijev (aka dimir) I’ve been in IT for 24 years. • Born in Tallinn, Estonia. • Joined IT in 2001 (24 years), working only on GNU/Linux since. 3
  • 7.
    About myself My nameis Vladimir Levijev (aka dimir) I’ve been in IT for 24 years. • Born in Tallinn, Estonia. • Joined IT in 2001 (24 years), working only on GNU/Linux since. • Working as a C developer since 2003. 3
  • 8.
    About myself My nameis Vladimir Levijev (aka dimir) I’ve been in IT for 24 years. • Born in Tallinn, Estonia. • Joined IT in 2001 (24 years), working only on GNU/Linux since. • Working as a C developer since 2003. • At the age of 29 moved to Latvia. 3
  • 9.
    About myself My nameis Vladimir Levijev (aka dimir) I’ve been in IT for 24 years. • Born in Tallinn, Estonia. • Joined IT in 2001 (24 years), working only on GNU/Linux since. • Working as a C developer since 2003. • At the age of 29 moved to Latvia. • Working at Zabbix as a C developer for the last 14 years. 3
  • 10.
  • 11.
  • 12.
  • 13.
    Land area perperson 7
  • 14.
    Some facts Estonia: • Estoniahas produced global tech companies like Skype, TransferWise (Wise), and Bolt. • Alfred Neuland won a gold medal (1920 Antwerp Olympics, Belgium) in Men’s lightweight (67.5 kg) weightifting. • First nation to offer Internet Voting (2005). 8
  • 15.
    Some facts Estonia: • Estoniahas produced global tech companies like Skype, TransferWise (Wise), and Bolt. • Alfred Neuland won a gold medal (1920 Antwerp Olympics, Belgium) in Men’s lightweight (67.5 kg) weightifting. • First nation to offer Internet Voting (2005). Latvia: • First Olympic Gold in Men’s BMX in 2008 in Beijing, Māris Štrombergs. • First Olympic Gold in 3x3 Basketball in 2024, Paris. • First Latvian Oscar Winner (2025) for the animated film Flow (Latvian: Straume). 8
  • 16.
    About Zabbix • 100%Open Source monitoring solution. 9
  • 17.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. 9
  • 18.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. 9
  • 19.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. 9
  • 20.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. 9
  • 21.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. • Features: • Agent-based and Agentless monitoring 9
  • 22.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. • Features: • Agent-based and Agentless monitoring • Distributed 9
  • 23.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. • Features: • Agent-based and Agentless monitoring • Distributed • Automation (auto-discovery) 9
  • 24.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. • Features: • Agent-based and Agentless monitoring • Distributed • Automation (auto-discovery) • Visualization 9
  • 25.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. • Features: • Agent-based and Agentless monitoring • Distributed • Automation (auto-discovery) • Visualization • Highly customizable 9
  • 26.
    About Zabbix • 100%Open Source monitoring solution. • The initial version 0.1 was released in 2001 under GPLv2. • The company with the same name was founded in 2005. • Has grown to 150 employees and 5 locations since including Latvia, Japan, Brazil, Mexico, France. • GPLv2 -> AGPLv3 in 2024. • Features: • Agent-based and Agentless monitoring • Distributed • Automation (auto-discovery) • Visualization • Highly customizable • Global Footprint: Used by organizations in more than 160 countries, serving both small businesses and global enterprises. 9
  • 27.
  • 28.
    Zabbix company services •Technical support 10
  • 29.
    Zabbix company services •Technical support • Consulting 10
  • 30.
    Zabbix company services •Technical support • Consulting • Training and certification 10
  • 31.
    Zabbix company services •Technical support • Consulting • Training and certification • Turnkey solutions and implementation 10
  • 32.
    Zabbix company services •Technical support • Consulting • Training and certification • Turnkey solutions and implementation • Integration and custom development 10
  • 33.
    Zabbix company services •Technical support • Consulting • Training and certification • Turnkey solutions and implementation • Integration and custom development • Zabbix Cloud 10
  • 34.
  • 35.
    Linux documentaries Recommended documentarymovies about Linux: The Code (2001) Revolution OS (2001) 11
  • 36.
    What is aShell?
  • 37.
    Without operating system •Accessing the SATA controller • Communicate with the controller via MMIO or I/O ports (usually protected by the OS). • Obtain access to hardware registers that control the SATA interface. • Sending commands • Construct and send ATA/ATAPI commands such as READ SECTOR(S). • Place command parameters into the controller’s command registers. • Handling DMA or PIO transfers • Use PIO to read/write data through CPU-accessible registers, or • Set up DMA so the controller moves data directly into memory buffers. • Waiting for status • Poll or check status registers to determine readiness, completion, or errors. • Handle and interpret controller/drive error bits if they appear. • Reading the data • Once transfer completes, access the memory buffer (or controller data registers) to obtain raw sector bytes. • Verify data integrity (e.g., via status or checksums if available). • No filesystem support • This method reads raw sectors — the program must implement filesystem parsing (FAT, NTFS, ext, etc.) to locate a file’s sectors. • There is no fopen/OS-level abstraction in this approach. 12
  • 38.
    What is aShell? Operating systems provide various services to their users, including file management, process management (running and terminating applications), batch processing, and operating system monitoring and configuration. The term shell in computing refers to an interface that allows a user to interact with an operating system. There are two main types: command-line shells and graphical shells. 13
  • 39.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. 14
  • 40.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. • It was simple, mainly for running commands and basic scripting. 14
  • 41.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. • It was simple, mainly for running commands and basic scripting. • In 1977 at AT&T Bell Labs Stephen Bourne developed much more powerful Bourne shell (also sh). 14
  • 42.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. • It was simple, mainly for running commands and basic scripting. • In 1977 at AT&T Bell Labs Stephen Bourne developed much more powerful Bourne shell (also sh). • 1978 - C Shell (csh), C-like syntax 14
  • 43.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. • It was simple, mainly for running commands and basic scripting. • In 1977 at AT&T Bell Labs Stephen Bourne developed much more powerful Bourne shell (also sh). • 1978 - C Shell (csh), C-like syntax • 1980s - Korn Shell (ksh), TENEX C Shell (tcsh), Almquist Shell (ash) • 1980s - Z Shell (zsh), combining features from sh, ksh, and csh 14
  • 44.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. • It was simple, mainly for running commands and basic scripting. • In 1977 at AT&T Bell Labs Stephen Bourne developed much more powerful Bourne shell (also sh). • 1978 - C Shell (csh), C-like syntax • 1980s - Korn Shell (ksh), TENEX C Shell (tcsh), Almquist Shell (ash) • 1980s - Z Shell (zsh), combining features from sh, ksh, and csh • 1989 - Brian Fox developed Bourne Again Shell (bash), combines features of sh and csh. 14
  • 45.
    Fist shells • Thefirst widely used UNIX shell was the Thompson shell (sh), created by Ken Thompson around 1971 at Bell Labs. • It was simple, mainly for running commands and basic scripting. • In 1977 at AT&T Bell Labs Stephen Bourne developed much more powerful Bourne shell (also sh). • 1978 - C Shell (csh), C-like syntax • 1980s - Korn Shell (ksh), TENEX C Shell (tcsh), Almquist Shell (ash) • 1980s - Z Shell (zsh), combining features from sh, ksh, and csh • 1989 - Brian Fox developed Bourne Again Shell (bash), combines features of sh and csh. • Modern shells (bash, ksh, dash etc.) still maintain backwards compatibility with sh. 14
  • 46.
    Graphical shells Graphical shellsprovide means for manipulating programs based on graphical user interface (GUI). Graphical shells are typically build on top of a windowing system. In case of Linux it is usually X Window System and the shell consists of an X window manager. Some examples: • KDE • GNOME • Xfce 15
  • 47.
    Bash configuration files Global: •RedHat: /etc/bashrc • Debian: /etc/bash.bashrc Personal: • ~/.bashrc 16
  • 48.
    Controlling command history $ls $ ls $ history | tail -3 498 ls 499 ls 500 history | tail -3 17
  • 49.
    Controlling command history $ls $ ls $ history | tail -3 498 ls 499 ls 500 history | tail -3 HISTCONTROL=ignoredups # ignore consecutive duplicates HISTCONTROL=ignorespace # ignore commands that start with spaces HISTCONTROL=ignoreboth # ignore both above mentioned 17
  • 50.
    Controlling command history $ls $ ls $ history | tail -3 498 ls 499 ls 500 history | tail -3 HISTCONTROL=ignoredups # ignore consecutive duplicates HISTCONTROL=ignorespace # ignore commands that start with spaces HISTCONTROL=ignoreboth # ignore both above mentioned With HISTCONTROL=ignoredups: $ ls $ ls $ history | tail -3 498 cd 499 ls 500 history | tail -3 17
  • 51.
    Controlling command history WithHISTCONTROL=ignorespace $ ls $ pwd $ history | tail -3 498 cd 499 ls 500 history | tail -3 18
  • 52.
    Controlling command history WithHISTCONTROL=ignorespace $ ls $ pwd $ history | tail -3 498 cd 499 ls 500 history | tail -3 $ grep HIST ~/.bashrc HISTCONTROL=ignoredups 18
  • 53.
    Controlling command history WithHISTCONTROL=ignorespace $ ls $ pwd $ history | tail -3 498 cd 499 ls 500 history | tail -3 $ grep HIST ~/.bashrc HISTCONTROL=ignoredups Some other options: HISTSIZE=500 # default HISTFILE=~/.bash_history # default HISTFILESIZE=500 # by default, the value of HISTSIZE 18
  • 54.
    Bash aliases $ alias aliasgrep='grep --color=always' alias less='less -R' 19
  • 55.
    Bash aliases $ alias aliasgrep='grep --color=always' alias less='less -R' $ git diff --color linux-cmd.tex | less ESC[1;33m--- a/linux-cmd.texESC[m ESC[1;33m+++ b/linux-cmd.texESC[m ESC[1;35m@@ -1,153 +1,321 @@ESC[m ESC[1;31m-documentclass{beamer}ESC[m 19
  • 56.
    Bash aliases $ alias aliasgrep='grep --color=always' alias less='less -R' $ git diff --color linux-cmd.tex | less ESC[1;33m--- a/linux-cmd.texESC[m ESC[1;33m+++ b/linux-cmd.texESC[m ESC[1;35m@@ -1,153 +1,321 @@ESC[m ESC[1;31m-documentclass{beamer}ESC[m $ git diff --color linux-cmd.tex | less -R --- a/linux-cmd.tex +++ b/linux-cmd.tex @@ -1,153 +1,321 @@ -documentclass{beamer} 19
  • 57.
    Me listing files $alias l='ls -lA --color' -l use a long listing format -A do not list implied . and .. 20
  • 58.
    Me listing files $alias l='ls -lA --color' -l use a long listing format -A do not list implied . and .. $ l -tr -t sort by modification time, newest first -r reverse order while sorting 20
  • 59.
    Me listing files $alias l='ls -lA --color' -l use a long listing format -A do not list implied . and .. $ l -tr -t sort by modification time, newest first -r reverse order while sorting $ l -tr ~/Downloads [...] linux-cmd.pdf $ 20
  • 60.
    Me listing files $alias l='ls -lA --color' -l use a long listing format -A do not list implied . and .. $ l -tr -t sort by modification time, newest first -r reverse order while sorting $ l -tr ~/Downloads [...] linux-cmd.pdf $ $ alias cl="find . -name '.#*' -o -name '*~' -o -name '#*#' , → -o -name '*.rej' -o -name '*.orig' | xargs rm -fv" $ cl removed './.#linux-cmd.tex.bak' removed './#linux-cmd.tex.bak#' 20
  • 61.
    Searching through history $history | tail -3 592 l -tr ~/Downloads 593 cd - 594 locate -i --regex 'zbxnext.*.patch' 21
  • 62.
    Searching through history $history | tail -3 592 l -tr ~/Downloads 593 cd - 594 locate -i --regex 'zbxnext.*.patch' For searching a command you typed some time ago hit Ctrl+r and type part of it that you remember: Ctrl+r <PATTERN> 21
  • 63.
    Searching through history $history | tail -3 592 l -tr ~/Downloads 593 cd - 594 locate -i --regex 'zbxnext.*.patch' For searching a command you typed some time ago hit Ctrl+r and type part of it that you remember: Ctrl+r <PATTERN> More Ctrl+r to go to next match 21
  • 64.
    Searching through history $history | tail -3 592 l -tr ~/Downloads 593 cd - 594 locate -i --regex 'zbxnext.*.patch' For searching a command you typed some time ago hit Ctrl+r and type part of it that you remember: Ctrl+r <PATTERN> More Ctrl+r to go to next match E. g.: searching for command that contained "loca": (reverse-i-search)`loca': locate -i --regex 'zbxnext.*.patch' 21
  • 65.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 22
  • 66.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 22
  • 67.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 2. Tells the terminal to pause sending data to the screen 22
  • 68.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 2. Tells the terminal to pause sending data to the screen 3. Useful if a program is spamming output too quickly (when you don’t want to Ctrl+C) 22
  • 69.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 2. Tells the terminal to pause sending data to the screen 3. Useful if a program is spamming output too quickly (when you don’t want to Ctrl+C) 4. Ctrl+Q (XON) → "resume output" 22
  • 70.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 2. Tells the terminal to pause sending data to the screen 3. Useful if a program is spamming output too quickly (when you don’t want to Ctrl+C) 4. Ctrl+Q (XON) → "resume output" 5. Tells the terminal to continue sending data after a pause 22
  • 71.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 2. Tells the terminal to pause sending data to the screen 3. Useful if a program is spamming output too quickly (when you don’t want to Ctrl+C) 4. Ctrl+Q (XON) → "resume output" 5. Tells the terminal to continue sending data after a pause • If you accidentally hit Ctrl+S, the terminal looks "frozen" but the program is still running. Pressing Ctrl+Q unfreezes it. 22
  • 72.
    XON/XOFF flow control InUnix-like terminals, Ctrl+S and Ctrl+Q are part of the classic XON/XOFF flow control. 1. Ctrl+S (XOFF) → "stop output" 2. Tells the terminal to pause sending data to the screen 3. Useful if a program is spamming output too quickly (when you don’t want to Ctrl+C) 4. Ctrl+Q (XON) → "resume output" 5. Tells the terminal to continue sending data after a pause • If you accidentally hit Ctrl+S, the terminal looks "frozen" but the program is still running. Pressing Ctrl+Q unfreezes it. • You can disable this behavior with: $ stty -ixon then Ctrl+S and Ctrl+Q won’t pause/resume output anymore 22
  • 73.
    Clearing the terminalscreen Clearing the terminal in Linux can be done in several ways, depending on what you want to achieve. 23
  • 74.
    Clearing the terminalscreen Clearing the terminal in Linux can be done in several ways, depending on what you want to achieve. • Purpose: clears the visible terminal screen • How it works: moves all previous output off-screen but doesn’t remove it from the terminal’s scrollback buffer so you can still scroll up to see it • Usage: $ clear • Shortcult: Ctrl+L 23
  • 75.
    Clearing the terminalscreen Clearing the terminal in Linux can be done in several ways, depending on what you want to achieve. • Purpose: clears the visible terminal screen • How it works: moves all previous output off-screen but doesn’t remove it from the terminal’s scrollback buffer so you can still scroll up to see it • Usage: $ clear • Shortcult: Ctrl+L Sometimes you get garbled text on the screen and clearing doesn’t help. In such case you need to fully reset the terminal. 23
  • 76.
    Clearing the terminalscreen Clearing the terminal in Linux can be done in several ways, depending on what you want to achieve. • Purpose: clears the visible terminal screen • How it works: moves all previous output off-screen but doesn’t remove it from the terminal’s scrollback buffer so you can still scroll up to see it • Usage: $ clear • Shortcult: Ctrl+L Sometimes you get garbled text on the screen and clearing doesn’t help. In such case you need to fully reset the terminal. • Purpose: reset the terminal session • How it works: resets the terminal modes, colors and settings, clears garbled text • Usage: $ cat /dev/random | head [...] • �*��+��,� reset 23
  • 77.
    Who am I? $whoami vl 24
  • 78.
    Who am I? $whoami vl $ echo $USER vl 24
  • 79.
    Who am I? $whoami vl $ echo $USER vl $ id uid=1000(vl) gid=1000(vl) groups=1000(vl),4(adm),999(docker) 24
  • 80.
    Who am I? $whoami vl $ echo $USER vl $ id uid=1000(vl) gid=1000(vl) groups=1000(vl),4(adm),999(docker) $ id -u 1000 24
  • 81.
    Who am I? $whoami vl $ echo $USER vl $ id uid=1000(vl) gid=1000(vl) groups=1000(vl),4(adm),999(docker) $ id -u 1000 $ lastlog -u vl Username Port From Latest vl pts/4 192.168.3.68 Tue Jan 16 09:40:05 24
  • 82.
    What is myShell? $ echo $SHELL /bin/bash 25
  • 83.
    What is myShell? $ echo $SHELL /bin/bash $ grep vl /etc/passwd vl:x:1000:1000:Vladimir Levijev,,,:/home/vl:/bin/bash 25
  • 84.
    What is myShell? $ echo $SHELL /bin/bash $ grep vl /etc/passwd vl:x:1000:1000:Vladimir Levijev,,,:/home/vl:/bin/bash $ chsh Password: Changing the login shell for vl Enter the new value, or press ENTER for the default Login Shell [/bin/bash]: 25
  • 85.
    Where am I? $pwd /tmp 26
  • 86.
    Where am I? $pwd /tmp $ echo $PWD /tmp 26
  • 87.
    Where am I? $pwd /tmp $ echo $PWD /tmp $ export PWD=/tmp/non-existent 26
  • 88.
    Where am I? $pwd /tmp $ echo $PWD /tmp $ export PWD=/tmp/non-existent $ pwd /home/vl/git/linux-cmd $ echo $PWD /tmp/non-existent 26
  • 89.
    Where am I? $pwd /tmp $ echo $PWD /tmp $ export PWD=/tmp/non-existent $ pwd /home/vl/git/linux-cmd $ echo $PWD /tmp/non-existent $ export PWD=$(pwd) 26
  • 90.
    Where am I? $pwd /tmp $ echo $PWD /tmp $ export PWD=/tmp/non-existent $ pwd /home/vl/git/linux-cmd $ echo $PWD /tmp/non-existent $ export PWD=$(pwd) $ export PWD=`pwd` 26
  • 91.
    Where am I? Debian: $cat /etc/timezone Europe/Riga RedHat: $ readlink /etc/localtime ../usr/share/zoneinfo/Europe/Riga 27
  • 92.
    Where am I? Debian: $cat /etc/timezone Europe/Riga RedHat: $ readlink /etc/localtime ../usr/share/zoneinfo/Europe/Riga $ timedatectl Local time: Sun 2025-10-12 20:43:49 EEST Universal time: Sun 2025-10-12 17:43:49 UTC RTC time: Sun 2025-10-12 17:43:49 Time zone: Europe/Riga (EEST, +0300) System clock synchronized: yes 27
  • 93.
    Where I justwas? $ echo $OLDPWD /etc 28
  • 94.
    Where I justwas? $ echo $OLDPWD /etc $ env | grep WD OLDPWD=/etc PWD=/home/vl 28
  • 95.
    Where I justwas? $ echo $OLDPWD /etc $ env | grep WD OLDPWD=/etc PWD=/home/vl $ cd - - is not a regular argument — it’s a special option/operand to cd that means "previous working directory" ($OLDPWD) 28
  • 96.
    Where I justwas? $ echo $OLDPWD /etc $ env | grep WD OLDPWD=/etc PWD=/home/vl $ cd - - is not a regular argument — it’s a special option/operand to cd that means "previous working directory" ($OLDPWD) $ ls - ls: cannot access '-': No such file or directory 28
  • 97.
    Where I justwas? $ echo $OLDPWD /etc $ env | grep WD OLDPWD=/etc PWD=/home/vl $ cd - - is not a regular argument — it’s a special option/operand to cd that means "previous working directory" ($OLDPWD) $ ls - ls: cannot access '-': No such file or directory $ export OLDPWD=/tmp/non-existent $ cd - -bash: cd: /tmp/non-existent: No such file or directory 28
  • 98.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" 29
  • 99.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? 29
  • 100.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? $ USERNAME=john; echo "USERNAME=$USERNAME" USERNAME=john 29
  • 101.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? $ USERNAME=john; echo "USERNAME=$USERNAME" USERNAME=john • USERNAME=john in the first case sets environment variable for echo 29
  • 102.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? $ USERNAME=john; echo "USERNAME=$USERNAME" USERNAME=john • USERNAME=john in the first case sets environment variable for echo • But before launching the command, the shell (your running shell) first parses and expands arguments USERNAME=$USERNAME. 29
  • 103.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? $ USERNAME=john; echo "USERNAME=$USERNAME" USERNAME=john • USERNAME=john in the first case sets environment variable for echo • But before launching the command, the shell (your running shell) first parses and expands arguments USERNAME=$USERNAME. • Since current shell session has no idea about this variable it expands it to USERNAME=. 29
  • 104.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? $ USERNAME=john; echo "USERNAME=$USERNAME" USERNAME=john • USERNAME=john in the first case sets environment variable for echo • But before launching the command, the shell (your running shell) first parses and expands arguments USERNAME=$USERNAME. • Since current shell session has no idea about this variable it expands it to USERNAME=. After expanding the arguments shell calls: $ USERNAME=john echo "USERNAME=" 29
  • 105.
    Environment variable glitch $USERNAME=john echo "USERNAME=$USERNAME" USERNAME= ??? $ USERNAME=john; echo "USERNAME=$USERNAME" USERNAME=john • USERNAME=john in the first case sets environment variable for echo • But before launching the command, the shell (your running shell) first parses and expands arguments USERNAME=$USERNAME. • Since current shell session has no idea about this variable it expands it to USERNAME=. After expanding the arguments shell calls: $ USERNAME=john echo "USERNAME=" $ USERNAME=john sh -c 'echo "USERNAME=$USERNAME"' USERNAME=john 29
  • 106.
    Where can Igo? Home! $ cd ~$ 30
  • 107.
    Where can Igo? Home! $ cd ~$ Alternatives: $ cd $HOME $ cd ~ the tilde ~ is not specific to cd, it’s handled by the shell itself 30
  • 108.
    Where can Igo? Home! $ cd ~$ Alternatives: $ cd $HOME $ cd ~ the tilde ~ is not specific to cd, it’s handled by the shell itself Parent directory! ~$ cd .. /home 30
  • 109.
    Where can Igo? Home! $ cd ~$ Alternatives: $ cd $HOME $ cd ~ the tilde ~ is not specific to cd, it’s handled by the shell itself Parent directory! ~$ cd .. /home Stay where you are. $ cd . /home 30
  • 110.
    Where is thatfile? $ locate zbxnext-3106.patch 31
  • 111.
    Where is thatfile? $ locate zbxnext-3106.patch Debian: $ sudo apt install plocate RedHat: $ sudo yum install mlocate 31
  • 112.
    Where is thatfile? $ locate zbxnext-3106.patch Debian: $ sudo apt install plocate RedHat: $ sudo yum install mlocate $ sudo updatedb 31
  • 113.
    Where is thatfile? $ locate zbxnext-3106.patch Debian: $ sudo apt install plocate RedHat: $ sudo yum install mlocate $ sudo updatedb /etc/cron.daily/plocate 31
  • 114.
    Where is thatfile? $ locate zbxnext-3106.patch Debian: $ sudo apt install plocate RedHat: $ sudo yum install mlocate $ sudo updatedb /etc/cron.daily/plocate $ locate -i zbxnext-3106.patch /home/vl/work/ZBXNEXT-3106/zabbix-2.2.14-ZBXNEXT-3106.patch 31
  • 115.
    Where is thatfile? $ locate zbxnext-3106.patch Debian: $ sudo apt install plocate RedHat: $ sudo yum install mlocate $ sudo updatedb /etc/cron.daily/plocate $ locate -i zbxnext-3106.patch /home/vl/work/ZBXNEXT-3106/zabbix-2.2.14-ZBXNEXT-3106.patch $ locate -i –regex ’zbxnext.*ṗatch’ /home/vl/work/zabbix-2.0.10-zbxnext-300-zbx-8992.patch /home/vl/work/ZBXNEXT-3106/zabbix-2.2.14-ZBXNEXT-3106.patch 31
  • 116.
    How locate works /var/lib/plocate/plocate.db •specific binary format • zstd compressed • faster than mlocate, might be notable on big systems with millions of files 32
  • 117.
    Listing directory permissions $ls -l /var/lib/mysql 33
  • 118.
    Listing directory permissions $ls -l /var/lib/mysql drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0 drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0 33
  • 119.
    Listing directory permissions $ls -l /var/lib/mysql drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0 drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0 $ ls -l /var/lib | grep mysql drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files -rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool -rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1 33
  • 120.
    Listing directory permissions $ls -l /var/lib/mysql drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0 drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0 $ ls -l /var/lib | grep mysql drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files -rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool -rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1 $ ls -l /var/lib | grep 'mysql$' drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql 33
  • 121.
    Listing directory permissions $ls -l /var/lib/mysql drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0 drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0 $ ls -l /var/lib | grep mysql drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files -rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool -rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1 $ ls -l /var/lib | grep 'mysql$' drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql $ ls --color -l /var/lib | grep 'mysql$' $ 33
  • 122.
    Listing directory permissions $ls -l /var/lib/mysql drwx------ 2 mysql mysql 4096 May 3 2023 zabbix6_0 drwx------ 2 mysql mysql 4096 Jun 15 2024 zabbix7_0 $ ls -l /var/lib | grep mysql drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql drwx------ 2 mysql mysql 4096 Dec 8 2016 mysql-files -rw-rw---- 1 mysql mysql 18278 Feb 27 2025 ib_buffer_pool -rw-rw---- 1 mysql mysql 66562688 Sep 22 10:45 ibdata1 $ ls -l /var/lib | grep 'mysql$' drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 mysql $ ls --color -l /var/lib | grep 'mysql$' $ $ ls -l -d --color /var/lib/mysql drwxr-xr-x 33 mysql mysql 4096 Sep 22 14:01 /var/lib/mysql -d, --directory list directories themselves, not their contents 33
  • 123.
    Securing files $ echopass123 > secret.pw -rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw 34
  • 124.
    Securing files $ echopass123 > secret.pw -rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw $ umask 0022 34
  • 125.
    Securing files $ echopass123 > secret.pw -rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw $ umask 0022 New files : 666 – 022 = 644 New directories : 777 – 022 = 755 34
  • 126.
    Securing files $ echopass123 > secret.pw -rw-r––r–– 1 vl adm 8 Jan 25 16:35 secret.pw $ umask 0022 New files : 666 – 022 = 644 New directories : 777 – 022 = 755 The first digit in a umask value (if present) controls special permission bits: • 0 — no special restrictions • 1 — disables the sticky bit • 2 — disables the setgid bit • 4 — disables the setuid bit These bits are rarely masked, so the first digit is usually 0. 34
  • 127.
  • 128.
    Securing files $ umask0266 New files : 666 – 266 = 400 $ touch /tmp/foo $ l /tmp/foo -r-------- 1 root root 0 Oct 11 23:30 /tmp/foo 35
  • 129.
    Securing files $ umask0266 New files : 666 – 266 = 400 $ touch /tmp/foo $ l /tmp/foo -r-------- 1 root root 0 Oct 11 23:30 /tmp/foo $ chmod 600 secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw 35
  • 130.
    Securing files $ umask0266 New files : 666 – 266 = 400 $ touch /tmp/foo $ l /tmp/foo -r-------- 1 root root 0 Oct 11 23:30 /tmp/foo $ chmod 600 secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw $ chmod u=rw,g=,o= secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw 35
  • 131.
    Securing files $ umask0266 New files : 666 – 266 = 400 $ touch /tmp/foo $ l /tmp/foo -r-------- 1 root root 0 Oct 11 23:30 /tmp/foo $ chmod 600 secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw $ chmod u=rw,g=,o= secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw $ chmod g+r secret.pw -rw-r----- 1 vl adm 8 Jan 25 16:35 secret.pw 35
  • 132.
    Securing files $ umask0266 New files : 666 – 266 = 400 $ touch /tmp/foo $ l /tmp/foo -r-------- 1 root root 0 Oct 11 23:30 /tmp/foo $ chmod 600 secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw $ chmod u=rw,g=,o= secret.pw -rw------- 1 vl adm 8 Jan 25 16:35 secret.pw $ chmod g+r secret.pw -rw-r----- 1 vl adm 8 Jan 25 16:35 secret.pw $ chmod g-r secret.pw -rw------- 1 vl adm 8 Jan 25 18:47 secret.pw 35
  • 133.
    Appending to afile $ l secret.pw -rw------- 1 root vl 8 Oct 1 18:47 secret.pw 36
  • 134.
    Appending to afile $ l secret.pw -rw------- 1 root vl 8 Oct 1 18:47 secret.pw $ echo foobar >> secret.pw -bash: secret.pw: Permission denied 36
  • 135.
    Appending to afile $ l secret.pw -rw------- 1 root vl 8 Oct 1 18:47 secret.pw $ echo foobar >> secret.pw -bash: secret.pw: Permission denied $ sudo echo foobar >> secret.pw -bash: secret.pw: Permission denied 36
  • 136.
    Appending to afile $ l secret.pw -rw------- 1 root vl 8 Oct 1 18:47 secret.pw $ echo foobar >> secret.pw -bash: secret.pw: Permission denied $ sudo echo foobar >> secret.pw -bash: secret.pw: Permission denied Who handles the redirection? • The >> secret.pw redirection is handled by your shell, before sudo is applied. • sudo only applies to echo, not to the redirection. 36
  • 137.
    Appending to afile $ echo foobar | sudo tee -a secret.pw foobar -a, --append append to the given FILEs, do not overwrite 37
  • 138.
    Appending to afile $ echo foobar | sudo tee -a secret.pw foobar -a, --append append to the given FILEs, do not overwrite $ sudo cat secret.pw pass123 foobar 37
  • 139.
    File attributes In Linux,file attributes are special flags that control additional behaviors of files and directories beyond the normal read, write, and execute permissions. These are supported on ext2, ext3, ext4, and some other file systems. Key points: • Independent of permissions • Persistent at the filesystem level • Attributes control: • Modification • Access time updates • Deletion behavior • Compression • Analogy: • Permissions = keys to the cabinet with documents • Attributes = seals, instructions on the document 38
  • 140.
    Securing a file $lsattr /etc/passwd --------------e------- /etc/passwd 39
  • 141.
    Securing a file $lsattr /etc/passwd --------------e------- /etc/passwd $ echo -n --------------e------- | wc -c 22 39
  • 142.
    Securing a file $lsattr /etc/passwd --------------e------- /etc/passwd $ echo -n --------------e------- | wc -c 22 A file with the ’i’ attribute cannot be modified: it cannot be deleted or re- named, no link can be created to this file. 39
  • 143.
    Securing a file $lsattr /etc/passwd --------------e------- /etc/passwd $ echo -n --------------e------- | wc -c 22 A file with the ’i’ attribute cannot be modified: it cannot be deleted or re- named, no link can be created to this file. $ sudo chattr +i /etc/passwd $ lsattr /etc/passwd ----i---------e------- /etc/passwd 39
  • 144.
    Securing a file $lsattr /etc/passwd --------------e------- /etc/passwd $ echo -n --------------e------- | wc -c 22 A file with the ’i’ attribute cannot be modified: it cannot be deleted or re- named, no link can be created to this file. $ sudo chattr +i /etc/passwd $ lsattr /etc/passwd ----i---------e------- /etc/passwd $ sudo useradd -M -r -s /usr/sbin/nologin tempuser useradd: cannot open /etc/passwd 39
  • 145.
    Securing a file $lsattr /etc/passwd --------------e------- /etc/passwd $ echo -n --------------e------- | wc -c 22 A file with the ’i’ attribute cannot be modified: it cannot be deleted or re- named, no link can be created to this file. $ sudo chattr +i /etc/passwd $ lsattr /etc/passwd ----i---------e------- /etc/passwd $ sudo useradd -M -r -s /usr/sbin/nologin tempuser useradd: cannot open /etc/passwd $ sudo chattr -i /etc/passwd 39
  • 146.
    Securing a file $whoami vl $ touch /tmp/foo $ chattr +i /tmp/foo Pchattr: Operation not permitted while setting flags on /tmp/foo 40
  • 147.
    Common filesystem attributes AttributeEffect i Immutable: cannot modify, delete, or rename a Append-only: can only add data A No atime updates: read does not change access time c Compressed: filesystem handles compression s Secure deletion: wiped securely when deleted 41
  • 148.
    Common filesystem attributes AttributeEffect i Immutable: cannot modify, delete, or rename a Append-only: can only add data A No atime updates: read does not change access time c Compressed: filesystem handles compression s Secure deletion: wiped securely when deleted $ sudo tune2fs -l /dev/sda3 | grep 'Filesystem features' Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum , → , → , → $ sudo tune2fs -l /dev/sda3 | grep 'Filesystem features' | grep compress , → $ 41
  • 149.
    What is ascript?
  • 150.
    Long commands Sometimes thecommand may become really long... $ while true; do output="$(pgrep -lf zabbix_server)"; if [ -n "$output" ]; then echo Zabbix server is running...; fi; sleep 3; done , → , → Zabbix server is running... Zabbix server is running... 42
  • 151.
    Long commands So youneed to start writing scripts! 43
  • 152.
    A shebang #!/bin/bash echo "Cleaning/tmp..." rm -rfv /tmp/* 44
  • 153.
    A shebang #!/bin/bash echo "Cleaning/tmp..." rm -rfv /tmp/* A shebang is the special character sequence #! at the very beginning of a script file, followed by the path to the interpreter that should be used to execute the script. 44
  • 154.
    A shebang #!/bin/bash echo "Cleaning/tmp..." rm -rfv /tmp/* A shebang is the special character sequence #! at the very beginning of a script file, followed by the path to the interpreter that should be used to execute the script. It’s also known as a hashbang or hash-bang, and it allows a script to be run directly as an executable in Unix-like systems by telling the system which program (like a shell or Python interpreter) to use. 44
  • 155.
    A bash shebang echo"Cleaning /tmp..." rm -rf /tmp/* 45
  • 156.
    A bash shebang echo"Cleaning /tmp..." rm -rf /tmp/* $ bash cleanup.sh 45
  • 157.
    A bash shebang echo"Cleaning /tmp..." rm -rf /tmp/* $ bash cleanup.sh $ ./cleanup.sh -bash: ./cleanup.sh: Permission denied 45
  • 158.
    A bash shebang echo"Cleaning /tmp..." rm -rf /tmp/* $ bash cleanup.sh $ ./cleanup.sh -bash: ./cleanup.sh: Permission denied $ chmod +x cleanup.sh 45
  • 159.
    A bash shebang echo"Cleaning /tmp..." rm -rf /tmp/* $ bash cleanup.sh $ ./cleanup.sh -bash: ./cleanup.sh: Permission denied $ chmod +x cleanup.sh $ ./cleanup.sh Cleaning /tmp... $ 45
  • 160.
    A bash shebang echo"Cleaning /tmp..." rm -rf /tmp/* $ bash cleanup.sh $ ./cleanup.sh -bash: ./cleanup.sh: Permission denied $ chmod +x cleanup.sh $ ./cleanup.sh Cleaning /tmp... $ When a script without a shebang is executed directly (e.g., ./cleanup.sh) and the current shell is Bash, the kernel will attempt to execute the file using the current shell. 45
  • 161.
    A bash shebang Considera simple perl script. print("I am $ENV{USER}n"); 46
  • 162.
    A bash shebang Considera simple perl script. print("I am $ENV{USER}n"); $ /tmp/echo.pl -bash: /tmp/echo.pl: Permission denied 46
  • 163.
    A bash shebang Considera simple perl script. print("I am $ENV{USER}n"); $ /tmp/echo.pl -bash: /tmp/echo.pl: Permission denied $ chmod +x /tmp/echo.pl 46
  • 164.
    A bash shebang Considera simple perl script. print("I am $ENV{USER}n"); $ /tmp/echo.pl -bash: /tmp/echo.pl: Permission denied $ chmod +x /tmp/echo.pl $ /tmp/echo.pl /tmp/echo.pl: line 1: syntax error near unexpected token `"I am $ENV{USER}n"' 46
  • 165.
    A bash shebang Considera simple perl script. print("I am $ENV{USER}n"); $ /tmp/echo.pl -bash: /tmp/echo.pl: Permission denied $ chmod +x /tmp/echo.pl $ /tmp/echo.pl /tmp/echo.pl: line 1: syntax error near unexpected token `"I am $ENV{USER}n"' $ perl /tmp/echo.pl I am vl 46
  • 166.
    A shebang Bash scriptscommonly start with #!/bin/bash 47
  • 167.
    A shebang Bash scriptscommonly start with #!/bin/bash On many systems (Linux, macOS, BSDs, NixOS, embedded systems, custom setups), bash might be installed elsewhere (e.g. /usr/local/bin/bash, /opt/homebrew/bin/bash). If bash isn’t at /bin/bash, your script won’t run. 47
  • 168.
    A shebang Bash scriptscommonly start with #!/bin/bash On many systems (Linux, macOS, BSDs, NixOS, embedded systems, custom setups), bash might be installed elsewhere (e.g. /usr/local/bin/bash, /opt/homebrew/bin/bash). If bash isn’t at /bin/bash, your script won’t run. Start you bash code with: #!/usr/bin/env bash 47
  • 169.
    A shebang Bash scriptscommonly start with #!/bin/bash On many systems (Linux, macOS, BSDs, NixOS, embedded systems, custom setups), bash might be installed elsewhere (e.g. /usr/local/bin/bash, /opt/homebrew/bin/bash). If bash isn’t at /bin/bash, your script won’t run. Start you bash code with: #!/usr/bin/env bash • On almost all modern Unix-like systems (Linux, macOS, BSD, etc.), env is indeed located at /usr/bin/env • That’s why it became the de facto portable way of locating interpreters (e.g. bash, python3, perl) • The POSIX standard requires env, but it does not require it to live in /usr/bin — only that it be somewhere in the user’s $PATH 47
  • 170.
    Hello, world! #!/usr/bin/env bash echo'Hello, world!' $ ./hello.sh Hello, world! 48
  • 171.
    Hello, world! #!/usr/bin/env bash echo'Hello, world!' $ ./hello.sh Hello, world! Create a backup script that: 1. Takes 2 arguments: file to back up and target directory 2. Target directory is optional, default $HOME/backup 3. If target directory does not exist - create it. 48
  • 172.
  • 173.
    Reading command-line arguments #!/usr/bin/envbash if [ $1 == "" ]; then 49
  • 174.
    Reading command-line arguments #!/usr/bin/envbash if [ $1 == "" ]; then # ./backup.sh /etc/resolv.conf if [ /etc/resolv.conf == "" ]; then 49
  • 175.
    Reading command-line arguments #!/usr/bin/envbash if [ $1 == "" ]; then # ./backup.sh /etc/resolv.conf if [ /etc/resolv.conf == "" ]; then # ./backup.sh if [ == "" ]; then ./backup.sh: line 4: [: =: unary operator expected 49
  • 176.
    Reading command-line arguments #!/usr/bin/envbash if [ $1 == "" ]; then # ./backup.sh /etc/resolv.conf if [ /etc/resolv.conf == "" ]; then # ./backup.sh if [ == "" ]; then ./backup.sh: line 4: [: =: unary operator expected Variant What it does Safe? [ -z $1 ] Works, but may break with special characters. ✗ [ -z "$1" ] Correct, safe way to check for an empty argument. ✓ 49
  • 177.
  • 178.
  • 179.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" 50
  • 180.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" exit 1 fi 50
  • 181.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" exit 1 fi FILE="$1" 50
  • 182.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" exit 1 fi FILE="$1" BACKUP_DIR="$2" 50
  • 183.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" exit 1 fi FILE="$1" BACKUP_DIR="$2" if [ -z "$BACKUP_DIR" ]; then # default backup directory BACKUP_DIR="$HOME/backup" fi 50
  • 184.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" exit 1 fi FILE="$1" BACKUP_DIR="$2" if [ -z "$BACKUP_DIR" ]; then # default backup directory BACKUP_DIR="$HOME/backup" fi # create backup directory if it doesn’t exist mkdir -p "$BACKUP_DIR" 50
  • 185.
    Reading command-line arguments #!/usr/bin/envbash if [ -z "$1" ]; then echo "Usage: $0 <file> [backup directory]" echo "default backup directory - $HOME/backup" exit 1 fi FILE="$1" BACKUP_DIR="$2" if [ -z "$BACKUP_DIR" ]; then # default backup directory BACKUP_DIR="$HOME/backup" fi # create backup directory if it doesn’t exist mkdir -p "$BACKUP_DIR" cp "$FILE" "$BACKUP_DIR/" echo "Backup of ’$FILE’ saved to ’$BACKUP_DIR/’" 50
  • 186.
    Reading command-line arguments $./backup.sh Usage: ./backup.sh <file> [backup directory] default backup directory - /home/vl/backup 51
  • 187.
    Reading command-line arguments $./backup.sh Usage: ./backup.sh <file> [backup directory] default backup directory - /home/vl/backup $ echo $? $ 1 51
  • 188.
    Reading command-line arguments $./backup.sh Usage: ./backup.sh <file> [backup directory] default backup directory - /home/vl/backup $ echo $? $ 1 $ ./backup.sh 2> /dev/null || echo "Backup failed on $(date --rfc-3339=date)"'!' , → Backup failed on 2025-09-30! 51
  • 189.
    Reading command-line arguments $./backup.sh Usage: ./backup.sh <file> [backup directory] default backup directory - /home/vl/backup $ echo $? $ 1 $ ./backup.sh 2> /dev/null || echo "Backup failed on $(date --rfc-3339=date)"'!' , → Backup failed on 2025-09-30! $ ./backup.sh backup.sh Backup of 'backup.sh' saved to '/home/vl/backup/' 51
  • 190.
    Reading command-line arguments $./backup.sh Usage: ./backup.sh <file> [backup directory] default backup directory - /home/vl/backup $ echo $? $ 1 $ ./backup.sh 2> /dev/null || echo "Backup failed on $(date --rfc-3339=date)"'!' , → Backup failed on 2025-09-30! $ ./backup.sh backup.sh Backup of 'backup.sh' saved to '/home/vl/backup/' $ ./backup.sh backup.sh /tmp Backup of 'backup.sh' saved to '/tmp/' $ ls ~/backup/backup.sh /tmp/backup.sh /home/vl/backup/backup.sh /tmp/backup.sh 51
  • 191.
    Error handling $ ./backup.shnon-existent.txt cp: cannot stat 'non-existent.txt': No such file or directory Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’ 52
  • 192.
    Error handling $ ./backup.shnon-existent.txt cp: cannot stat 'non-existent.txt': No such file or directory Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’ cp "$FILE" "$BACKUP_DIR/" if [ $? -ne 0 ]; then echo "An error occured, see above." exit 1 fi 52
  • 193.
    Error handling $ ./backup.shnon-existent.txt cp: cannot stat 'non-existent.txt': No such file or directory Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’ cp "$FILE" "$BACKUP_DIR/" if [ $? -ne 0 ]; then echo "An error occured, see above." exit 1 fi cp: cannot stat 'non-existent.txt': No such file or directory , → An error occured, see above. 52
  • 194.
    Error handling $ ./backup.shnon-existent.txt cp: cannot stat 'non-existent.txt': No such file or directory Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’ cp "$FILE" "$BACKUP_DIR/" if [ $? -ne 0 ]; then echo "An error occured, see above." exit 1 fi cp: cannot stat 'non-existent.txt': No such file or directory , → An error occured, see above. if ! cp "$FILE" "$BACKUP_DIR/"; then exit 1 fi 52
  • 195.
    Error handling $ ./backup.shnon-existent.txt cp: cannot stat 'non-existent.txt': No such file or directory Backup of ’non-existent.txt’ saved to ’/home/vl/backup/’ cp "$FILE" "$BACKUP_DIR/" if [ $? -ne 0 ]; then echo "An error occured, see above." exit 1 fi cp: cannot stat 'non-existent.txt': No such file or directory , → An error occured, see above. if ! cp "$FILE" "$BACKUP_DIR/"; then exit 1 fi cp: cannot stat 'non-existent.txt': No such file or directory , → 52
  • 196.
    Error handling if !mkdir -p "$BACKUP_DIR/"; then exit 1 fi if ! cp "$FILE" "$BACKUP_DIR/"; then exit 1 fi if ! ls "$BACKUP_DIR/$(basename $FILE)" > /dev/null; then , → exit 1 fi 53
  • 197.
    Error handling if !mkdir -p "$BACKUP_DIR/"; then exit 1 fi if ! cp "$FILE" "$BACKUP_DIR/"; then exit 1 fi if ! ls "$BACKUP_DIR/$(basename $FILE)" > /dev/null; then , → exit 1 fi mkdir -p "$BACKUP_DIR/" || exit 1 cp "$FILE" "$BACKUP_DIR/" || exit 1 ls "$BACKUP_DIR/$(basename FILE)" > /dev/null || exit 1 53
  • 198.
  • 199.
    Error handling #!/usr/bin/env bash set-e #!/usr/bin/env bash set -e mkdir -p "$BACKUP_DIR/" cp "$FILE" "$BACKUP_DIR/" ls "$BACKUP_DIR/$(basename FILE)" 54
  • 200.
    Error handling #!/usr/bin/env bash set-e #!/usr/bin/env bash set -e mkdir -p "$BACKUP_DIR/" cp "$FILE" "$BACKUP_DIR/" ls "$BACKUP_DIR/$(basename FILE)" $ ./backup.sh backup.sh mkdir: cannot create directory ‘/home/vl/backup/’: Permission denied , → $ echo $? 1 54
  • 201.
    Error handling #!/usr/bin/env bash set-e false | true echo "continue with the script" 55
  • 202.
    Error handling #!/usr/bin/env bash set-e false | true echo "continue with the script" $ ./test.sh continue with the script 55
  • 203.
    Error handling #!/usr/bin/env bash set-e false | true echo "continue with the script" $ ./test.sh continue with the script #!/usr/bin/env bash set -e set -o pipefail false | true echo "continue with the script" 55
  • 204.
    Error handling #!/usr/bin/env bash set-e false | true echo "continue with the script" $ ./test.sh continue with the script #!/usr/bin/env bash set -e set -o pipefail false | true echo "continue with the script" $ ./test.sh $ echo $? 1 55
  • 205.
    Debugging $ ./backup.sh backup.sh"/opt/backup dir" ./backup.sh: line 12: [: /opt/backup: binary operator expected , → 56
  • 206.
    Debugging $ ./backup.sh backup.sh"/opt/backup dir" ./backup.sh: line 12: [: /opt/backup: binary operator expected , → 12: if [ -z $BACKUP_DIR ]; then 13: BACKUP_DIR="$HOME/backup" 14: fi 56
  • 207.
    Debugging $ ./backup.sh backup.sh"/opt/backup dir" ./backup.sh: line 12: [: /opt/backup: binary operator expected , → 12: if [ -z $BACKUP_DIR ]; then 13: BACKUP_DIR="$HOME/backup" 14: fi set -x if [ -z $BACKUP_DIR ]; then BACKUP_DIR="$HOME/backup" fi set +x 56
  • 208.
    Debugging $ ./backup.sh backup.sh"/opt/backup dir" ./backup.sh: line 12: [: /opt/backup: binary operator expected , → 12: if [ -z $BACKUP_DIR ]; then 13: BACKUP_DIR="$HOME/backup" 14: fi set -x if [ -z $BACKUP_DIR ]; then BACKUP_DIR="$HOME/backup" fi set +x $ ./backup.sh backup.sh "/opt/backup dir" + '[' -z /opt/backup dir ']' ./backup.sh: line 12: [: /opt/backup: binary operator expected + set +x 56
  • 209.
    Functions dbg() { [ "$DEBUG" ="1" ] || return echo "DBG: $*" } msg() { echo "MSG: $*" } err() { echo "ERR: $*" exit 1 } 57
  • 210.
    Functions dbg() { [ "$DEBUG" ="1" ] || return echo "DBG: $*" } msg() { echo "MSG: $*" } err() { echo "ERR: $*" exit 1 } • Less lines • Consistency 57
  • 211.
    Catching signals #!/usr/bin/env bash TMP_FILE="$(mktemp)"|| exit 1 cleanup() { echo "Cleaning up..." rm -fv "$TMP_FILE" exit $1 } trap "cleanup 1" INT TERM [...] cleanup 58
  • 212.
    Built-in string operations Ifyou use bash 4 or later, don’t bother with ’tr’ and ’expr’: $ str=Shell; echo $str | tr [a-z] [A-Z] SHELL 59
  • 213.
    Built-in string operations Ifyou use bash 4 or later, don’t bother with ’tr’ and ’expr’: $ str=Shell; echo $str | tr [a-z] [A-Z] SHELL Use bash built-in string operations power: str=shell; echo ${str^}; echo ${str^^}; echo ${str„} Shell SHELL shell 59
  • 214.
    Built-in string operations ${variable#pattern} Removesthe shortest match of pattern from the beginning. ${variable##pattern} Removes the longest match of pattern from the beginning. ${variable%pattern} Removes the shortest match of pattern from the end. ${variable%%pattern} Removes the longest match of pattern from the end. 60
  • 215.
    Built-in string operations ${variable#pattern} Removesthe shortest match of pattern from the beginning. ${variable##pattern} Removes the longest match of pattern from the beginning. ${variable%pattern} Removes the shortest match of pattern from the end. ${variable%%pattern} Removes the longest match of pattern from the end. Example: filename="report.2025.txt" echo "${filename%.*}" # Output: report.2025 echo "${filename##*.}" # Output: txt 60
  • 216.
    Brace expansion Brace expansionin Bash is a feature that generates strings from patterns without loops or external commands. 61
  • 217.
    Brace expansion Brace expansionin Bash is a feature that generates strings from patterns without loops or external commands. Ranges: $ echo file{1..3}.txt file1.txt file2.txt file3.txt 61
  • 218.
    Brace expansion Brace expansionin Bash is a feature that generates strings from patterns without loops or external commands. Ranges: $ echo file{1..3}.txt file1.txt file2.txt file3.txt Lists: $ echo {A,B,C} A B C 61
  • 219.
    Brace expansion Brace expansionin Bash is a feature that generates strings from patterns without loops or external commands. Ranges: $ echo file{1..3}.txt file1.txt file2.txt file3.txt Lists: $ echo {A,B,C} A B C $ rm -fv server.c.{orig,rej,bak} # translates to -> rm -fv server.c.orig server.c.rej server.c.bak removed 'server.c.bak' 61
  • 220.
  • 221.
  • 222.
    Randomize $ echo $[RANDOM] 11419 $[ $[RANDOM % 2] -eq 0 ] && echo -n Gleb || echo -n dimir; echo will go to India'!' , → dimir will go to India! 62
  • 223.
    Randomize $ echo $[RANDOM] 11419 $[ $[RANDOM % 2] -eq 0 ] && echo -n Gleb || echo -n dimir; echo will go to India'!' , → dimir will go to India! $ sleep $(( RANDOM % 5 + 1 )) # sleep for 1–5 seconds 62
  • 224.
    Randomize $ echo $[RANDOM] 11419 $[ $[RANDOM % 2] -eq 0 ] && echo -n Gleb || echo -n dimir; echo will go to India'!' , → dimir will go to India! $ sleep $(( RANDOM % 5 + 1 )) # sleep for 1–5 seconds This is called bash arithmetic evaluation. • (( )) evaluates arithmetic expressions • $(( )) expands to the numeric result • Works with standard arithmetic operators (+, -, *, /, %, ++, --, etc.) • No need for $ before variable names inside (( )) 62
  • 225.
    Arithmetic evaluation $ ((2 + 3 )) $ echo $? 0 63
  • 226.
    Arithmetic evaluation $ ((2 + 3 )) $ echo $? 0 $ (( 2 - 2 )) $ echo $? 1 63
  • 227.
    Arithmetic evaluation $ ((2 + 3 )) $ echo $? 0 $ (( 2 - 2 )) $ echo $? 1 $ a=5 $ (( a = a + 2 )) $ echo $a 7 63
  • 228.
    Arithmetic evaluation $ ((2 + 3 )) $ echo $? 0 $ (( 2 - 2 )) $ echo $? 1 $ a=5 $ (( a = a + 2 )) $ echo $a 7 $ rv=$(( 5 + 5 )) $ echo $rv 10 63
  • 229.
    Wildcard expansion $ ls* | grep bashrc $ ls .* | grep bashrc .bashrc 64
  • 230.
    Wildcard expansion $ ls* | grep bashrc $ ls .* | grep bashrc .bashrc Make sure you never delete hidden files like this: $ rm -rf .* because ".*" matches ".." (higher-level directory)! $ ls -R .* | grep bashrc bash.bashrc bash.bashrc bash.bashrc bash.bashrc 64
  • 231.
    Wildcard expansion $ ls* | grep bashrc $ ls .* | grep bashrc .bashrc Make sure you never delete hidden files like this: $ rm -rf .* because ".*" matches ".." (higher-level directory)! $ ls -R .* | grep bashrc bash.bashrc bash.bashrc bash.bashrc bash.bashrc Correct way of doing this: $ rm -rfv .[a-zA-Z]* 64
  • 232.
    Following the logfile Log file rotated by logrotate. $ tail -f /var/log/zabbix_server.log 29631:20160927:152455.721 In substitute_functions() 29631:20160927:152455.721 In zbx_extract_functionids() tr_num:1 , → tail: /tmp/foo.log: file truncated 65
  • 233.
    Following the logfile Log file rotated by logrotate. $ tail -f /var/log/zabbix_server.log 29631:20160927:152455.721 In substitute_functions() 29631:20160927:152455.721 In zbx_extract_functionids() tr_num:1 , → tail: /tmp/foo.log: file truncated $ tail -F /var/log/zabbix_server.log 29631:20160927:152455.721 In substitute_functions() 29631:20160927:152455.721 In zbx_extract_functionids() tr_num:1 tail: /tmp/foo.log: file truncated 29631:20160927:152455.721 End of zbx_extract_functionids() functionids_num:1 29631:20160927:152455.721 In zbx_populate_function_items() functionids_num:1 29631:20160927:152455.721 End of zbx_populate_function_items() ifuncs_num:1 29631:20160927:152455.721 In zbx_evaluate_item_functions() ifuncs_num:1 29631:20160927:152455.721 In evaluate_function() function:'Zabbix server:test.strle 29631:20160927:152455.721 In evaluate_STRLEN() 29631:20160927:152455.721 In evaluate_LAST() 65
  • 234.
    History expansion The Historylibrary provides a history expansion feature that is similar to the history expansion provided by csh. It is used to manipulate the history information. The following are event designators. It is a reference to a command line entry in the history list !^ first argument !$ last argument !* all arguments !:2 second argument !:2-3 second to third arguments !:2-$ second to last arguments !:2* second to last arguments !:2- second to next to last arguments !:0 the command itself !! repeat the previous line 66
  • 235.
    History expansion $ mkdir-p /usr/local/bin 67
  • 236.
    History expansion $ mkdir-p /usr/local/bin $ cp backup.sh !$ cp backup.sh /usr/local/bin 67
  • 237.
    History expansion $ mkdir-p /usr/local/bin $ cp backup.sh !$ cp backup.sh /usr/local/bin $ chmod +x !$/backup.sh chmod +x /usr/local/bin/backup.sh 67
  • 238.
    History expansion $ mkdir-p /usr/local/bin $ cp backup.sh !$ cp backup.sh /usr/local/bin $ chmod +x !$/backup.sh chmod +x /usr/local/bin/backup.sh $ !$ list.pl /usr/local/bin/backup.sh list.pl Backup of 'list.pl' saved to '/home/vl/backup/' 67
  • 239.
    History expansion $ ls/var/log/mysql/error.log /var/log/mysql.err /var/log/mysql.err /var/log/mysql/error.log 68
  • 240.
    History expansion $ ls/var/log/mysql/error.log /var/log/mysql.err /var/log/mysql.err /var/log/mysql/error.log $ cp !:1 /backup/ cp /var/log/mysql/error.log /backup/ 68
  • 241.
    History expansion $ ls/var/log/mysql/error.log /var/log/mysql.err /var/log/mysql.err /var/log/mysql/error.log $ cp !:1 /backup/ cp /var/log/mysql/error.log /backup/ $ apt install openssh-server build-essential binutils make pkg-config libldns-dev automake libssl-dev emacs-nox php-mbstring php-bcmath php-gd php-ldap php-mysql php-curl locales python3 faketime libnet-dns-perl less wget curl tmux git jq , → , → , → , → E: Could not open [...] (13: Permission denied) 68
  • 242.
    History expansion $ ls/var/log/mysql/error.log /var/log/mysql.err /var/log/mysql.err /var/log/mysql/error.log $ cp !:1 /backup/ cp /var/log/mysql/error.log /backup/ $ apt install openssh-server build-essential binutils make pkg-config libldns-dev automake libssl-dev emacs-nox php-mbstring php-bcmath php-gd php-ldap php-mysql php-curl locales python3 faketime libnet-dns-perl less wget curl tmux git jq , → , → , → , → E: Could not open [...] (13: Permission denied) $ sudo !! sudo apt install [...] 68
  • 243.
    End of command-lineoptions $ diff -ur master master.new | grep --- 69
  • 244.
    End of command-lineoptions $ diff -ur master master.new | grep --- grep: unrecognized option '---' Try 'grep --help' for more information. 69
  • 245.
    End of command-lineoptions $ diff -ur master master.new | grep --- grep: unrecognized option '---' Try 'grep --help' for more information. $ diff -ur master master.new | grep '---' --- master/src/libs/zbxservice/service.c 2025-10-10 08:01:16.942685012 +0000 , → --- master/src/zabbix_server/server.c 2025-10-10 08:01:16.944685012 +0000 , → 69
  • 246.
    End of command-lineoptions $ diff -ur master master.new | grep --- grep: unrecognized option '---' Try 'grep --help' for more information. $ diff -ur master master.new | grep '---' --- master/src/libs/zbxservice/service.c 2025-10-10 08:01:16.942685012 +0000 , → --- master/src/zabbix_server/server.c 2025-10-10 08:01:16.944685012 +0000 , → $ diff -ur master master.new | grep –– --- --- a/src/libs/zbxservice/service.c --- a/src/zabbix_server/server.c -- stop reading command-line options 69
  • 247.
    End of command-lineoptions $ diff -ur master master.new | grep --- grep: unrecognized option '---' Try 'grep --help' for more information. $ diff -ur master master.new | grep '---' --- master/src/libs/zbxservice/service.c 2025-10-10 08:01:16.942685012 +0000 , → --- master/src/zabbix_server/server.c 2025-10-10 08:01:16.944685012 +0000 , → $ diff -ur master master.new | grep –– --- --- a/src/libs/zbxservice/service.c --- a/src/zabbix_server/server.c -- stop reading command-line options $ man grep | grep -- -R -R, --dereference-recursive 69
  • 248.
  • 249.
  • 250.
    Viewing diff $ aptinstall dwdiff 72
  • 251.
    Viewing diff $ aptinstall dwdiff $ cat /usr/local/bin/vldiff #!/usr/bin/env bash dwdiff --color=bred,bgreen -d ',;&*()[]{}=".' --diff-input $@ 72
  • 252.
    Viewing diff $ aptinstall dwdiff $ cat /usr/local/bin/vldiff #!/usr/bin/env bash dwdiff --color=bred,bgreen -d ',;&*()[]{}=".' --diff-input $@ $ git diff | vldiff 72
  • 253.
    grep not working $ssh | grep --color bind_address usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address] , → [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-J destination] [-L address] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-P tag] [-p port] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] destination [command [argument ...]] ssh [-Q query_option] 73
  • 254.
    grep not working $ssh | grep --color bind_address usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address] , → [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-J destination] [-L address] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-P tag] [-p port] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] destination [command [argument ...]] ssh [-Q query_option] $ ssh 2>&1 | grep --color bind_address usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address] 73
  • 255.
    Working with processes $ps ax | grep zabbix_proxy 28559 ? S 0:00 sbin/zabbix_proxy 28560 ? S 0:00 sbin/zabbix_proxy 74
  • 256.
    Working with processes $ps ax | grep zabbix_proxy 28559 ? S 0:00 sbin/zabbix_proxy 28560 ? S 0:00 sbin/zabbix_proxy $ pgrep zabbix_proxy 28559 28560 74
  • 257.
    Working with processes $ps ax | grep zabbix_proxy 28559 ? S 0:00 sbin/zabbix_proxy 28560 ? S 0:00 sbin/zabbix_proxy $ pgrep zabbix_proxy 28559 28560 $ pgrep -la zabbix_proxy 28559 sbin/zabbix_proxy -c /etc/zabbix/zabbix_proxy.conf 28560 sbin/zabbix_proxy: configuration syncer [syncing configuration] -l, --list-name List the process name as well as the process ID (pgrep only) -a, --list-full List the full command line as well as the process ID (pgrep only) 74
  • 258.
    Working with processes $pgrep -lfa poller 28564 sbin/zabbix_proxy: browser poller 28573 sbin/zabbix_proxy: agent poller 28575 sbin/zabbix_proxy: snmp poller -f, --full The pattern is matched against the full command line 75
  • 259.
    Working with processes $pgrep -lfa poller 28564 sbin/zabbix_proxy: browser poller 28573 sbin/zabbix_proxy: agent poller 28575 sbin/zabbix_proxy: snmp poller -f, --full The pattern is matched against the full command line $ pkill -e zabbix_proxy zabbix_proxy killed (pid 31152) zabbix_proxy killed (pid 31153) 75
  • 260.
    Working with processes $pgrep -lfa poller 28564 sbin/zabbix_proxy: browser poller 28573 sbin/zabbix_proxy: agent poller 28575 sbin/zabbix_proxy: snmp poller -f, --full The pattern is matched against the full command line $ pkill -e zabbix_proxy zabbix_proxy killed (pid 31152) zabbix_proxy killed (pid 31153) -e, --echo Display name and PID of the process being killed (pkill only) 75
  • 261.
    Working with processes $pgrep -lfa poller 28564 sbin/zabbix_proxy: browser poller 28573 sbin/zabbix_proxy: agent poller 28575 sbin/zabbix_proxy: snmp poller -f, --full The pattern is matched against the full command line $ pkill -e zabbix_proxy zabbix_proxy killed (pid 31152) zabbix_proxy killed (pid 31153) -e, --echo Display name and PID of the process being killed (pkill only) $ pkill -fe poller zabbix_proxy killed (pid 31656) zabbix_proxy killed (pid 31661) 75
  • 262.
    Thank you Thank youfor listening! Questions? 76