122 lines
5.5 KiB
Markdown
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!
|