
Bash scripting cheatsheet - GordonS
https://devhints.io/bash
======
ricardobeat
My bash scripting cheatsheet after maintaining a couple tools for 4+ years:
use system Ruby / Python or anything else instead, really. Once over a hundred
lines of code it becomes a massive pain to work with.

~~~
oxguy3
The biggest benefit to Bash scripting over a friendlier language is that Bash
is guaranteed to just work on pretty much every machine. Bash is on virtually
every *nix machine (except maybe embedded devices), and it's extremely
resilient to time. Looking through the change history ([https://wiki.bash-
hackers.org/scripting/bashchanges](https://wiki.bash-
hackers.org/scripting/bashchanges)), I'd wager that 99% of scripts don't use
anything that wasn't in Bash 2.05b, which came out in Jul 2002. Anecdotally,
the "newest" feature that I've ever used is regex support in conditional
expressions, which was added in 3.0-alpha (Aug 2004).

Ruby and Python, meanwhile, are not quite so universally installed as Bash,
and they have had all sorts of major breaking changes since 2004. And even if
they are installed and version-compatible, they still might not work.
Countless times have I seen a misconfiguration with Python 2 vs 3 or RVM or
the PATH variable or whatever else cause errors, but I have never run into a
scenario where a Bash script didn't just work.

All that said, I'm totally with you that Bash scripts should be short and
sweet. If you're doing something with over 100 lines, Bash's downsides
overtake its benefits.

~~~
roryrjb
It's true that bash can be installed on any Unix-like OS, but so can Python,
Perl, etc, but it's not available by default, on OpenBSD there's pdksh,
FreeBSD tcsh iirc, and on MacOS I believe the version of bash is really old
due to some previous license change.

Edit: I read your post quickly, you addressed the old bash scenario, but still
in my opinion I would always, and do target POSIX shell for maximum cross-
platform compatibility and that is definitely available on Unix-like OSs by
default.

------
ben509
> Note that [[ is actually a command/program that returns either 0 (true) or 1
> (false).

This is true for the [ command, which is the same as `test`, but the [[ ]]
syntax is parsed directly by bash, as is (( )). In particular, [[ ]] is not
subject to whitespace splitting, so you don't need to quote most variables.

They mention $(( )), but don't go into (( )), which gives you very nice
integer arithmetic and logic:

    
    
        for (( x = 0; x < 5; x += 2 )); do
           echo $x
           (( y = x * 10, z = y + 2 ))
           echo $y $z
        done

~~~
chubot
Yes I wrote about this on my blog:

 _[ Is a Builtin, But [[ Is Part of the Language_

[http://www.oilshell.org/blog/2016/10/12.html](http://www.oilshell.org/blog/2016/10/12.html)

I didn't know that before I started implementing a shell :)

I knew that [ could be a builtin or /usr/bin/[, but not about the difference
between [ and [[.

------
zimbatm
Here are two lesser-know tricks that can be quite handy:

Using () instead of {} for the functions body allows to isolate it:

    
    
        foo=5
        bar() (
          cd /
          foo=7
        )
        bar
        echo $PWD # not / but the current directory
        echo $foo # still 5
    

Bash 4.0 can splat array of arguments back into a valid bash string with @Q.
This is super useful when you need to stop using arrays, like with `bash -c`

    
    
        ary=("arg1" "arg 2")
        echo "${ary[*]@Q}"
        # outputs: 'arg1' 'arg 2'

------
karmakaze
My newly forming pet peeve are cheatsheets that are long scrolling documents
with way too much whitespace.

Cheatsheets are dense, well formatted items that either demonstrated a thing
or reminded you of a thing. Ideally they would be printable in either letter
or A4, possibly double-sided.

We need a different word for what this is. Tips/hints blog post?

~~~
gshubert17
Plus this page doesn't print nicely, either to paper or PDF.

------
iClaudiusX
A few things I would add:

brace expansions

    
    
      combine for combinations
    
        {a..c}{1..3}  # a1 a2 a3 b1 b2 b3 c1 c2 c3
    
      step size
    
        {0..10..2}  # 0 2 4 6 8 10
    

mapfile to read lines from file to array

    
    
      mapfile -t arrayname < filename
    
        -t     # remove trailing newline for each element
    
        -u FD  # read from file descriptor FD
    
        -s N   # skip first N lines
    
        -n N   # read at most N lines
    
        -O N   # start populating array at index N
    

array quoting

    
    
      "${array[@]}"  # expand all elements, individually quoted
    
      "${array[*]}"  # expand all elements, group quoted
    

translate number from base to decimal for base in (2,64)

    
    
      $(( base#num ))

------
mistrial9
.. looking at >1000 LOC bash project this week, with autotools install to make
them go.. it uses 'SOURCE' as an include mechanism.. runs FAST and uses lots
of whitespace, but no one on earth but the original author wants to touch it

~~~
ben509
If you want to port such a project:

Start by moving code into bash functions if it's not already doing that.

Declare variables with `local` to reduce the amount of global state.

Once you have code in functions, you can factor code into independent scripts.
Anything that runs in a subshell can move into its own script, so anything:

1\. in a pipe | line

2\. running in the background&

3\. in a ( subshell )

4\. that doesn't modify global variables

You can then work on those scripts piece by piece.

~~~
zimbatm
0\. run shellcheck and fix all the warnings

Using shellcheck will give the developer more confidence in their code, and
also learn through the feedback it gives

------
Domenic_S
Related, I love to send my bash scripts though
[https://www.shellcheck.net/](https://www.shellcheck.net/)

~~~
Karupan
Shellcheck is awesome! Been using it for about a month and it’s great to
understand certain behaviour in the script, even if I don’t end up fixing
every single warning.

------
egwynn
The very first example (echo "Hello $NAME!") won’t work if `histexpand` is
set, which I believe is the default.

~~~
blueflow
Also, '< file.txt | ...' does not work as well. Not sure if its well
researched.

~~~
ben509
Which is surprising since "research" here means pasting it into a terminal...

Maybe they meant `cat file.txt | while ...`? Even then, you never want to do
that when you're reading because elements of a pipeline are run in subshells.
The usual idiom is:

    
    
        while cond; do
           thing
        done < input > output
    

It's also the problem with cheatsheets: the underlying idea in bash is that
compound statements are like mini programs, so they have file handles that can
be redirected. That doesn't come through a cheatsheet very well.

------
gist
We used to use things like this way back. In fact I would purchase these
laminated sheets full of hints on various topics (c, unix, bash) and so on.

The thing is I actually find it quicker to simply use my own notes today
and/or google anything I need to know when I need to know it.

