Operations 22 min read

Master Shell Script Style: Essential Guidelines for Clean, Efficient Bash Code

This article consolidates practical shell scripting standards—covering shebang usage, commenting, parameter validation, naming conventions, encoding, indentation, function structuring, variable scope, efficient command patterns, parallel execution, and static analysis with ShellCheck—to help developers write readable, maintainable, and performant Bash scripts.

Efficient Ops
Efficient Ops
Efficient Ops
Master Shell Script Style: Essential Guidelines for Clean, Efficient Bash Code

Preface

Due to work requirements, I revisited shell scripting. Although most commands are familiar, scripts often look messy and are hard to read, especially compared to other people's scripts. Shell scripts are more of a tool than a formal programming language, used to glue together various programs.

Many scripts become a long main function without structure, and the variety of shell versions and overlapping commands make standardization difficult.

After researching, I found scattered articles on these issues, so I organized them here as a technical specification for my future scripts.

Code Style Guidelines

Shebang

The shebang ( #!) at the first line specifies the interpreter, e.g.: #!/bin/bash You can list supported interpreters with cat /etc/shells:

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

Running ./a.sh without a shebang uses the interpreter defined by $SHELL; otherwise the shebang interpreter is used. This is the recommended approach.

Comments

Comments are essential in shell scripts because many one‑line commands are not self‑explanatory. A good comment acts like a README, explaining:

shebang

script parameters

purpose

cautions

author, date, license

function description

complex one‑liner explanation

Parameter Validation

Always check that parameters meet expectations and provide clear feedback. For example, ensure the correct number of arguments:

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

Variables and Magic Numbers

Define important environment variables at the script top, e.g. JAVA_HOME and PATH. Avoid hard‑coded magic numbers; use named variables instead.

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

Indentation Rules

Use consistent indentation (soft tabs with 2 or 4 spaces, or hard tabs). Keep then and do on the same line as the preceding statement to improve readability.

soft tab: spaces

hard tab: literal

\t

Naming Standards

Follow these conventions:

File names end with .sh Variable names are meaningful and correctly spelled

Use lowercase with underscores for identifiers

Encoding Consistency

Write scripts in UTF‑8. Prefer English for comments and logs to avoid garbled output on machines without Chinese locale. When editing on Windows, ensure UTF‑8 without BOM; otherwise the BOM bytes may cause “command not found” errors on Linux.

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

Permission

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

Logging and Echo

Logging aids debugging, especially in large projects. For user‑facing scripts, provide real‑time echo output, optionally with ANSI colors for better UX.

Password Removal

Never hard‑code passwords in scripts; this is critical when scripts are stored in public repositories.

Line Continuation

Split long command lines with a backslash and a trailing space for readability:

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

Efficiency

Prefer a single command over multiple when possible. Example:

# less efficient
cat /etc/passwd | grep root
# more efficient
grep root /etc/passwd

Combine multiple sed replacements into one command to reduce the number of find executions.

# single find, combined sed
find . -name '*.txt' | xargs sed -i "s/233/666/g;s/235/626/g;s/333/616/g;s/233/664/g"

Use xargs -P $(nproc) for parallel processing.

find . -name '*.txt' | xargs -P $(nproc) sed -i "s/233/666/g;s/235/626/g;s/333/616/g;s/233/664/g"

Double Quotes

Wrap variable expansions in double quotes to prevent word splitting and globbing.

var="*.sh"
echo $var   # expands to file list
echo "$var" # prints literal *.sh

Using a Main Function

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

#!/usr/bin/env bash

func1(){
    # do something
}

func2(){
    # do something else
}

main(){
    func1
    func2
}

main "$@"

Scope Considerations

Variables are global by default. Use local or declare to limit scope and avoid unintended side effects.

#!/usr/bin/env bash
var=1
func(){
    local var=2
}
func
echo $var   # prints 1

Function Return Values

Shell functions return integer status codes. To return strings, echo them and capture the output:

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

Indirect Reference

Access a variable whose name is stored in another variable using ${!VAR}:

VAR1="value"
VAR2="VAR1"
echo ${!VAR2}   # prints "value"

For assignments, eval is required but generally discouraged.

Heredocs

Use heredocs to embed multi‑line content, 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 Lookup

Obtain the script’s directory reliably:

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

Code Conciseness

Prefer the shortest command that accomplishes the task, e.g., use grep root /etc/passwd instead of piping cat into grep.

Parallel Execution

Run functions in background and wait for completion:

func(){
    # do something
}
for ((i=0;i<10;i++)); do
    func &
done
wait

Full‑Text Search

Search across files while handling spaces and binary files:

find . -type f | xargs -i echo '"{}"' | xargs grep 2333
find . -type f | xargs grep -a 2333

New Syntax Recommendations

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

Prefer printf over echo for formatted output

Other Tips

Prefer absolute paths; if using relative paths, prefix with ./ Use Bash’s parameter expansion instead of external tools like awk or sed when possible

Write simple if statements with && and || on a single line

Namespace exported variables to avoid collisions

Use trap to handle termination signals

Generate temporary files with mktemp Redirect unwanted output to /dev/null Check command exit status to determine success

Test file existence before operating on it

Avoid parsing ls output

Read files with while read loops instead of for Be aware of cp -r behavior regarding destination directories

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 explanations and fixes.

Installation

It is available on many platforms (Debian, Arch, Gentoo, EPEL, Fedora, macOS, openSUSE, etc.) via standard package managers.

Integration

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

Examples

The tool’s “Gallery of bad code” offers many real‑world examples, similar to a “Java Puzzlers” book for shell scripting.

Essence

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

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.

ShellBashscriptcoding-standards
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.