Add new blog post
This commit is contained in:
parent
082cfdf9b3
commit
939be3a600
125
articles/Shell script tricks - Get better at shell scripting.md
Normal file
125
articles/Shell script tricks - Get better at shell scripting.md
Normal file
|
@ -0,0 +1,125 @@
|
|||
# Shell script tricks - Get better at shell scripting
|
||||
|
||||
2023-07-16
|
||||
|
||||
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!
|
|
@ -0,0 +1 @@
|
|||
2023-07-16
|
133
rss.xml
133
rss.xml
|
@ -3,6 +3,139 @@
|
|||
<title>speedie's blog</title>
|
||||
<description>speedie's blog, about stuff I want to talk about.</description>
|
||||
<atom:link href="https://speedie.site/blog" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Shell script tricks - Get better at shell scripting</title>
|
||||
<link>/blog.php/Shell+script+tricks+-+Get+better+at+shell+scripting</link>
|
||||
<guid>/blog.php/Shell+script+tricks+-+Get+better+at+shell+scripting</guid>
|
||||
<pubDate>Sun, 16 Jul 2023 00:00:00 +0000</pubDate>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<h1>Shell script tricks - Get better at shell scripting</h1>
|
||||
|
||||
<p>2023-07-16</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h2>Don't use echo</h2>
|
||||
|
||||
<p><code>echo</code> seems simple right? <code>echo Hello World!</code> 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.</p>
|
||||
|
||||
<p>The main reason you should not be using echo is because the way
|
||||
it works will differ depending on your operating system. For instance,
|
||||
<code>echo -e</code> on Arch Linux will interpret backslash escapes such as newlines,
|
||||
however on other distributions like Gentoo, <code>-e</code> is intepreted as text.</p>
|
||||
|
||||
<p>So on some systems, <code>echo -e 'Install Gentoo\n'</code> 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.</p>
|
||||
|
||||
<p>What should I use then, you might say. The answer is use <code>printf</code>. <code>printf</code> is
|
||||
designed to print more complex text, and can do way more than <code>echo</code> can.
|
||||
The first difference you're likely going to notice if you've been using <code>echo</code>
|
||||
is <code>printf</code> does not append a <code>\n</code> newline character. This means</p>
|
||||
|
||||
<pre><code class="Shell">printf "a"
|
||||
printf "b"
|
||||
printf "c"
|
||||
</code></pre>
|
||||
|
||||
<p>will result in 'abc' being printed. Of course, the solution is to just append
|
||||
<code>\n</code> to the text you're printing. So <code>printf 'Hello world\!\n</code> will result
|
||||
in the same thing as <code>echo 'Hello world\!'</code>.</p>
|
||||
|
||||
<p>If you're a C or maybe C++ programmer, you're likely already very familiar
|
||||
with <code>printf</code> and related functions, and the core utility works almost exactly
|
||||
the same.</p>
|
||||
|
||||
<p>Do not do something like <code>printf "$MyVar"</code> though. That's incorrect usage of
|
||||
<code>printf</code> even though it <em>does</em> technically work. The correct way to use <code>printf</code>
|
||||
with variables is <code>printf 'MyVar1: %s\nMyVar2: %s\n' "$MyVar1" "$MyVar2"</code>.
|
||||
<code>%s</code> here means string, but if you have an integer, you can use <code>%d</code>. It is
|
||||
pretty safe to use <code>%s</code> for everything though, because you don't really have
|
||||
different data types in shell scripting.</p>
|
||||
|
||||
<h2>Mostly useless Bash-isms</h2>
|
||||
|
||||
<p>The Bash shell provides notable useful features, such as arrays. This is a
|
||||
valid use case for Bash. Sometimes using Bash will increase speed <em>because</em>
|
||||
of these features.</p>
|
||||
|
||||
<p>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:</p>
|
||||
|
||||
<pre><code class="Bash">if \<a href="/blog.php/+-n+%22%24DISPLAY%22+-" class="noexist"> -n "$DISPLAY" \</a>; then
|
||||
x=true
|
||||
fi
|
||||
</code></pre>
|
||||
|
||||
<p>This <em>does</em> 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:</p>
|
||||
|
||||
<pre><code class="Shell">if [ -n "$DISPLAY" ]; then
|
||||
x = true
|
||||
fi
|
||||
</code></pre>
|
||||
|
||||
<p>Yep, it's that simple. Using an <code>if</code> statement here at all is dumb too, though
|
||||
but not as bad. The good way to do this is simply <code>[ -n "$DISPLAY" ] && x=true</code>.
|
||||
The worst I've seen (and done) is when people use Bash-isms and then proceed to
|
||||
put <code>#!/bin/sh</code> or <code>#!/usr/bin/env sh</code> 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 <code>#!/bin/bash</code> or <code>#!/usr/bin/env bash</code>
|
||||
at the top, so that Bash specifically interprets the script.</p>
|
||||
|
||||
<p>Another almost completely useless Bash feature is <code>source</code>. It is nearly identical
|
||||
to the POSIX compliant <code>.</code> which simply loads functions and variables from a file.
|
||||
Refrain from using <code>source</code> even when writing Bash scripts though, because it's
|
||||
completely unnecessary and a bad habit.</p>
|
||||
|
||||
<h2>Loading in functions when necessary</h2>
|
||||
|
||||
<p>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 <code>.</code> to load that script in when you actually
|
||||
need to use those functions.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h2>Conclusion</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>In any case, hope this blog post was informative, have a good day!</p>
|
||||
|
||||
]]>
|
||||
</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>I will say what I want</title>
|
||||
<link>/blog.php/I+will+say+what+I+want</link>
|
||||
|
|
Reference in a new issue