[dev] Shell style guide

From: Evan Gates <evan.gates_AT_gmail.com>
Date: Tue, 6 Sep 2016 11:35:35 -0700

suckless.org projects have traditionally been small amounts of pure C.
The code tends towards simplicity and correctness. I value this and
have learned much over the past years from reading and contributing to
various projects.

The addition of stali means there will probably be a fair amount of
shell scripting. In my experience the vast majority of shell scripts
are complete crap. Worse than poor style are poor practices that
create broken code. As such I propose that we add a shell scripting
style guide to go along with the existing C style guide in hopes of
keeping suckless.org's shell scripts as clean, simple, and correct as
the C code.

I think it should include the following, and probably some more. Many
of these rules are covered in the only bash guide I've found that
doesn't include bad practices. It also has a lot of information
pertaining to POSIX sh. Please check out the guide[0], faq[1], and
common pitfalls[2].

Shebang: Use #!/bin/sh and only use POSIX shell features. If you need
bash features use the proper shebang, either #!/path/to/bash or
#!/usr/bin/env bash

Extension: Do not give your script a .sh extension. An executable
script is defining a new command. Do you run ls.elf? Furthermore if
the script is later rewritten in a different language the extension is
now wrong. It is acceptable to have a script with a .sh extension in a
project as long as it is then stripped of the extension and made
executable during the build (just like a .c file would be). The
following rule already exists as a builtin inference rule in POSIX
make to do this:

        cp $< $_AT_
        chmod a+x $_AT_

Quoting: Quote all expansions/substitutions. e.g. always use "$foo"
and never use $foo. There are an extremely small number of acceptable
reasons to break this rule, e.g. $CFLAGS (note that some parts of the
grammar do not require quotes for safe expansion such as assignment
and case $var in, we should discuss what to require in these cases)

Storing Commands: Do not store commands in strings. This is what
functions are for. Storing complex commands in strings and trying to
execute or eval them is fragile and needlessly complex.

Command Substitution: Always use "$()", never use backticks. This
makes for easier nesting and fewer surprises. Remember these should
always be quoted.

Variable Names: By convention all cap names are reserved for internal
shell variables and environment variables. If your variable is not
exported to the environment for use by a child process it should not
be all caps. Lower case variables also greatly increase readability.

Errexit: Do not use set -e. It is a legacy feature that is broken by
design and includes many corner cases and gotchas. Check the result of
each command that can fail and exit if necessary.

Checking exit status: Do not run a command and then check against $?.
This is pointless. Instead check the exit status directly with if
cmd; then .... or by using a boolean operator such as cmd && ...

Do not parse ls: ls is a tool to view files in a human readable
format. Most often when someone tries to use the output of ls they
really just wanted a glob anyway.

Test: Do not use parens or boolean operators inside test expressions.
They are deprecated and useless. Instead of [ "$a" = foo -a "$b" = bar
] use [ "$a" = foo ] && [ "$b" = bar ]

Echo and printf: Do not use echo if your input includes a variable or
backslash. There is no safe way to do so. Use printf and %s instead.

These cover the most common mistakes I see.

I would be happy to comb through suckless projects and submit patches
that at least fix broken/dangerous code and preferably style aspects
as well.

Please discuss,

Received on Tue Sep 06 2016 - 20:35:35 CEST

This archive was generated by hypermail 2.3.0 : Tue Sep 06 2016 - 20:48:05 CEST