This repository has been archived on 2024-01-22. You can view files and clone it, but cannot push or open issues or pull requests.
speedie-page/articles/Shell script tricks - Get better at shell scripting.md

122 lines
5.5 KiB
Markdown

Today I'm going to talk about some dumb things people do in
shell scripting, and I'm also going to tell you a better way
to do it, as well as a cool trick.
## Don't use echo
`echo` seems simple right? `echo Hello World!` will print out
'Hello World!' to standard output. Despite being such a simple
command and likely one of the first UNIX programs you interact
with, echo is garbage.
The main reason you should not be using echo is because the way
it works will differ depending on your operating system. For instance,
`echo -e` on Arch Linux will interpret backslash escapes such as newlines,
however on other distributions like Gentoo, `-e` is intepreted as text.
So on some systems, `echo -e 'Install Gentoo\n'` will result in 'Install Gentoo'
being printed and on others it will result in ' -e Install Gentoo' being
printed. In short, the reason echo sucks is because it does not work the same
on all operating systems.
What should I use then, you might say. The answer is use `printf`. `printf` is
designed to print more complex text, and can do way more than `echo` can.
The first difference you're likely going to notice if you've been using `echo`
is `printf` does not append a `\n` newline character. This means
```Shell
printf "a"
printf "b"
printf "c"
```
will result in 'abc' being printed. Of course, the solution is to just append
`\n` to the text you're printing. So `printf 'Hello world\!\n` will result
in the same thing as `echo 'Hello world\!'`.
If you're a C or maybe C++ programmer, you're likely already very familiar
with `printf` and related functions, and the core utility works almost exactly
the same.
Do not do something like `printf "$MyVar"` though. That's incorrect usage of
`printf` even though it *does* technically work. The correct way to use `printf`
with variables is `printf 'MyVar1: %s\nMyVar2: %s\n' "$MyVar1" "$MyVar2"`.
`%s` here means string, but if you have an integer, you can use `%d`. It is
pretty safe to use `%s` for everything though, because you don't really have
different data types in shell scripting.
## Mostly useless Bash-isms
The Bash shell provides notable useful features, such as arrays. This is a
valid use case for Bash. Sometimes using Bash will increase speed *because*
of these features.
But when people are new to shell scripting, they tend to overcomplicate things,
and that can result in big problems. I'm guilty of this as well, which is why
I'm telling you this. Let's say you want to check if $DISPLAY is defined
or not. You know, to check if X or Wayland is used. The brainlet way to do
this would be something like this:
```Bash
if [[ -n "$DISPLAY" ]]; then
x=true
fi
```
This *does* work in case you are using Bash, but it is really ugly. You should
write POSIX compliant shell scripts unless you depend on a Bash feature which
simply cannot be had with POSIX sh. This is the most useless use of Bash I can
think of. In order to make this POSIX compliant, you could do this:
```Shell
if [ -n "$DISPLAY" ]; then
x = true
fi
```
Yep, it's that simple. Using an `if` statement here at all is dumb too, though
but not as bad. The good way to do this is simply `[ -n "$DISPLAY" ] && x=true`.
The worst I've seen (and done) is when people use Bash-isms and then proceed to
put `#!/bin/sh` or `#!/usr/bin/env sh` at the top. At first glance, it may
appear to work just fine. This is likely because /bin/bash is a symlink to
/usr/bin/bash and so Bash is used anyway. If the user is using a strictly
POSIX compliant shell like Dash instead, the script will not run properly.
If you write bash scripts, you should put `#!/bin/bash` or `#!/usr/bin/env bash`
at the top, so that Bash specifically interprets the script.
Another almost completely useless Bash feature is `source`. It is nearly identical
to the POSIX compliant `.` which simply loads functions and variables from a file.
Refrain from using `source` even when writing Bash scripts though, because it's
completely unnecessary and a bad habit.
## Loading in functions when necessary
This is not something bad people do but rather something I've found very
useful. If you have a big script, having to read a bunch of functions every time
the script is being interpreted by a shell might waste a lot of time.
If you find that this is the case, you could move some functions to a
separate script and use `.` to load that script in when you actually
need to use those functions.
spmenu_run and packr both do this because it results in a pretty big
speed improvement. spmenu_run is almost 1000 lines of Bash, and splitting
the script like this cut the time it took for stuff to be printed in half.
In the case of packr, if you're installing a program you might not need
to have any functions for removing programs. Why not load in the functions
you need and no more? Well, there are some notable issues with this. It
requires keeping track of more scripts, and if those scripts happen to
be missing, your main script will not work. I think this is worth it
though but only if you have really big scripts. For a 100 line shell script,
this just isn't worth it because the slowdown isn't noticeable.
## Conclusion
Shell scripting is basically magic, but you need to use it properly.
The reason people aren't being taught to use the right tools for the job
is because most articles regarding shell scripting are written for Bash
specifically and not POSIX compliant scripts. This teaches new hackers to
write Bash scripts when Bash is not required to get the job done.
In any case, hope this blog post was informative, have a good day!