aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/_notes/bash.html
blob: cedfb4361004788fe57e7bd340b35ef89baaef8f (plain) (tree)
1
2
3
4
5
6
7



                        

                                                  
         










                                                 








                            
















                                        


                                                  
























                                                                                

                                    

                                            
 




                                                           

             

                                            
 
                                                   
                          












                                                         




                                   


                                                     


                




                                                                   

                
                                                               

                                       

                                                     

                                        

                                                          






                                       
                                            




                   


                                                   





                          





                                                                  



                                   


                                


                                   



                                                                                    



                                     



                                                                                        




                    
---
title: Bash
subtitle: best practices
layout: plain
links:
  - {rel: stylesheet, href: 'assets/css/bash.css'}
features:
  - title: Script header
    topics:
      - do:
          - |
            #!/usr/bin/env bash

            set -o errexit -o nounset -o pipefail
            shopt -s inherit_errexit lastpipe
        dont:
          - |
            #!/bin/sh -e
  - title: Arrays
    topics:
      - title: Declaration
        do:
          - |
            local -a xs=()
            declare -a xs=()
            local -A xs=()
            declare -A xs=()
        dont:
          - |
            local -a xs
            declare -a xs
            local -A xs
            declare -A xs

            # Doesn't work with nounset:
            echo "${#xs[@]}"
      - title: Expansion
        do:
          - |
            func ${arr[@]+"${arr[@]}"}
        dont:
          - |
            # Doesn't work with nounset:
            func "${arr[@]}"
          - |
            # Expands to 0 arguments instead of 1:
            declare -a arr=('')
            func "${arr[@]+"${arr[@]}"}"
      - title: unset
        do:
          - |
            unset -v 'arr[x]'
            unset -v 'arr[$i]'
        dont:
          - |
            # May break due to globbing:
            unset -v arr[x]
            # In addition, possible quoting problem:
            unset -v arr[$i]
            # Doesn't work for some reason:
            unset -v 'arr["x"]'
            unset -v 'arr["]"]'
            # Also rejected:
            unset -v 'arr["$i"]'

            # An insightful discussion on the topic:
            # https://lists.gnu.org/archive/html/help-bash/2016-09/msg00020.html
  - title: errexit
    topics:
      - title: Command substitution
        do:
          - |
            shopt -s inherit_errexit

            foo() { echo foo ; }
            bar() { false ; echo bar >&2 ; }

            output="$( bar )"
            foo "$output"

            # If inherit_errexit is unavailable, you can do
            #output="$( set -e; bar )"
        dont:
          - |
            foo() { echo foo ; }
            bar() { false ; echo bar >&2 ; }

            # This will print both "foo" and "bar":
            foo "$( bar )"
            # This will also print "foo":
            foo "$( false )"
          - |
            foo() { echo foo ; }
            bar() { false ; echo bar >&2 ; }

            # This will still print both "foo" and "bar".
            output="$( bar )"
            foo "$output"

            # This won't print anything.
            output="$( false )"
            foo "$output"
      - title: Process substitution
        do:
          - |
            shopt -s lastpipe

            result=()
            cmd | while IFS= read -r line; do
                result+=("$( process_line "$line" )")
            done
        dont:
          - |
            # Without lastpipe, the loop is executed is a subshell,
            # and the array will be empty:
            result=()
            cmd | while IFS= read -r line; do
                result+=("$( process_line "$line" )")
            done
          - |
            # errexit doesn't work for <( cmd ) no matter what:
            while IFS= read -r line; do
                process_line "$line"
            done < <( cmd )
            # This will be printed even if cmd fails:
            echo 'should never see this'
          - |
            # This breaks if $output contains the \0 byte:
            output="$( cmd )"

            while IFS= read -r line; do
                process_line "$line"
            done <<< "$output"
      - title: Functions
        do:
          - |
            foo() { false ; echo foo >&2 ; }

            foo
            echo ok
        dont:
          - |
            foo() { false ; echo foo >&2 ; }

            # This will print "foo" no matter what.
            if foo; then
                echo ok
            fi

            # Same below.
            foo && echo ok
            foo || echo fail

            # It currently appears to be completely impossible to
            # execute a function inside a conditional with errexit
            # enabled. Therefore, you should try to avoid this
            # whenever possible.
---
{% for feature in page.features %}
  <h2>{{ feature.title }}</h2>
  {% for topic in feature.topics %}
    {% if topic.title %}
      <h3>{{ topic.title }}</h3>
    {% endif %}
    <div class="row">
      <div class="col-md-6">
        {% for guide in topic.do %}
          <div class="pre_container pre_do">
            {% highlight bash %}{{ guide }}{% endhighlight %}
            <div class="pre_mark"><span class="glyphicon glyphicon-ok"></span></div>
          </div>
        {% endfor %}
      </div>
      <div class="col-md-6">
        {% for guide in topic.dont %}
          <div class="pre_container pre_dont">
            {% highlight bash %}{{ guide }}{% endhighlight %}
            <div class="pre_mark"><span class="glyphicon glyphicon-remove"></span></div>
          </div>
        {% endfor %}
      </div>
    </div>
  {% endfor %}
{% endfor %}