A (human) index that likes to code
Also drinks way too much coffee
Published Mar 07, 2020 15:00
By no means am I a professional at bash scripting. That being said, I’ve done some pretty cool projects with just pure bash scripting, like ContainerTop, a container-based desktop environment launcher, and ungoogled-chromium-builder to let my private server build ungoogled chromium for my laptops.
At work, I also write a bunch of utility aliases and functions to aid my work - these are my “commonly utilized” techniques and commands condensed into a blog post. This article is written as a “cheatsheet”, hopefully to serve as a quick reference for myself; though, if you find it useful, you can bookmark this page!
NOTE: When writing bash scripts, make sure to run chmod +x ./relative/path/to/script
at least once to flag the script as an executable.
Any executable file containing the “shebang” in its first line:
#!/bin/bash
Will cause the script to be executed by the specified executable, which also means the script needs to be in a language understood by those executables. Some common shebangs I’ve write are: /usr/bin/ruby
and /usr/bin/python3
, which uses the Ruby and Python interpretor for the script respectively. Most people publishing scripts for usage online use /usr/bin/env ruby
and /usr/bin/env python3
instead, allowing users with non-standard or different interpretor locations to use the scripts.
Redirects use the operators <
and >
. By default, inputs come from STDIN
and outputs go to STDOUT
, which are typically your terminal input and output respectively. This behaviour can be changed with <
and >
.
For example,
echo "hello" > file
Will write the word “hello” out to file
, which is a normal file.
cat < file
Will read the word “hello” from file
, which is the same file the “echo” has written “hello” to.
Redirects can let you log the outputs of commands:
cat /var/log/syslog 2>&1 > log
Whatever expression in front of >
will produce outputs that go into the log file. 2>&1
instructs bash to redirect STDERR
(2) to STDOUT
(1). This is quite helpful for commands that don’t typically output to log files.
The ampersand symbol at the back of a command will cause it to run in the background:
sleep 999 &
When a background command is executed within an interactive shell, you can switch back to it with fg
and switch away from it with CTRL+Z
. Use ps
to view the processes currently running in the terminal session. For a terminal session with no background processes, you should see two items: bash
itself, and ps
.
Subshells are created with the following syntax:
$(echo "hi")
The results are assignable to a variable, but without protection, it can run arbitrary commands. Try this:
$(echo "echo oh no")
This will print oh no
instead of echo oh no
. Such arbitrary commands can be avoided by enclosing the subshell with quotes (“). This works because $
is an escape cue for bash when interpreting strings.
VARIABLE="$(echo 'echo oh no')"
Variables can be defined like this:
VARIABLE="hello"
It can be used with “$VARIABLE”:
echo $VARIABLE
echo "$VARIABLE"
Both variants should print hello
.
To inhibit printing, use single quotes (‘):
echo '$VARIABLE'
The line should print $VARIABLE
.
There are two ways to achieve this: (i) set -e
and (ii) || exit 1
.
set -e
test -x thisfiledoesnotexist || exit 1
It is highly recommended to use the latter, as the behaviour is more well-structured and well-defined. set -e
causes the shell to exit on error if any subcommands return with an error (defined as non-zero return status), while || exit
only exits on that particular line.
To know the last return code of a command, use $?
.
echo "this should succeed"
echo "$?" # prints 0
Remember that 0
is success.
Different control statements have different ending statements. Below show some examples.
if [[ -e "/var/log/syslog" ]]; then
echo "congratulations, you have a system log"
fi
case "$VARIABLE" in
7)
echo "lucky seven"
;;
10|16)
echo "my favourite number"
;;
42)
echo "universe number"
;;
*)
echo "nothing special"
;;
esac
for NUMBERS in 1 2 3 4 5
do
echo "$NUMBERS"
done
Arrays can be defined like this:
ARRAY=(1 2 3 4 5 6)
echo "${ARRAY[@]}" # prints 1 2 3 4 5 6
echo "${ARRAY}" # prints 1
echo "${ARRAY[0]}" # prints 1
Turning a string of space-delimited strings into an array can be done with the following command (thanks to this link):
IFS=" " read -r -a ARRAY <<< "1 2 3 4 5"
echo "${ARRAY[@]}" # prints 1 2 3 4 5
Use arrays in for loops like this:
for NUMBERS in ${ARRAY[@]}
do
echo "$NUMBERS"
end
Files can be recursively found using the find
command:
find . -name "*.js"
change .
to a directory of your choice. The pattern defined by -name
supports the metacharacters: *
, ?
and []
, but only works for filenames, so a pattern of a/b
cannot be used. Instead, use the -prune
option to remove directories that should not be included in the search. (Thanks to this)
Use the -prune
command to remove directories you are not interested in, like this:
find . -path "Documents" -prune -o -name "*.zip" -print
find . -type d \( .path dir1 -o -path dir2 -o path dir3 \) -prune -o -print
Strings can be replaced with the sed
command (among other utilities):
echo "i think apples are great" | sed 's/apples/oranges/g'
It might sometimes be necessary to use the -E
flag for sed
when a more complicated regex is used. I typically use regexr to build my Regex. The ‘s/’ is a sed
command that means “substitute”, with the ‘/g’ at the back representing “replace everything”. The one page GNU sed
manual can be found here, or obtained via the man sed
command.
The escape character for sed
is \
(backslash).
To replace a string in a file, simply provide an input file instead of piping the input:
touch aFile
cat << EOF > aFile
i
like
oranges
EOF
sed -i 's/oranges/pears/g' aFile
The -i
flag means “edit file in-place”.
Strings can be recursively found in directories using grep
(among other utilities):
grep "important string" -R . # searches all files recursively for important string
grep -E "[aeiou]+" -R . # searches all files for strings with one or more vowels
grep -E "[aeiou]+" *.js # searches all .js files for strings wiht one or more vowels
To pass the left-hand-side of a pipe as an argument to the right-hand-side for commands that don’t support reading from pipes, use xargs
.
echo "newBranch" | xargs git checkout -b
If you have text like this (you can get something like this with git branch
):
> git branch
* master
develop
bug-fixes
You can make the output processable by removing the first two columns (which contain the asterisk) with:
git branch | colrm 1 2
Output:
master
develop
bug-fixes
Say you have a string with newlines, like this: "hello\nworld"
. If you assign it to a variable after processing like this:
VARIABLE="$(echo -e "hello\nworld")"
echo $VARIABLE
Your output would be "hello world"
. Be aware of this issue when writing scripts; this can probably be mitigated by using tr
:
echo $VARIABLE | tr ' ' '\n' | cat
Output:
hello
world
That should be all for now. I may occasionally pop by to update this page, but the permalink should still stay the same. Keep a bookmark if you found it useful!
Happy coding,
CodingIndex