DEV Community

Cover image for A Practical Shell Scripting Introduction for JavaScript Engineers
Karolis Šarapnickis
Karolis Šarapnickis

Posted on • Originally published at karolis.sh

A Practical Shell Scripting Introduction for JavaScript Engineers

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

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:

echo "PORT=${PRODUCTION_PORT}" > .env
echo "API_URL=${PRODUCTION_API_URL}" >> .env
Enter fullscreen mode Exit fullscreen mode

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:

echo Version - $(node -e "console.log(require('./package.json').version)")
Enter fullscreen mode Exit fullscreen mode

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:

#!/bin/bash

if [[ $CI ]]; then
    yarn --cache-folder $PWD/.yarn --prefer-offline --frozen-lockfile
else
    yarn
fi
Enter fullscreen mode Exit fullscreen mode

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:
{
  "config": {
    "port": 1234
  },
  "srcipts": {
    "start": "serve -l ${PORT:-$npm_package_config_port}"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Reusing .env file in shell sessions - e.g., a localhost release script for Lerna project that loads .env, and performs some necessary checks:
#!/bin/bash

set -a; source .env; set +a

if [[ ! $GH_TOKEN ]]; then
    echo "🚨 Missing GH_TOKEN env variable" && exit 1
fi

if [[ ! "$(npm whoami)" ]]; then
    echo "🚨 Not logged in to npm" && exit 1
fi

if [[ ! "$(git status --porcelain)" ]]; then
    git checkout main && git pull && yarn release
else
    git status --porcelain
    echo "🧹 Working directory not clean" && exit 1
fi
Enter fullscreen mode Exit fullscreen mode

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:

Discussion (2)

Collapse
stevewhitmore profile image
Steve Whitmore

Good for you branching out and learning new tools. I come from a frontend background and found Bash intimidating at first. I think it's great for simple tasks but have found embracing Python for advanced stuff really helpful as well. It's all about learning the right tool for the job and being open to learning outside your comfort zone!

Collapse
karolis_sh profile image
Karolis Šarapnickis Author • Edited on

Good points there, but Bash is a bit different I think. It's like a core thing - it's there on everyone's terminal. It's like a starting point for any comman or an app you'd want to use.
I'm all for using the right tool for the problem, but you also need the team on board. You'd need a bit more justification add a Python script to a JavaScript project 😅