SlideShare ist ein Scribd-Unternehmen logo
1 von 27
Downloaden Sie, um offline zu lesen
BASH
BASH
is not a second zone citizen programming language
is not a second zone citizen programming language
Agile Grenoble
Agile Grenoble
November 2019
November 2019
2 words about me
2 words about me
$ cat ~/.bash_profile
NAME="René Ribaud"
UPTIME=44
source ./1998.sh
PROFESSION="Unices system and storage" 
"administrator , since 2003"
HISTORY="Lots (too many) infrastructure" 
"implementation projects" 
"Discover Linux & FLOSS between" 
"1995 / 2000" 
"First step in the cloud around 2011" 
", pre-sales solution architect" 
"2014 (Cloud, DevOps)" 
"I’m an Ops !"
COMPANY="CGI 20th November 2017"
JOB="Information system architect" 
"specialized around DevOps technologies"
What is Bash ?
What is Bash ?
A command line
interpreter
A Turing complete
language
Default shell for most
Linux distributions
A 30 years old free
software project
https://en.wikipedia.org/wiki/Bash_(Unix_shell)
https://en.wikipedia.org/wiki/Bash_(Unix_shell)
Bash ⚠ 💥
Bash ⚠ 💥
Bash does not respect Posix completely and have
bashism
Bashism extends functionalities
Debian is aliasing sh to Dash !
Shell scripts must use Unix file format
Why should Bash usage not be underestimated ?
Why should Bash usage not be underestimated ?
Bash is everywhere !
In application startup scripts
In Docker images (entrypoint)
In CI/CD
...
Bash is running critical stuffs !
Managing the system
Monitoring / backups
Packaging artifacts
Checking file integrity and signature
...
So why are bash scripts not written with the same
discipline as other languages ?
Pitfall #1
Pitfall #1
#!/bin/bash
# Synchornize fses
# Use this script to migrate fs data to a remote server
# Parameters:
# $1: remote server
# $2: src fs name
# $3: dst fs name
#
# Example to copy all data fses to rmt_server:
# for i in $(mount | grep data | awk '{print $3}')
# do
# pitfall1.sh $i rmt_server:$i
# done
RSERVER=$1
SRC=$2
DST=$3
echo "Synchronize $SRC to $RSERVER..."
rsync -avx --progress --delete /"$SRC" "$RSERVER":/"$DST"
Pitfall #2
Pitfall #2
#!/bin/bash
# Backup mysqldb and gzip the content
# Parameters:
# $1: db name
usage() {
echo "Missing parameter DB" && exit 1
}
DBNAME=$1
if [ -z "$DBNAME" ]; then
usage
fi
echo "Backuping $DBNAME..."
mysqldump "$DBNAME" --add-drop-table | gzip -c >"$DBNAME.sql.gz"
if [ $? -ne 0 ]; then
echo "Backup failed"
exit 1
fi
How to prevent such pitfalls ?
How to prevent such pitfalls ?
How to make shell scripts more reliable ?
How to make shell scripts more reliable ?
Bash strict mode
Bash strict mode
Shebang to not hardcode bash path and use the one specified in PATH
-e immediately exit if any command has a non-zero exit
-u requires variables to be defined
-o pipefail if any command in a pipeline fails, that return code will be
used as the return code of the whole pipeline
IFS Internal Filed Separator set to tab and carriage return
#!/usr/bin/env bash
set -euo pipefail
IFS=$'nt'
http://redsymbol.net/articles/unofficial-bash-strict-mode/
http://redsymbol.net/articles/unofficial-bash-strict-mode/
Shell style guide from Google
Shell style guide from Google
https://google.github.io/styleguide/shell.xml
https://google.github.io/styleguide/shell.xml
When to not use a shell script
When to not use a shell script
Performance needs
Complex data structures
Declarations
Declarations
Immutability
Use declare -r or readonly
Use uppercase for constants
Variable scope
Use local to restrict visible scope to a function
local uses the same options as declare
Trap signals
Trap signals
for proper exit
for proper exit
#!/usr/bin/env bash
set -euo pipefail
IFS=$'nt'
_exit() {
echo "Exit time"
date -Iseconds
exit 2
}
trap _exit SIGINT
date -Iseconds
sleep 5m
Use [[ ]]
Use [[ ]]
Replace and enhanced test
Bash builtin (not a spawn process)
Allow regexp using =~
if [[ ! -f bin/bfg-1.13.0.jar ]]; then
[[ "$output" =~ "DiG" ]]
Function naming
Function naming
Use explicit naming to "auto document" code
Snake case is recommended
Do not use the function keyword
Always use functions and a main for complex scripts
Always use functions and a main for complex scripts
...
install_bfg_via_curl() {
if [[ ! -f bin/bfg-1.13.0.jar ]]; then
mkdir -p bin
...
}
main() {
if [[ "${PART}" == "bash" ]]; then
install_tools_via_packages
install_screenkey_via_git
install_bfg_via_curl
backup_regular_file_and_stow bash
source "${HOME}/.bashrc"
elif [[ "${PART}" == "tmux" ]]; then
install_tpm_via_git
backup_regular_file_and_stow tmux
fi
}
main
Productivity tools
Productivity tools
Linter: Shellcheck
Linter: Shellcheck
A shell script static analysis tool
A shell script static analysis tool
Really, really help writing shells ! 💎 🤩
Can be plugged into editors
https://github.com/koalaman/shellcheck
https://github.com/koalaman/shellcheck
Formatter: shfmt
Formatter: shfmt
Format code no headache ! 🔨
Can be configured to respect Google guide using '-i 2 -
ci'
Can be plugged into editors
https://github.com/mvdan/sh#shfmt
https://github.com/mvdan/sh#shfmt
Unit test: Bats
Unit test: Bats
Bash Automated Testing System
Bash Automated Testing System
TAP compliant testing
framework (Jenkins
plugin)
Run and skip helper
function to control test
execution
Setup, teardown helper
functions to manage pre
and post test scripts
https://github.com/sstephenson/bats#bats-bash-automated-testing-system
https://github.com/sstephenson/bats#bats-bash-automated-testing-system
Bats
Bats
Examples
Examples
#!/usr/bin/env bats
shopt -s expand_aliases
source ${HOME}/.aliases
@test "Test xclip is available for aliases" {
run xclip -h
[[ "$status" -eq 0 ]]
[[ "$output" =~ "Usage: xclip" ]]
}
@test "Test sudo is available for aliases" {
run sudo -h
[[ "$status" -eq 0 ]]
[[ "$output" =~ "usage: sudo -h" ]]
}
Test sandbox: Vagrant or Docker
Test sandbox: Vagrant or Docker
Bash can be tested
against a fresh minimal
installation
Help reproduce cases
CI/CD with Travis CI +
Docker for open source
projects
GNU parallel
GNU parallel
A tool to simplify executing job in parallel
A tool to simplify executing job in parallel
Ripgrep (rg)
Ripgrep (rg)
An application written in Rust
An application written in Rust
A grep enhanced
Compatible with standard grep
Search speed is really impressive
Look also at fd, exa, lsd...
The million dollar Guides !
The million dollar Guides !
Advanced Bash-Scripting Guide
The art of command line
THANK YOU
THANK YOU
René Ribaud <rene.ribaud@cgi.com>
René Ribaud <rene.ribaud@cgi.com>
🎬 Source: https://github.com/uggla/presentation/tree/agile2019/strictbash
🎬 Source: https://github.com/uggla/presentation/tree/agile2019/strictbash

Weitere ähnliche Inhalte

Was ist angesagt?

How to herd cat statues and make awesome things
How to herd cat statues and make awesome thingsHow to herd cat statues and make awesome things
How to herd cat statues and make awesome things
meldra
 

Was ist angesagt? (13)

Perl Dancer for Python programmers
Perl Dancer for Python programmersPerl Dancer for Python programmers
Perl Dancer for Python programmers
 
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
 
Webinar - Windows Application Management with Puppet
Webinar - Windows Application Management with PuppetWebinar - Windows Application Management with Puppet
Webinar - Windows Application Management with Puppet
 
Perl Moderno
Perl ModernoPerl Moderno
Perl Moderno
 
Rush, a shell that will yield to you
Rush, a shell that will yield to youRush, a shell that will yield to you
Rush, a shell that will yield to you
 
Hubot: a look inside our robot friend
Hubot: a look inside our robot friendHubot: a look inside our robot friend
Hubot: a look inside our robot friend
 
Composer the right way - SunshinePHP
Composer the right way - SunshinePHPComposer the right way - SunshinePHP
Composer the right way - SunshinePHP
 
Happy porting x86 application to android
Happy porting x86 application to androidHappy porting x86 application to android
Happy porting x86 application to android
 
An introduction to Rex - FLOSS UK DevOps York 2015
An introduction to Rex - FLOSS UK DevOps York 2015An introduction to Rex - FLOSS UK DevOps York 2015
An introduction to Rex - FLOSS UK DevOps York 2015
 
How to herd cat statues and make awesome things
How to herd cat statues and make awesome thingsHow to herd cat statues and make awesome things
How to herd cat statues and make awesome things
 
App Lego
App LegoApp Lego
App Lego
 
2019 11-bgphp
2019 11-bgphp2019 11-bgphp
2019 11-bgphp
 
Shell scripting
Shell scriptingShell scripting
Shell scripting
 

Ähnlich wie Bash is not a second zone citizen programming language

Unix Shell Scripting Basics
Unix Shell Scripting BasicsUnix Shell Scripting Basics
Unix Shell Scripting Basics
Sudharsan S
 

Ähnlich wie Bash is not a second zone citizen programming language (20)

Shell scripting
Shell scriptingShell scripting
Shell scripting
 
Unleash your inner console cowboy
Unleash your inner console cowboyUnleash your inner console cowboy
Unleash your inner console cowboy
 
Lets make better scripts
Lets make better scriptsLets make better scripts
Lets make better scripts
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Terraform in deployment pipeline
Terraform in deployment pipelineTerraform in deployment pipeline
Terraform in deployment pipeline
 
C Under Linux
C Under LinuxC Under Linux
C Under Linux
 
Linux basic for CADD biologist
Linux basic for CADD biologistLinux basic for CADD biologist
Linux basic for CADD biologist
 
Porting your favourite cmdline tool to Android
Porting your favourite cmdline tool to AndroidPorting your favourite cmdline tool to Android
Porting your favourite cmdline tool to Android
 
Nix: What even is it though?
Nix: What even is it though?Nix: What even is it though?
Nix: What even is it though?
 
NYPHP March 2009 Presentation
NYPHP March 2009 PresentationNYPHP March 2009 Presentation
NYPHP March 2009 Presentation
 
Raspberry pi Part 25
Raspberry pi Part 25Raspberry pi Part 25
Raspberry pi Part 25
 
Bash production guide
Bash production guideBash production guide
Bash production guide
 
Unix shell scripting basics
Unix shell scripting basicsUnix shell scripting basics
Unix shell scripting basics
 
One-Liners to Rule Them All
One-Liners to Rule Them AllOne-Liners to Rule Them All
One-Liners to Rule Them All
 
Unleash your inner console cowboy
Unleash your inner console cowboyUnleash your inner console cowboy
Unleash your inner console cowboy
 
Unix Shell Scripting Basics
Unix Shell Scripting BasicsUnix Shell Scripting Basics
Unix Shell Scripting Basics
 
Text mining on the command line - Introduction to linux for bioinformatics
Text mining on the command line - Introduction to linux for bioinformaticsText mining on the command line - Introduction to linux for bioinformatics
Text mining on the command line - Introduction to linux for bioinformatics
 
Bioinformatica 29-09-2011-p1-introduction
Bioinformatica 29-09-2011-p1-introductionBioinformatica 29-09-2011-p1-introduction
Bioinformatica 29-09-2011-p1-introduction
 
Licão 05 scripts exemple
Licão 05 scripts exempleLicão 05 scripts exemple
Licão 05 scripts exemple
 
Beautiful Bash: Let's make reading and writing bash scripts fun again!
Beautiful Bash: Let's make reading and writing bash scripts fun again!Beautiful Bash: Let's make reading and writing bash scripts fun again!
Beautiful Bash: Let's make reading and writing bash scripts fun again!
 

Kürzlich hochgeladen

Kürzlich hochgeladen (20)

Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 

Bash is not a second zone citizen programming language

  • 1. BASH BASH is not a second zone citizen programming language is not a second zone citizen programming language Agile Grenoble Agile Grenoble November 2019 November 2019
  • 2.
  • 3. 2 words about me 2 words about me $ cat ~/.bash_profile NAME="René Ribaud" UPTIME=44 source ./1998.sh PROFESSION="Unices system and storage" "administrator , since 2003" HISTORY="Lots (too many) infrastructure" "implementation projects" "Discover Linux & FLOSS between" "1995 / 2000" "First step in the cloud around 2011" ", pre-sales solution architect" "2014 (Cloud, DevOps)" "I’m an Ops !" COMPANY="CGI 20th November 2017" JOB="Information system architect" "specialized around DevOps technologies"
  • 4. What is Bash ? What is Bash ? A command line interpreter A Turing complete language Default shell for most Linux distributions A 30 years old free software project https://en.wikipedia.org/wiki/Bash_(Unix_shell) https://en.wikipedia.org/wiki/Bash_(Unix_shell)
  • 5. Bash ⚠ 💥 Bash ⚠ 💥 Bash does not respect Posix completely and have bashism Bashism extends functionalities Debian is aliasing sh to Dash ! Shell scripts must use Unix file format
  • 6. Why should Bash usage not be underestimated ? Why should Bash usage not be underestimated ? Bash is everywhere ! In application startup scripts In Docker images (entrypoint) In CI/CD ... Bash is running critical stuffs ! Managing the system Monitoring / backups Packaging artifacts Checking file integrity and signature ... So why are bash scripts not written with the same discipline as other languages ?
  • 7. Pitfall #1 Pitfall #1 #!/bin/bash # Synchornize fses # Use this script to migrate fs data to a remote server # Parameters: # $1: remote server # $2: src fs name # $3: dst fs name # # Example to copy all data fses to rmt_server: # for i in $(mount | grep data | awk '{print $3}') # do # pitfall1.sh $i rmt_server:$i # done RSERVER=$1 SRC=$2 DST=$3 echo "Synchronize $SRC to $RSERVER..." rsync -avx --progress --delete /"$SRC" "$RSERVER":/"$DST"
  • 8. Pitfall #2 Pitfall #2 #!/bin/bash # Backup mysqldb and gzip the content # Parameters: # $1: db name usage() { echo "Missing parameter DB" && exit 1 } DBNAME=$1 if [ -z "$DBNAME" ]; then usage fi echo "Backuping $DBNAME..." mysqldump "$DBNAME" --add-drop-table | gzip -c >"$DBNAME.sql.gz" if [ $? -ne 0 ]; then echo "Backup failed" exit 1 fi
  • 9. How to prevent such pitfalls ? How to prevent such pitfalls ? How to make shell scripts more reliable ? How to make shell scripts more reliable ?
  • 10. Bash strict mode Bash strict mode Shebang to not hardcode bash path and use the one specified in PATH -e immediately exit if any command has a non-zero exit -u requires variables to be defined -o pipefail if any command in a pipeline fails, that return code will be used as the return code of the whole pipeline IFS Internal Filed Separator set to tab and carriage return #!/usr/bin/env bash set -euo pipefail IFS=$'nt' http://redsymbol.net/articles/unofficial-bash-strict-mode/ http://redsymbol.net/articles/unofficial-bash-strict-mode/
  • 11. Shell style guide from Google Shell style guide from Google https://google.github.io/styleguide/shell.xml https://google.github.io/styleguide/shell.xml
  • 12. When to not use a shell script When to not use a shell script Performance needs Complex data structures
  • 13. Declarations Declarations Immutability Use declare -r or readonly Use uppercase for constants Variable scope Use local to restrict visible scope to a function local uses the same options as declare
  • 14. Trap signals Trap signals for proper exit for proper exit #!/usr/bin/env bash set -euo pipefail IFS=$'nt' _exit() { echo "Exit time" date -Iseconds exit 2 } trap _exit SIGINT date -Iseconds sleep 5m
  • 15. Use [[ ]] Use [[ ]] Replace and enhanced test Bash builtin (not a spawn process) Allow regexp using =~ if [[ ! -f bin/bfg-1.13.0.jar ]]; then [[ "$output" =~ "DiG" ]]
  • 16. Function naming Function naming Use explicit naming to "auto document" code Snake case is recommended Do not use the function keyword
  • 17. Always use functions and a main for complex scripts Always use functions and a main for complex scripts ... install_bfg_via_curl() { if [[ ! -f bin/bfg-1.13.0.jar ]]; then mkdir -p bin ... } main() { if [[ "${PART}" == "bash" ]]; then install_tools_via_packages install_screenkey_via_git install_bfg_via_curl backup_regular_file_and_stow bash source "${HOME}/.bashrc" elif [[ "${PART}" == "tmux" ]]; then install_tpm_via_git backup_regular_file_and_stow tmux fi } main
  • 19. Linter: Shellcheck Linter: Shellcheck A shell script static analysis tool A shell script static analysis tool Really, really help writing shells ! 💎 🤩 Can be plugged into editors https://github.com/koalaman/shellcheck https://github.com/koalaman/shellcheck
  • 20. Formatter: shfmt Formatter: shfmt Format code no headache ! 🔨 Can be configured to respect Google guide using '-i 2 - ci' Can be plugged into editors https://github.com/mvdan/sh#shfmt https://github.com/mvdan/sh#shfmt
  • 21. Unit test: Bats Unit test: Bats Bash Automated Testing System Bash Automated Testing System TAP compliant testing framework (Jenkins plugin) Run and skip helper function to control test execution Setup, teardown helper functions to manage pre and post test scripts https://github.com/sstephenson/bats#bats-bash-automated-testing-system https://github.com/sstephenson/bats#bats-bash-automated-testing-system
  • 22. Bats Bats Examples Examples #!/usr/bin/env bats shopt -s expand_aliases source ${HOME}/.aliases @test "Test xclip is available for aliases" { run xclip -h [[ "$status" -eq 0 ]] [[ "$output" =~ "Usage: xclip" ]] } @test "Test sudo is available for aliases" { run sudo -h [[ "$status" -eq 0 ]] [[ "$output" =~ "usage: sudo -h" ]] }
  • 23. Test sandbox: Vagrant or Docker Test sandbox: Vagrant or Docker Bash can be tested against a fresh minimal installation Help reproduce cases CI/CD with Travis CI + Docker for open source projects
  • 24. GNU parallel GNU parallel A tool to simplify executing job in parallel A tool to simplify executing job in parallel
  • 25. Ripgrep (rg) Ripgrep (rg) An application written in Rust An application written in Rust A grep enhanced Compatible with standard grep Search speed is really impressive Look also at fd, exa, lsd...
  • 26. The million dollar Guides ! The million dollar Guides ! Advanced Bash-Scripting Guide The art of command line
  • 27. THANK YOU THANK YOU René Ribaud <rene.ribaud@cgi.com> René Ribaud <rene.ribaud@cgi.com> 🎬 Source: https://github.com/uggla/presentation/tree/agile2019/strictbash 🎬 Source: https://github.com/uggla/presentation/tree/agile2019/strictbash