If you need to configure your shell environment, review this shell scripting tutorial. It’s a solid starting point for configuring your a environment. I’m here to share how I configure my shell environment.

I configure my macOS shell environment in the following ways:

I use the best package manager Homebrew, to install and manage software packages.

My shell interpreter is /bin/bash, and I apply many productivity customizations.

I still haven’t discovered a compelling reason to use /bin/zsh, but compare between bash and zsh for yourself.

I prefer to keep shell scripts simple. If I need plugins, arrays, or globs, I use Python. Hence there’s no need to use fancy bash or zsh features.

I’d make my scripts public, but shell environments are personal, so they’re rarely useful to others. If you see something you like drop me a line.

My directory structure:

$ ls -1 -R
bash_profile.sh
bash_profile_canzan.sh
bash_profile_nike.sh
configure_aliases.sh
configure_exports.sh
configure_functions.sh
configure_git.sh
configure_paths.sh
configure_shell.sh
configure_software.sh
configure_system.sh
functions
gitconfig
gitignore_global
install_crontab.sh
install_crontab_md5
install_software.sh
install_software_debian.sh
install_software_dnf.sh
install_software_macos.sh
macos
nike
scripts
shellcheck.log
tests
vimrc

./functions:
aws
code_functions.sh
containers
git_functions.sh
nike
postgres_functions.sh
system 
website_functions.sh

./functions/aws:
aws_cloudformation_functions.sh
aws_cloudfront_functions.sh
aws_cloudtrail_functions.sh
aws_dynamodb_functions.sh
aws_ec2_functions.sh
aws_ecs_functions.sh
aws_functions.sh
aws_iam_functions.sh
aws_loadbalancer_functions.sh
aws_pricing_functions.sh
aws_s3_functions.sh
aws_sts_functions.sh

./functions/containers:
docker_functions.sh
podman_functions.sh

./functions/nike:
<snip; security>

./functions/system:
app_functions.sh
file_functions.sh
find_functions.sh
linux_functions.sh
list_functions.sh
macos_functions.sh
network_functions.sh
productivity_functions.sh
shell_functions.sh
ssh_functions.sh
ssl_functions.sh
system_functions.sh
systemctl_functions.sh
vbox_functions.sh
video_functions.sh

./macos/applescript:
turn-volume-to-0.scpt
turn-volume-to-5.scpt

./tests:
bash_profile_test.sh

Directory structure:

  • functions/ contains functions that are used by the configure scripts.
  • tests/ contain tests which I run with a GitHub pipeline that fails if shellcheck or the tests fail.
  • macos/ contains macOS specific scripts.

Key files:

bash_profile.sh is the main entry point. It calls all the other scripts. bash_profile_nike.sh is the main entry point for my work laptop. configure_*.sh configure the shell environment. install_*.sh contain software packages & runtime scripts. functions/*.sh contains functions used by scripts and at the command-line. shellcheck.log is the shellcheck log. gitconfig is symlinked to $HOME/.gitconfig on all my systems like my homelab, work computer, and several containers. gitignore_global is symlinked to $HOME/.gitignore and ignores annoying files like .DS_Store. vimrc is symlinked to $HOME/.vimrc and contains my vim configuration like syntax on.

My install_crontab.sh script adds recurring jobs like auto sound adjustments and rsync jobs.

My scripts are POSIX compliant and I use shellcheck to validate them at startup. I run them in containers, on macOS, and Linux.

My $HOME/.bashrc is empty.

I only use .profile to configure Fig and run my bash_profile.sh, script with an alias. This speeds up my shell startup by only loading necessary scripts. I never load nvm and have an enable_nvm script to run when required. It’s a bloated tool that I only use when required.

I use several software development tools to enhance my productivity at the command line.

If any of my methods sounds interesting, drop me a line, and I’ll share more details.