Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Check If a String Contains a Substring in Bash

check-if-a-string-contains-a-substring-in-bash.jpg

This can be handy to do something different based on a condition which helps solve any number of business use cases.

Quick Jump:

Prefer video? Here it is on YouTube.

In my case I was modifying a release script to support the idea of being able to deploy to multiple environments in a CI pipeline and I wanted to be able to easily skip deploying to an environment by setting an environment variable.

The use case here is you may want to lock your app(s) to a specific release for a few week long penetration test but still deploy the latest version to production. However, you can apply this to anything like demo, staging or whatever environments you might deploy to.

I landed on the idea of having DEPLOYABLE_ENVS=prod which also supports DEPLOYABLE_ENVS=pen,prod and the idea is you can call your release script multiple times in a pipeline for a specific environment such as: mkrelease pen ... or mkrelease prod ...

Then it’s a matter of exiting early in your release script if the environment (pen) doesn’t exist in DEPLOYABLE_ENVS. Super simple but very effective. It’s a matter of searching a string for another string, AKA. checking if a string contains a substring.

It worked well because it doesn’t require a code or pipeline file change to control which environments get deployed to. You only have to adjust a single environment variable in your CI pipeline’s settings. It also supports releases happening automatically after code is merged to your main branch as opposed to manually running a custom pipeline.

# The Script

Here’s a standalone demo script that shows how it works, it works with Bash 3.2+. Since we’re using a * wildcard in a condition it does require [[ ]] instead of [ ] so that means needing a Bash compatible shell, this is not POSIX compliant:

#!/usr/bin/env bash

set -o errexit
set -o pipefail
set -o nounset

ENV="${1:-prod}"
DEPLOYABLE_ENVS="${DEPLOYABLE_ENVS:-pen,prod}"

# This is the condition. We're using * to match anything on either side of the
# string. Of course you can adjust this as needed for your use case. It's
# important that the wildcard is on the right side of the condition.
if [[ "${DEPLOYABLE_ENVS}" == *"${ENV}"* ]]; then
  echo "Yes"
else
  echo "No"
  exit
fi

# The rest of the script.

If you don’t plan to have an else condition you can also negate the condition with !=.

What about a Regex?

You might be thinking “why not use a regex with =~ .*"${ENV}".* instead? You could but I think the wildcard approach is more readable for this use case. I’d reach for using a regex if I needed more advanced matching.

Also a wildcard match is ~3.5 faster (at least on my machine) but keep in mind for normal usage this probably won’t have a big impact:

$ time for x in {1..1000000}; do [[ pen,prod == *prod* ]]; done

real    0m2.482s
user    0m2.422s
sys     0m0.060s

$ time for x in {1..1000000}; do [[ pen,prod =~ .*prod.* ]]; done

real    0m8.793s
user    0m8.793s
sys     0m0.000s

Here’s a couple of outputs with the original script:

# Since ENV defaults to prod, this is expected
./demo
Yes

./demo pen
Yes

./demo prod
Yes

# This environment isn't in DEPLOYABLE_ENVS so it gets skipped
./demo staging
No

The video below shows how it works.

# Demo Video

Timestamps

  • 0:33 – Solving a real business use case
  • 2:18 – Looking at the script

References

What use case did you end up doing this for? Let me know below.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per year (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments