Bash (scripting, not shell)
Created: 2016-03-25 15:31:50 -0700 Modified: 2022-07-04 15:09:22 -0700
Basics
Section titled Basics- Good Gist here with some general guidelines
- ShellCheck is a script analysis tool for shell scripts
- Safe scripting (e.g. by exiting on any error): reference
- For any of the following, just put a line at the top of the script, e.g. “set -Eeuo pipefail”.
- ↑ In fact, that command is a good start for most Bash scripts to make it work like how you’re used to with most programming languages.
- set -e: exit immediately when a command fails
- set -o pipefail: sets exit code of a pipeline (piped commands) to the rightmost failing command
- set -u: treat unset variables as an error and exit immediately
- set -x: print every command before executing it. It’s like Batch’s “@echo on”
- For any of the following, just put a line at the top of the script, e.g. “set -Eeuo pipefail”.
- .bash_profile vs. .bashrc (reference, reference for zsh): in short: bash_profile is for commands that should only run once (like setting your $PATH), bashrc is good for commands that should run every time you start a new shell.
- Interactive vs. login shell (reference): in short: a login shell is when you logged into the system through a shell, e.g. SSHing into a machine. An interactive shell is like when you start a new terminal on macOS.
- Check if a command was successful (reference)
some_commandif [ $? -eq 0 ]; then echo OKelse echo FAILfi
You can also do this
some_commandretval=$?do_something $retvalif [ $retval -ne 0 ]; then echo "Return code was not zero but $retval"fi
- Return
- “return” isn’t required from all functions
- Arguments
- Reading script-level arguments is the same as reading function-level arguments: 2, etc.
- I find it nice to name arguments in functions anyway:
add_two_numbers() { OPERAND_ONE=$1 OPERAND_TWO=$2
(( result = OPERAND_ONE + OPERAND_TWO ))}
add_two_numbers 5 6echo Result is: $result
- Comparing arguments is easy
if [[ "$1" != "deploy" && "$1" != "delete" ]]; then echo "You need to specify either 'deploy' or 'delete'" exit 1fi
- Checking if an argument is empty (reference)
if [ -z "$1" ] # Note: to check if it's not empty, just do if [ ! -z "$1" ] then echo "No argument supplied"fi
-
To pass in an empty argument, specify open and closing quotation marks like this:
- ./foo.sh "" second_arg "" fourth_arg
-
Using temporary buffers
Suppose you have sounds.json like this:
{ "frog": "REPLACE_ME", "dog": "woof"}
You can modify REPLACE_ME without having to make a temporary file by using a temporary buffer:
cat <(cat sounds.json | sed s/REPLACE_ME/ribbit/g)
↑ [09:24] Pneumatus: that sed command in the temporary buffers section, you know there’s an ‘-i’ arg to sed that lets you do in-place replacement? :)
[09:25] Pneumatus: sed -i ‘s/REPLACE_ME/ribbit/g’ sounds.json
[09:25] Pneumatus: or maybe the other way round
- If/elif/end
# This is just a simple example that does nothing helpful:if [[ "$arg" == "..." ]]; then # Go up 2 directories cd ../..elif [[ "$arg" == "...." ]]; then # Go up 3 directories cd ../../..else # Don't do anythingfi
Helpful snippets
Section titled Helpful snippets- Set error handling: set -Eeuo pipefail
- This was mentioned earlier, but it makes your environment more like a programming environment.
- Write to stderr and then exit (reference)
die () { echo >&2 "$@" exit 1}
This is useful for doing something like:
Section titled This is useful for doing something like:echo ’ || die “Numeric argument required, $1 provided”
Evaluating a scripting expression
Section titled Evaluating a scripting expressionYou’re supposed to use $():
cur_date = $(date)
It used to be that you’d use backticks, e.g.
cur_date=date
(do not use this since it’s deprecated)
Evaluating an arithmetic expression (reference)
Section titled Evaluating an arithmetic expression (reference)Simple example:
foo=5bar=6(( baz = foo + bar ))echo $baz # This shows 11
For floating-point calculations, use bc.
[14:22] woodworker_: so {varname} is for using variable names when you do not have spaces so you can do {VAR2}
So: (echo $VAR)
“test”
Section titled “test””[ … ]” is syntactic sugar for the “test” command, meaning if you’re having problems with it, you can look through “man test”. Example usage:
if [ ${var}=YES ]
Accessing your bash_profile functions from a script
Section titled Accessing your bash_profile functions from a scriptThe best way I found was just to add “source ~/.bash_profile” to the top of whatever script you’re writing, e.g.:
~/.bash_profile:
#!/bin/bash
function foo() {
echo hi
}
test.sh:
#!/bin/bash
source ~/.bash_profile
foo
Using ssh-agent from within bash (notes reference, GitHub reference)
Section titled Using ssh-agent from within bash (notes reference, GitHub reference)(just look at the references)
Section titled (just look at the references)Using “sudo” inside of a script
Section titled Using “sudo” inside of a scriptDon’t do something like this:
sudo -iu bldeploy
<some command that expects to be bldeploy>
Instead, do this:
run_as_bldeploy.sh
Section titled run_as_bldeploy.shsudo -u bldeploy impl.sh
impl.sh
Section titled impl.sh<some command that expects to be bldeploy>
If you really wanted, you could follow the advice from here and do something like “sudo bash -c ‘cmd1; cmd2; cmd3’“. That link also has advice for using ‘here’ documents.
Regex
Section titled RegexI had a string like this: <a bunch of garbage><a href=“http://127.0.0.1:8000/show/df8cd949661a”\>
I got the hash at the end by doing this:
if [[$curlOutput =~ show([a-zA-Z0-9]+)]]; then
bpasteHash=${BASH_REMATCH[1]}
else
echo “No match found”;
fi
The key was not using “w” like you have in other languages. I don’t think that works in Bash to represent a letter character.