Bash Core

The core.bash file serves as the main Opal file. It’s the only required file. All the others create a nicer experience. When you want to create a custom script, and don’t need the entire framework, you can include only this file.

#!/usr/bin/env bash
source ~/opal/bash/core.bash

Export Variables

export OPAL_VERSION="3.0.0"

# The $OPAL_CONFIG_DIR is not meant to replace the XDG_CONFIG_HOME directory.
# Rather it's an additional directory - one that could be used in the
# XDG_CONFIG_DIRS list of directories.
export OPAL_CONFIG_DIR="${OPAL_DIR}/config"

# This makes the log file accessible as a variable. However, it's preferred that
# the opal:data_dir, opal:state_dir, opal:cache_dir and opal:config_dir
export OPAL_XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}/opal"
export OPAL_LOG_FILE="${OPAL_XDG_STATE_HOME}/error.log"

OPAL_DIR

The OPAL_DIR holds the Opal install directory. This should be directly under your $HOME directory. Opal was designed so that $HOME/opal is where it resides.

OPAL_CONFIG_DIR

The OPAL_CONFIG_DIR is a supplemental configuration directory. It’s how the project can provide support for third party applications. This is a “top level” directory, and each supported application has a subdirectory. The primary purpose is to allow you to get setup faster by providing a solid, simple foundation. Should you find yourself wishing that the configuration was different. Don’t change these configuration files. Instead, create a copy of these files, and put it in your dotfiles project.

Functions

The core file core.bash contains all the essential functions of the Opal framework. There are several Bash files that make up Opal, but core.bash is meant to provide the foundation. One reason this is important, is becuase you might want to create your own Bash app. Loading just this file makes it easy for you to create while keeping things small, without having to override different parts of the framework.

This file is divided into multiple sections. Each sections contains multiple functions.

General

opal:version

Display the current version of the Opal framework. Uses the OPAL_VERSION variable.

@output String
$ opal:version
Opal version: 3.0.0-beta-0406
Bash version: 3.2.57(1)-release

opal:about

Display notice about what Opal is, where to find the repo, and copyright notice.

@output String
$ opal:about
Opal version: 3.0.0
Bash version: 3.2.57(1)-release

Opal is a command line framework. It's a foundation to provide a consistent
foundation across machines and users. It's meant to be extended. Version 3
improves the scripting experience.

For the most up-to-date version, and the full documentation, visit the
Github repo at https://github.com/andrewwoods/opal

Copyright (C) 2023-2026 Andrew Woods

opal:std_error

Use this to write a message to Standard Error. This makes it easier for people to write an error message that can be redirected.

@param String $message
The user-facing error message

@output String
The same message but redirected to STDERR
$ opal:std_error "Something went wrong"

opal:std_log

Use this to write a message to Standard Error and to the log. This makes it easier for people to write an error message on the command line.

@param String $message
The user-facing error message

@output String
The same message but redirected to STDERR

@return 0 | 1

Write a custom log message when your script fails.

$ script.sh || opal:std_log "Write a custom log message."

Inside of a function, you might call it like this

if opal:is_unset $1; then
    opal:std_log "You forgot to specify a parameter"
    return 1
fi

opal:is_set

Check to see if a value is present.

This is a wrapper function for Bash’s -n check. The value has a length greater than zero.

@param String | Int $value
The user’s content

@output bool
name="World"
if opal:is_set "$1"; then
    name="$1"
fi
echo "Hello $name"

opal:is_unset

Check to see if a value is not present.

This is a wrapper function for Bash’s -z check. A null or empty string will return true.

@param String | Int $value
The user’s content

@output bool
if opal:is_unset "$1"; then
    opal:std_error "Please specify a value"
    return 1
fi

opal:is_empty

An enhancement of opal:is_unset

A null or empty string will return true. This function additionally trims whitespace from both ends of the input for comparison. So a string consisting entirely of whitespace will evaluate true.

@param string $value

@output bool
if opal:is_unset "$1"; then
    opal:std_error "Please specify a value"
    return 1
fi

opal:success

Write a success message written in a bright green color.

@param String $message
The user-facing error message

@output string
The same message wrapped in terminal escape codes
opal:success "This is your message"

opal:failure

Write a failure message written in a bright red color.

@param String $message
The user-facing error message

@output String
The same message wrapped in terminal escape codes
opal:failure "This is your success message"

opal:message

Write a informational message written in a bright cyan color.

@param String $message
The user-facing info message

@output String
The same message wrapped in terminal escape codes
opal:message "This is your informational message"

opal:label

Write a label written in a bright yellow color.

@param String $message
The user-facing error message

@output String
The same message wrapped in terminal escape codes
opal:label "This is your label"

opal:speak

Read out loud a string of text. Assumes the say command is installed.

@param String $message
The user-facing content

@output String
The same message wrapped in terminal escape codes

@uses say
opal:speak 'Would you like to play a nice game of chess?'

opal:ask

Prompt the user with a statement and receive their input

@param String $prompt
What is your request of the user

@output String
the user input

@uses opal:label
$ answer="$(opal:ask "Would you like to play a nice game of chess?")"
$ echo "The answer was $answer"

opal:sleep

Pause execution for a limited number of seconds. Default is 5 seconds.

This wraps the standard sleep command. The benefit is to tell you how long it will sleep. It also prevents execution from sleeping forever, by ensuring a default value is provided.

@param int $seconds

@output string
The same message wrapped in terminal escape codes
$ opal:sleep
Using default value.
Sleeping for 5 seconds

$ opal:sleep 3
Sleeping for 3 seconds

Conditionals

Bash treats comparison for strings differently than for numbers. So there are different operators that are used.

These functions are made to provide wrappers to Bash’s handling of equality, which is not very intuitive. They’re meant to improve the developer experience. Part of this effort is to not use abbrevation is function names. This increases readability and understanding. For example, the name number_at_least is a simpler way of saying greater than or equal to without abbreviation.

Bash by default treats strings as case-sensitive.

opal:str_equals

Provides a function to check for string equality.

@param string

@param string

@output bool
fruit="Apple"

#
# Bash Native logic
#
if [[ $fruit == "Apple" ]]; then
    echo "Apple is the fruit of the month"
fi

#
# With Opal, the conditional can now expressed like this
#
if opal:str_equals "$fruit" "Apple"; then
    opal:success "Apple is the fruit of the month"
fi

opal:str_unequals

A common pattern is to use negation to flip the truth of a logicial expression. Borrowing from the previous example, we might write the following

@param string

@param string

@output bool
if ! opal:str_equals "$fruit" "Apple"; then
    opal:failure "Apple is not available"
fi

Opal provides a function to combine that logic into a single function name: str_unequals. This is in the spirit of Perl’s unless keyword.

if opal:str_unequals "$fruit" "Apple"; then
    opal:failure "Apple is not available"
fi

opal:number_equals

Check for the equality of two numbers.

@param integer

@param integer

@output bool
#
# Bash uses $? to expand to the exit status of the most recently
# executed command.
#
if opal:number_equals $? 0; then
    opal:success 'The command succeeded'
else
    opal:failure 'The command failed'
fi

#
# If you only wanted to check for failure, you can
# negate the condition.
#
if ! opal:number_equals $? 0; then
    opal:failure 'The command failed'
fi

opal:number_at_least

The name number_at_least is a simpler way of saying greater than or equal to, and without using an abbreviation.

@param Integer $value

@param Integer $minimum

@output Bool
#
# In the shell, an exit status of 0 is zero. Any integer 1 or more represents
# an error.
#
if ! opal:number_at_least $? 1; then
    opal:failure 'The command failed'
fi

opal:number_is_above

This is a simpler way to say greater than the minimum value.

@param Integer $value

@param Integer $minimum

@output Bool
#
# Check the current temperature.
#
if opal:number_is_above $temp_f 86; then
    opal:message 'It is hot! A great day to watch a baseball game!'
fi

opal:number_at_most

This is a simpler way to say less than or equal to the maximum value.

@param Integer $value

@param Integer $maximum

@output Bool
#
# Check the current temperature.
#
if opal:number_at_most $temp_f 32; then
    opal:message "It's freezing "
fi

opal:number_is_below

This is a simpler way to say less than a maximum value.

@param Integer $value

@param Integer $maximum

@output Bool
#
# Check the person's height.
#
if opal:number_is_below $height_inches 48; then
    opal:message "You must be at least this tall (48 inches) to ride this ride"
fi

opal:number_between

Determine if a number is inclusively between a minimum and a maximum value.

@param Integer $value

@param Integer $minimum

@param Integer $maximum

@output Bool
#
# Check the year an album was released.
#
if opal:number_between $year_released 1980 1989; then
    opal:success "The album was released in the best decade"
fi

opal:command_exists

Check that a command exists on the system.

@param String $command_name

@output Bool

@uses type command to perform the check
#
# Check if command exists.
#
if opal:command_exists $command_name; then
    opal:success "The command is available. Go ahead and run it"
fi

opal:function_exists

Check that a bash function is available on the system.

@param String $function_name

@output Bool

@uses type command to perform the check
#
# Check the function is loaded into the shell
#
if opal:function_exists $function_name; then
    opal:success "The function is available. Go ahead and run it"
fi

Logging

The ability to record errors about our code is important. Getting feedback about our code is how we understand to make it better. Opal provides a collection of logging functions, one for each of the eight syslog levels, defined in RFC 5424.

opal:log

Write a message to the error log, including the log level. This does the actual writing to the log file.

@param String $level
The logging level of the message. The RFC 5424 determines the levels.
The level determines the importance of the message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.
$ opal:log ERROR "Something went wrong"

opal:log_emergency

Log an EMERGENCY-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_emergency "Something went very wrong. Fix it STAT!"

opal:log_alert

Log an ALERT-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_alert "Something went very wrong. Please respond!"

opal:log_critical

Log a CRITICAL-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_critical "Oh No! Something went very wrong!"

opal:log_error

Log an ERROR-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_error "An error has occurred!"

opal:log_warning

Log an WARNING-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_warning "An error has occurred!"

opal:log_notice

Log a NOTICE-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_notice "hrmm, that's weird"

opal:log_info

Log an INFO-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_info "This is something you should know."

opal:log_debug

Log an DEBUG-level message.

@param String $message
The message you want to write to the error log.

@param String $filepath
Optional. The log file $HOME/.local/state/opal/error.log will be
if not specified.

@uses opal:log
$ opal:log_debug "A developer wanted to know this."

Time

We often need to display dates and times differently, depending upon where you are in the world. So it’s helpful to have functions for improving the user experience of manipulating dates and times.

These functions are in Bash Datetime

File System

These functions are in Bash System