Skip to content

Latest commit

 

History

History
220 lines (154 loc) · 7.89 KB

shell.md

File metadata and controls

220 lines (154 loc) · 7.89 KB

Shell

The following conventions and suggestions should be followed. They help a lot keeping overview and the code clear.

Although the language is bash or zsh, the project's style bases on python's philosophie.

The Zen of Python should be kept in mind while coding. From Wikipedia - Python:
In general, the language's core philosophy (...) includes

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Readability counts.

which may be called pythonic.

Table of Contents

  1. File Style
  2. Coding Conventions
  3. Documentation
  4. Project Conventions
  5. Colors

File Style

  • Maximum line width is 80. Exceptions can be made for String-constants and similar.

    Humans have trouble reading the code with increasing line width. In general, more than 80 is not recommended.

  • Use 4 spaces for indention (p.s.: could help salary!).

Coding Conventions

First of all, a great linter shellcheck helps distinguishing between styles/standards like POSIX, bash.

  • Functions, fields and file names are written in snake_case.

    Since functions and fields are added to the shell's environment, their names are slightly changed to prevent conflicts or verbose autocompletion and help understanding the code:

    • exported ("public") fields' names are written in UPPER_CASE_SNAKE_CASE
    • private fields' names are written in __UPPER_CASE_SNAKE_CASE (double-underscore!)
    • public functions' names are written in snake_case
    • private functions' names are written in __snake_case (double-underscore)
    # BAD
    Greet() {
        used_shell="unknown"
        # ...
    }
    
    export dotfiles="..."
    
    
    
    # GOOD
    __finished() {
        echo "Finished"
    }
    
    # used in the shell, hence no leading dunderscore
    greet() {
        local __USED_SHELL="unknown"
        if [ -n "${ZSH_NAME}" ]; then __USED_SHELL="zsh"; fi
        if [ -n "${BASH}" ];     then __USED_SHELL="bash"; fi
    
        echo ""
        echo "Hello ${USER}."
        echo "I'm a ${__USED_SHELL} window at your service."
        echo ""
    }
    
    export DOTFILES="..."
    __finished
  • Always use ${VAR} over $VAR for sake of consistency (searching, vim, ...).

    It helps a lot in doing automatic stuff like replacing code in an automatic way (e.g. using vi).

    - $VAR
    - $1
    + ${VAR}
    + ${1}
  • Use constants over magic numbers (or often used paths)!

    Even the author will not know the meaning of every number after several months. And if he knows though, he will probably forget the precision of the constant and the places, where he has put them (-> bad for debugging).

    # BAD
    # because the path is often used
    
    if [ -d "<path to shell lib>/func" ]; then
        ...
    fi
    
    if [ -d "<path to shell lib>/prompts" ]; then
        ...
    fi
    
    
    
    # GOOD
    
    shell_lib="<path to shell lib>"
    if [ -d "${shell_lib}/func" ]; then
        ...
    fi
    
    if [ -d "${shell_lib}/prompts" ]; then
        ...
    fi
  • Make visibility as closest as possible.

    Usually, programmer tend to not bother with visibility, but visibility helps a lot with getting nice and persistent interfaces.

    In shell, other scripts can be sourced, which acts like adding the code of the called script inside the calling script. Defined functions and fields are visible in the shells's environment. To remove them, call unset <name>.

  • Use white spaces around binary operators. Per default, shell scripting is able to execute integer calculations, e.g. echo $((3 + 4)). Exceptions can be made for special cases to improve readability (see below).

    - e = (- a) * b;
    + e = (-a) * b;
    
    - e = a*b;
    + e = a * b;
    
    + e = a * b + c * d;    # ok, but not recommended here
    + e = a*b + c*d;        # improves readability
  • Single quotes ' preserve the literal value of each character, double quotes " interprete some of them.

    $ echo "$(echo "hello")"
    hello
    
    $ echo '$(echo "hello")'
    $(echo "hello")
  • [[ ... ]] vs [ ... ] vs ((...)) vs (...) vs {...}
    See this super stackoverflow answers. In a nutshell:

    • [[ ... ]] is bash-extension and has additional features over [ ... ], but latter is POSIX and preferred always.
    • (...) executes shell commands and influences execution order.
    • ((...)) executes arithmetic instructions (no floats)
    • {...} groups commands which only influences parsing, so it doesn't change the execution order. Example:
      x=2; { x=4; }; echo ${x} prints 4 but
      x=2; ( x=4; ); echo ${x} prints 2

Documentation

  • Separate module sections with #-----# (whole line). Take the following code snippet for inspiration.

    #--------------------------------------------------------#
    # exports
    
    . "${shell_lib}/exports.sh"
    if [ -f "${custom_shell_lib}/exports.sh" ]; then
        . "${custom_shell_lib}/exports.sh"
    fi
    
    #--------------------------------------------------------#
    # aliases
    
    . "${shell_lib}/aliases.sh"
    if [ -f "${custom_shell_lib}/aliases.sh" ]; then
        . "${custom_shell_lib}/aliases.sh"
    fi

Project Conventions

  • Prefer package/folder management over file mangement if meaningful.
    BUT: Think in an intuitive, handy and deterministic(!) way and don't take structuring and subfolding too far.

    Always ask yourself:
    How would most of the people search for this module/class/file?
    Someone without knowing the whole project structure should be able to find a file at the first try.
    In every folder, there should be only one option to continue searching (-> determinism).

Colors

## in .bashrc
export LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:'

alias ls='ls --color=auto'