Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 24, 2021 11:51 pm GMT

Writing Bash Scripts Like A Pro - Part 1 - Styling Guide

Writing Bash scripts can be challenging if you don't know the quirks and perks. In my mother tongue, we use the Yiddish word for quirks and perks; it's called "Shtickim" (plural of "Shtick"). Are you ready to learn more about Bash's "Shtickim"?

This blog post is part of a series that I'm working on in my spare time to preserve the knowledge for future me that forgets stuff, to assist new colleagues, and indulge programmers like you who wish to love Bash as I do. So let's begin, shall we?

It's A Scripting Language

It's important to remember that Bash is a scripting language, which means it doesn't offer the standard functionalities that a programming language has to offer, such as:

As you already guessed, "Bash programmers" (if there is such a thing) face many challenges. The above list is merely the tip of the iceberg.

Here are great blog posts that share the same feelings as I do:

Now that we've covered the fact that I'm in love with Bash, I want to share that feeling with you; here goes.

Variables Naming Convention

Here's how I name variables in my Bash scripts

TypeScopeConvention
EnvironmentGlobalMY_VARIABLE
GlobalGlobal_MY_VARIABLE
LocalFunctionmy_variable

In my older Bash scripts, the names of the variables were hard to interpret. Changing to this naming convention helped me a lot to understand the scope of variables and their purpose.

Good Vibes Application

And of course, we gotta' see some practical example, so here's how I implement the above naming convention in my good_vibes.sh application.

good_vibes.sh

#!/usr/bin/env bash# ^ This is called a Shebang# I'll cover it in future blog posts# Global variables are initialized by Env Vars.# I'm setting a default value with "${VAR_NAME:-"DEFAULT_VALUE"}"_USER_NAME="${USER_NAME:-"$USER"}"_USER_AGE="${USER_AGE:-""}"complement_name(){  local name="$1"  echo "Wow, ${name}, you have a beautiful name!"}complement_age(){  local name="$1"  local age="$2"  if [[ "$age" -gt "30" ]]; then    echo "Seriously ${name}? I thought you were $((age-7))"  else    echo "Such a weird age, are you sure it's a number?"  fi}main(){  # The only function that is not "pure"  # This function is tightly coupled to the script  complement_name "$_USER_NAME"  complement_age "$_USER_NAME" "$_USER_AGE"}# Invokes the main functionmain

good_vibes.sh - Execution and output

export USER_NAME="Julia" USER_AGE="36" && \bash good_vibes.sh# OutputWow, Julia, you have a beautiful name!Seriously Julia? I thought you were 29

Let's break down the good_vibes.sh application to a "set of rules" that can be implemented in your scripts.

Code block spacing

Two (2) blank rows between each block of code make the script more readable.

Indentation

I'm using two (2) spaces, though it's totally fine to use four (4) spaces for indentation. Just make sure you're not mixing between the two.

Curly braces

If it's a ${VARIABLE} concatenated with string, use curly braces as it makes it easier to read.

In case it's a "$LONELY_VARIABLE" there's no need for that, as it will help you realize faster if it's "lonely" or not.

The primary purpose for curly braces is for performing a Shell Parameter Expansion, as demonstrated in the Global variables initialization part.

Squared brackets

Using double [[ ]] squared brackets makes it easier to read conditional code blocks. However, do note that using double squared brackets is not supported in Shell sh; instead, you should use single brackets [ ].

I always use double brackets to ensure that my target users/machines have Bash bash installed.

To demonstrate the readability, here's a "complex" conditional code block:

if [[ "$USER_NAME" = "Julia" || "$USER_NAME" = "Willy" ]] \   && [[ "$USER_AGE" -gt "30" ]]; then  echo "Easy to read right?"fiif [ "$USER_NAME" = "Julia" || "$USER_NAME" = "Willy" ] \   && [ "$USER_AGE" -gt "30" ]; then  echo "No idea why but I feel lost with single brackets."fi

In case you didn't notice, you've just learned that || stands for OR and && stands for AND. And the short -gt expression means greater than when using numbers. Finally, the \ character allows breaking rows in favor of making the code more readable.

Shtick: Using \ with an extra space \ <- extra space can lead to weird errors. Make sure there are no trailing spaces after \.

I assume that using [[ ]] feels more intuitive since most conditional commands are doubled && ||.

Variable initialization

Global variables are initialized with Environment Variables and are set with default values in case of empty Environment variables.

As mentioned in the good_vibes.sh comments, I'm setting a default value with

"${VAR_NAME:-"DEFAULT_VALUE"}"

In the above snippet, the text DEFAULT_VALUE is hardcoded, and it's possible to replace it with a variable. For example

_USER_NAME="${USER_NAME:-"$USER"}"

Functions and local function variables

Functions names and local function variables names are snake_cased. You might want to change functions names to lowerCamelCase, and of course, it's your call.

Coupling a function to the script is a common mistake, though I do sin from time to time, and you'll see Global/Environment variables in my functions, but that happens when I know that "this piece of code won't change a lot".

Oh, and make sure you don't use $1 or any other argument directly; always use local var_name="$1".

_USER_NAME="${USER_NAME:-"$USER"}"# Bad - coupledcoupled_username(){  echo "_USER_NAME = ${_USER_NAME}"}# Good - decoupleddecoupled_username(){  local name="$1"  echo "name = ${name}" }# Usagecoupled_username  decoupled_username "$_USER_NAME"

Functional Programming

This topic relates to Functions and local function variables, where functions are as "pure" as possible. As you can see in good_vibes.sh, almost everything is wrapped in a function, except for Initializing Global variables.

I don't see the point of writing the init_vars function, whose purpose is to deal with Global variables. However, I do find myself adding a validate_vars function from time to time, which goes over the Global variables and validates their values. I'm sure there's room for debate here, so feel free to comment with your thoughts.

Final Words

The "Good Vibes Application" mostly covered how to write a readable Bash script following the Functional Programming paradigm.

If you feel that there's a need to change how you name variables and functions, go for it! As long as it's easy to understand your code, you're on the right track.

The next blog posts in this series will cover the following topics:

  • Error handling
  • Retrieving JSON data from an HTTP endpoint
  • Background jobs and watching file for changes with fswatch
  • Git Repository structure - adding Bash scripts to existing repositories or creating a new repository with a Bash CLI application
  • Publishing a Bash CLI as a Docker image

And more, and more ... I'm just going to spit it all out to blog posts. Feel free to comment with questions or suggestions for my next blog posts.


Original Link: https://dev.to/unfor19/writing-bash-scripts-like-a-pro-part-1-styling-guide-4bin

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To