Operations 22 min read

Master Shell Script Best Practices: Clean Code, Safety, and Efficiency

This guide consolidates essential shell scripting conventions—including shebang usage, comprehensive commenting, parameter validation, variable handling, indentation, naming, encoding, permission settings, logging, password safety, line continuation, code brevity, quoting, main‑function patterns, scope control, indirect references, heredocs, path resolution, parallel execution, and static analysis with ShellCheck—to help developers write readable, maintainable, and performant scripts.

Efficient Ops
Efficient Ops
Efficient Ops
Master Shell Script Best Practices: Clean Code, Safety, and Efficiency

Preface

Due to work requirements, I recently started reorganizing my shell scripts. Although I frequently use most commands, the scripts often look messy, and reading others' scripts can be difficult because shell scripts are more of a tool than a formal programming language.

Consequently, many scripts become a long "main" function without clear structure, and the variety of shell versions and overlapping commands makes standardization hard.

After reviewing various documents, I found many scattered articles addressing these issues, so I compiled them here as a personal technical specification for future scripts.

Code Style Guidelines

Shebang at the Top

The shebang (#!) on the first line specifies the interpreter when none is explicitly set, e.g.: #!/bin/bash You can list supported interpreters with:

$ cat /etc/shells
#/etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/screen

If you run ./a.sh without a shebang, the script will use the interpreter defined by $SHELL; otherwise it follows the shebang. The shebang approach is recommended.

Include Comments

Comments are essential in shell scripts because many one‑line commands are not self‑explanatory. They serve as a README, explaining the shebang, script parameters, purpose, cautions, author, timestamps, and function descriptions.

Shebang

Script parameters

Script purpose

Precautions

Author, time, copyright

Function description comments

Complex one‑liner explanations

Validate Parameters

When a script accepts arguments, first check that they meet expectations and provide clear feedback:

if [[ $# != 2 ]]; then
    echo "Parameter incorrect."
    exit 1
fi

Avoid Magic Numbers

Define important environment variables at the top, e.g.:

source /etc/profile
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/apps/bin/"

When multiple Java versions exist, set JAVA_HOME and PATH explicitly. Prefer defining constants as variables rather than hard‑coding values.

Consistent Indentation

Use either soft tabs (2 or 4 spaces) or hard tabs (\t). Keep keywords like then and do on the same line as the condition to improve readability.

Standard Naming

File names should end with .sh Variable names must be meaningful and correctly spelled

Use lowercase with underscores for shell scripts

Unified Encoding

Write scripts in UTF‑8. Prefer English for comments and logs to avoid garbled output on machines without Chinese locale support. When editing on Windows, ensure UTF‑8 without BOM; otherwise the leading BOM bytes can cause syntax errors on Linux.

Be aware of line‑ending differences: Windows uses \r\n, Unix uses \n. Tools like dos2unix and unix2dos can convert them.

Set Execute Permissions

Remember to add execute permission to scripts; otherwise they cannot be run directly.

Logging and Echoing

Logging aids debugging, especially in large projects. Real‑time echoing of progress improves user experience; consider using ANSI/VT100 sequences for colors or effects.

Never Hard‑code Passwords

Never embed passwords in scripts, especially when the code is stored in public repositories.

Split Long Lines

Use a backslash with a preceding space to break long command lines for readability:

./configure \
--prefix=/usr \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \

Detailed Coding Tips

Efficiency

Choose commands wisely; for example, sed -n '1p;1q' file reads only the first line, whereas sed -n '1p' file reads the whole file.

Prefer Double Quotes

Wrap variable expansions in double quotes to avoid word splitting and globbing:

#!/bin/sh
var="*.sh"
echo $var   # expands to file list
echo "$var" # prints *.sh

Use a Main Function

Structure scripts with a main entry point similar to compiled languages:

#!/usr/bin/env bash

func1(){
    # do something
}

func2(){
    # do something else
}

main(){
    func1
    func2
}

main "$@"

Scope Control

Variables are global by default. Use local readonly or declare inside functions to limit scope.

Function Return Values

Shell functions return integer status codes. To return strings, echo the value and capture it:

func(){
    echo "2333"
}
res=$(func)
echo "This is from $res."

Indirect Variable Reference

Use ${!VAR} for simple indirect lookup, avoiding eval when possible.

Heredocs

Heredocs simplify multi‑line input, e.g. generating configuration files:

cat >>/etc/rsyncd.conf <<EOF
log file = /usr/local/logs/rsyncd.log
transfer logging = yes
log format = %t %a %m %f %b
syslog facility = local3
EOF

Path Resolution

Obtain the script's directory reliably with:

script_dir=$(cd $(dirname $0) && pwd)
# or
script_dir=$(dirname $(readlink -f $0))

Keep Commands Short

Prefer a single command over pipelines when possible, e.g. grep root /etc/passwd instead of cat /etc/passwd | grep root. Combine multiple sed replacements into one expression for speed.

Parallel Execution

Run functions in background and wait for completion, or use xargs -P for parallel processing.

Full‑Text Search

When searching files with spaces in names, quote the filenames or use -a with grep to treat binary files as text.

Modern Syntax

Define functions with func(){} instead of func{} Prefer [[ ]] over [ ] Use $(...) instead of backticks for command substitution

Prefer printf over echo for formatted output

Additional Tips

Prefer absolute paths; if using relative paths, prefix with ./ Prefer Bash parameter expansion over awk / sed for simple tasks

Use && and || for concise one‑line conditionals

Namespace exported variables to avoid conflicts

Use trap to handle termination signals

Generate temporary files with mktemp Redirect unwanted output to /dev/null Check file existence before operating on it

Avoid parsing ls output

Read files with while read loops instead of for When copying directories, be aware of destination behavior

Static Analysis Tool: ShellCheck

Overview

ShellCheck is an open‑source static analysis tool for shell scripts (over 8 K stars on GitHub) that helps catch common pitfalls and provides detailed explanations.

Installation

It supports many platforms, including Debian, Arch, Gentoo, EPEL, Fedora, macOS, openSUSE, etc., and can be installed via the respective package managers.

Integration

ShellCheck can be integrated into CI pipelines such as Travis CI to automatically lint shell‑script‑heavy projects.

Sample Usage

The project's "Gallery of bad code" offers many examples of problematic scripts, useful for learning what to avoid.

Core Value

The most valuable part is its extensive wiki, which explains each warning, why it matters, and how to fix it, making it an excellent resource for developers who want to understand the reasoning behind best practices.

Author: Myths

Link: https://blog.mythsman.com/2017/07/23/1/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

coding standardsBashShell scriptingShellCheck
Efficient Ops
Written by

Efficient Ops

This public account is maintained by Xiaotianguo and friends, regularly publishing widely-read original technical articles. We focus on operations transformation and accompany you throughout your operations career, growing together happily.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.