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.sh
and then just call script:$ ./ruby_install.sh
.