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.:

<code>#!/bin/bash</code>

You can list supported interpreters with

cat /etc/shells

:

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

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:

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

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.

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

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:

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

Efficiency

Prefer a single command over multiple when possible. Example:

<code># less efficient
cat /etc/passwd | grep root
# more efficient
grep root /etc/passwd</code>

Combine multiple

sed

replacements into one command to reduce the number of

find

executions.

<code># 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"</code>

Use

xargs -P $(nproc)

for parallel processing.

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

Double Quotes

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

<code>var="*.sh"
echo $var   # expands to file list
echo "$var" # prints literal *.sh</code>

Using a Main Function

Structure scripts with functions and a

main

entry point, similar to compiled languages:

<code>#!/usr/bin/env bash

func1(){
    # do something
}

func2(){
    # do something else
}

main(){
    func1
    func2
}

main "$@"</code>

Scope Considerations

Variables are global by default. Use

local

or

declare

to limit scope and avoid unintended side effects.

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

Function Return Values

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

<code>func(){
    echo "result"
}
res=$(func)
echo "This is from $res."</code>

Indirect Reference

Access a variable whose name is stored in another variable using

${!VAR}

:

<code>VAR1="value"
VAR2="VAR1"
echo ${!VAR2}   # prints "value"</code>

For assignments,

eval

is required but generally discouraged.

Heredocs

Use heredocs to embed multi‑line content, e.g., generating configuration files:

<code>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</code>

Path Lookup

Obtain the script’s directory reliably:

<code>script_dir=$(cd $(dirname $0) && pwd)
# or
script_dir=$(dirname $(readlink -f $0))</code>

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:

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

Full‑Text Search

Search across files while handling spaces and binary files:

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

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

&amp;&amp;

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.

DevOpscoding standardsshellbashscript
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

login 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.