2021-11-06

A Practical Shell Scripting Introduction for JavaScript Engineers

Compact rundown though shell basics focusing on practical use cases in JavaScript projects.

banner1Photo by Hiroyoshi Urushima

When developing scripts, I always had a tendency to go for Node.js instead of shell, even for relatively simple things. Shell scripting has always been challenging for me, until I dedicated some time in learning the basics. Now I understand that, when used correctly, shell scripts can really simplify the logic. So, here are some core concepts and practical use cases that you can learn and hopefully apply in your projects.

Essential Commands

Here is a list of some of the more common shell commands that you'd encounter in JS projects:

  • echo - prints text terminal window (e.g., echo Build is complete!)
  • Files and folders:
    • mkdir creates directories (e.g. create directories recursively mkdir -p dist/code)
    • rm - removes files or directories (e.g. forcibly and recursively remove dist directory - rm -rf dist)
    • cp copy files and directories (e.g., cp -f dist/404/index.html dist/404.html)
    • mv move or rename files or directories (e.g., mv -f gen/robots.txt gen/manifest.json dist)
  • tar - archiving utility (e.g., create a gzipped archive and write it to a file - tar -czf bundle.tar.gz dist/)

There are many more commands (ls, cd, cat, etc.), but when encountered you can look the up in the manual pages (e.g., man tar). There's also a very nifty community-driven tool called tldr, that aims to simplify the beloved man pages with practical examples (e.g., tldr tar).

Essential Operators

Command Chaining Operators

Usage of a single command is rarely enough, so here some chaining operators:

  • && (the AND operator) - executes second command only if the first one succeeds (e.g., yarn && yarn build && yarn publish)
  • ; (the semicolon operator) - runs several commands, despite if previous one succeeded or not (e.g., yarn build && mv -f gen/sitemap.xml dist; echo Build done!)

There are many more shell operators, but these and npm-run-all should cover the majority of your chaining needs.

Output > and Output Append >> Operators

Both, output > and output append >> operators redirects content to a destination, but only >> appends to the target. E.g., creating a .env file in your CI pipeline:

1echo "PORT=${PRODUCTION_PORT}" > .env
2echo "API_URL=${PRODUCTION_API_URL}" >> .env

Command Substitution

Command substitution is a mechanism by which the shell performs a given set of commands and then exchanges their output in the place of the commands. E.g., combine Node.js script evaluation and command substitution to have JavaScript output in shell environment:

1echo Version - $(node -e "console.log(require('./package.json').version)")

Conditional Statements

Just like JavaScript, shell scripts can have if statements. They can be written as both multi and single line statements. E.g., performing an optimized yarn install for CI environment only:

1#!/bin/bash
2
3if [[ $CI ]]; then
4 yarn --cache-folder $PWD/.yarn --prefer-offline --frozen-lockfile
5else
6 yarn
7fi

Environment Variables

Environment variables are a common way of passing dynamically configurable values. Here are some use cases:

  • Configurable environment values with defaults - e.g., optional PORT environment variable with defaults from npm config:
1{
2 "config": {
3 "port": 1234
4 },
5 "srcipts": {
6 "start": "serve -l ${PORT:-$npm_package_config_port}"
7 }
8}
  • Reusing .env file in shell sessions - e.g., a localhost release script for Lerna project that loads .env, and performs some necessary checks:
1#!/bin/bash
2
3set -a
4source .env
5set +a
6
7if [[ ! $GH_TOKEN ]]; then
8 echo "🚨 Missing GH_TOKEN env variable" && exit 1
9fi
10
11if [[ ! $(npm whoami) ]]; then
12 echo "🚨 Not logged in to npm" && exit 1
13fi
14
15if [[ ! $(git status --porcelain) ]]; then
16 git checkout main && git pull && yarn release
17else
18 git status --porcelain
19 echo "🧹 Working directory not clean" && exit 1
20fi

Conclusion

Shell scripting is a powerful and elegant way to perform some common operations. Dedicate some time and give it a proper try - it's a versatile skill to have at your tool belt. This blog post scratches only the surface of what's possible, so here are some resources for further reading:


profile
Karolis Šarapnickis 🇱🇹

Software Engineer

I build stuff using JavaScript and share my findings from time to time. I hope you will find something useful here.

GithubTwitterLinkedInemail