You can find it very obvious, but there are tons of bash scripts out there written very badly.
People often forget that Bash actually a programming language. And just like JavaScript, Python, Ruby, GoLang and many others languages, Bash language has functions.
Let’s check simple bash function which prints green string taken as an argument:
logger() {
  local GREEN="\033[1;32m"
  local NC="\033[00m"
  echo -e "${GREEN}Logger: $1 ${NC}"
}
First two variables it’s ANSI color codes, I used green color here to make logger message colored.
What’s interesting here it’s how functions take parameters/arguments. Simply, they don’t. You can’t specify arguments while declaring function, but it’s still possible to use passed arguments in function body calling them $1 , $2 , $3 , etc:
hello() {
  echo "Hello," $1
}
$ hello world
# prints: Hello, world
So thats how logger() function works.

Now when we understood how to write functions, it’s time to see how we can use them for good.
Let’s write bash automation script which installs all required modern ruby environment on Ubuntu 16.04 — 18.04:
All required steps for script takes from awesome gorails manual Setup Ruby On Rails on Ubuntu 18.04 Bionic Beaver
Here is a full script: ruby_install.sh:
#!/bin/bash
# check latest here https://www.ruby-lang.org/en/downloads/releases/
RUBY_VERSION=2.5.1
logger() {
  local GREEN="\033[1;32m"
  local NC="\033[00m"
  echo -e "${GREEN}Logger: $1 ${NC}"
}
ruby_compiling_dependencies_install() {
  logger "Installing ruby build dependencies..."
  sudo apt install -q -y zlib1g-dev build-essential libssl-dev libreadline-dev libreadline6-dev libyaml-dev libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev
}
rbenv_install() {
  logger "Installing rbenv..."
  cd && rm -rf ~/.rbenv
  git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
  git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
  echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
  echo 'eval "$(rbenv init -)"' >> ~/.bashrc
  # export rbenv path for further ruby installation using rbenv
  export PATH="$HOME/.rbenv/bin:$PATH"
}
ruby_version_install() {
  logger "Installing ruby ${RUBY_VERSION}..."
  export CONFIGURE_OPTS="--disable-install-doc"
  rbenv install $RUBY_VERSION
  logger "Enabling ruby ${RUBY_VERSION} as global version..."
  rbenv global $RUBY_VERSION
  rbenv rehash
}
bundler_install() {
  # gems config, disable downloading gems documentation
  echo "gem: --no-ri --no-rdoc" > ~/.gemrc && chmod 644 ~/.gemrc
  # install bundler
  logger "Installing bundler..."
  rbenv exec gem install bundler
  # bundler config, allow to install gems from insecure sources (git)
  rbenv exec bundle config --global git.allow_insecure true
  # enable parallel gems installation (4 in parallel)
  rbenv exec bundle config --global jobs 4
}
main() {
  ruby_compiling_dependencies_install
  rbenv_install
  ruby_version_install
  bundler_install
  logger "All done! Don't forget to reload env variables in your terminal session: '\$ exec \$SHELL'"
}
main
Step by step:
#!/bin/bash — at the beginning of the script there is shebang.
Next there is logger function just for convenience, to control script execution process:
logger() {
  local GREEN="\033[1;32m"
  local NC="\033[00m"
  echo -e "${GREEN}Logger: $1 ${NC}"
}
To compile and install ruby version, first we need to install all required packages:
ruby_compiling_dependencies_install() {
  logger "Installing ruby build dependencies..."
  sudo apt install -q -y zlib1g-dev build-essential libssl-dev libreadline-dev libreadline6-dev libyaml-dev libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev
}
Next we are going to install Rbenv, wrapping all required steps for this in the function rbenv_install:
rbenv_install() {
  logger "Installing rbenv..."
  cd && rm -rf ~/.rbenv
  git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
  git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
  echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
  echo 'eval "$(rbenv init -)"' >> ~/.bashrc
  # export rbenv path for further ruby installation using rbenv
  export PATH="$HOME/.rbenv/bin:$PATH"
}
After successfully installing Rbenv, we can install Ruby itself:
ruby_version_install() {
  logger "Installing ruby ${RUBY_VERSION}..."
  export CONFIGURE_OPTS="--disable-install-doc"
  rbenv install $RUBY_VERSION
  logger "Enabling ruby ${RUBY_VERSION} as global version..."
  rbenv global $RUBY_VERSION
  rbenv rehash
}
Last step is to install Bundler gem, since it’s very common dependency manager in the Ruby world:
bundler_install() {
  # gems config, disable downloading gems documentation
  echo "gem: --no-ri --no-rdoc" > ~/.gemrc && chmod 644 ~/.gemrc
  logger "Installing bundler..."
  rbenv exec gem install bundler
  # bundler config, allow to install gems from insecure sources (git)
  rbenv exec bundle config --global git.allow_insecure true
  # enable parallel gems installation (4 in parallel)
  rbenv exec bundle config --global jobs 4
}
After all functions declared, it’s good practice to wrap everything to the main function:
main() {
  ruby_compiling_dependencies_install
  rbenv_install
  ruby_version_install
  bundler_install
  logger "All done! Don't forget to reload env variables in your terminal session: '\$ exec \$SHELL'"
}
And at the end call this main function:
main
All done! You can execute bash script using two ways:
- First way is to execute script by passing path to the script to the bash interpreter: 
$ bash ruby_install.sh. - Second way is to make script executable first: 
$ chmod +x ruby_install.shand then just call script:$ ./ruby_install.sh.