From be5e0048913797f2ef91065908730da025113c34 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Sat, 6 Jun 2026 22:23:54 +0200 Subject: move Jekyll source files to src/ --- src/_notes/bash.html | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/_notes/bash.html (limited to 'src/_notes/bash.html') diff --git a/src/_notes/bash.html b/src/_notes/bash.html new file mode 100644 index 0000000..c401522 --- /dev/null +++ b/src/_notes/bash.html @@ -0,0 +1,184 @@ +--- +title: bash +subtitle: best practices +layout: nosidebar +links: + - {rel: stylesheet, href: 'assets/css/guides.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 %} +

{{ feature.title }}

+ {% for topic in feature.topics %} + {% if topic.title %} +

{{ topic.title }}

+ {% endif %} +
+
+ {% for guide in topic.do %} +
+ {% highlight bash %}{{ guide }}{% endhighlight %} +
+
+ {% endfor %} +
+
+ {% for guide in topic.dont %} +
+ {% highlight bash %}{{ guide }}{% endhighlight %} +
+
+ {% endfor %} +
+
+ {% endfor %} +{% endfor %} -- cgit v1.3