I recently had a new Mac, and that made me revisit my terminal setup, which I’m now sharing with you.
The Terminal Itself
When I had my old MacBook in 2014, I wanted to go full experience, got iTerm2 and Oh My Zsh.
iTerm2 Vs Default Terminal
Later I learned that if I wanted to get a consistent experience, it’s easier to just use the built-in terminal.app, and it’s not that bad.
I still made some modifications to it. I found most commands work better with dark shell so I duplicaed the “Pro” theme and used it (a duplicate was just to increase font size).
I also used bash-git-prompt (via Homebrew) to get a beautiful prompt with full path and git status.
And there’s a lot more when I talk about dot files and .bashrc…
Oh My Zsh Vs Bash
I also learned that from time to time you’d hit a few subtle script differences between Oh My Zsh and the default Bash, usually arround having to quote some strings that Bash doesn’t require quoting. This made copying and pasting commands from tutorials etc a pain sometimes, so I defaulted to Bash this time.
You see the pattern here, I try to utilise the power of defaults.
Homebrew
There’re a lot more Brew apps than there are for Mac Ports. Some of you might even not have heard of the latter, so, I just use Brew.
One thing I learned over time is that if something has an installer, I’d better use that installer itself, unless the installer is really a shell script itself.
I’ll talk about each case separately below.
Terminal Dot File Tweaks: .profile, .bash_profile, and .bashrc
I got quite confused between the 3 files at first, and head to learn about all this crap about login shells and non login shells, and how Mac is not standard itself. I ended up changing my files as below:
.bash_profile
Run the terminal-type-agnoistic (forgive my lack of clever wording here) .profile
file if it exists:
1 2 3 4 |
if [ -f "$HOME/.profile" ]; then . "$HOME/.profile" fi |
.profile
Just run the ~/.bashrc
file if it exists
1 2 3 4 |
if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi |
.bashrc
That’s where I end up putting all my terminal customizations. The next steps are to divide these into smaller files kept in a directory really.
For example, that’s where I enable the git prompt, which was by following their README by the way if the code feels complex:
1 2 3 4 5 |
if [ -f "/usr/local/opt/bash-git-prompt/share/gitprompt.sh" ]; then __GIT_PROMPT_DIR="/usr/local/opt/bash-git-prompt/share" source "/usr/local/opt/bash-git-prompt/share/gitprompt.sh" fi |
And also show hidden files in completions:
1 2 |
bind 'set match-hidden-files on' |
Relative .bashrc Files!!
This is a good one, and the motivation for writing this blog post, seriously.
I like to have the latest version of everything I use, but I had a React Native project that required Node 8 (for some dependencies that are not updated yet), and Java 8 (for Android).
I decided that I’m going to install multiple versions of Java and Node (via NVM), and pre-select the correct version in my project folder using a .bashrc file.
I wanted the versions to be pre-selected for me so I have to remember to select them less often. Then came the idea:
What if the terminal could load a .bashrc file for me automatically based on directory?
I usually use VS Code integrated terminal, or use its menu to start the terminal app in the project directory, so that will work great.
But the .bashrc file will have to live outside outside of the repository, because I cannot modify the repository for everyone else or a file that gets lost with git clean -dfx
, so it needs to be something like:
1 2 3 4 |
- project-parent-dir |- .bashrc |- project-git-clone-dir |
And I did it simply by adding the following to the ~/.bashrc
file, the one in my home directory which gets loaded automatically as I explained:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# This finds the closest folder, starting with current, that has a .bashrc file # It's based on some StackOverflow question about finding .git folder :D bashrc_dir=$(pwd -P 2>/dev/null || command pwd) while [ ! -e "$bashrc_dir/.bashrc" ]; do bashrc_dir=${bashrc_dir%/*} if [ "$bashrc_dir" = "" ]; then break; fi done if [ -f "$bashrc_dir/.bashrc" ] && [ "$bashrc_dir" != "$HOME" ]; then # Let me know when you source non standard files! echo "sourcing: $bashrc_dir/.bashrc" # This loads the file . "$bashrc_dir/.bashrc" fi |
Remember that I’m a true newbie when it comes to this stuff. There’s probably a better way to write this, and I probably should have capitalised bashrc_dir
, and sure violated other bash conversions, but it works!
I’ll talk below about what it enabled me to do.
Bash Completions
For a long time I felt that I wasn’t getting any tab completion (auto-complete menu on pressing tab after a command) in my new setup. At first I thought it was just Bash vs Zsh, but I recently learned that I can enhance it a bit.
I installed bash-completion
using Homebrew, which was not enough by itself, so I added the following to my home dir ~/.bashrc
:
1 2 3 4 5 |
# Bash completions if [ -f $(brew --prefix)/etc/bash_completion ]; then . $(brew --prefix)/etc/bash_completion fi |
Now I started seeing completions. For example if I type npm run
inside a folder with package.json
file, and press TAB twice (not sure why twice), I see a list of npm scripts. Similarly if I’m in a git folder and type git checkout
TAB TAB, I get to see all branch name (or a warning if there are so many).
Watching Files for Development
A lot of stuff, especially NodeJS stuff, watch files for changes during development. Think Webpack or TypeScript compiling your files, or React Native packager watching files to send them to mobile device app, etc.
To make this work with no errors, there’s something to do, and something NOT to do.
DO NOT install fsevents
or watchman
using NPM global install. This will only cause issues.
Instead, just install watchman using Homebrew:
1 2 |
brew install watchman |
If you still have watchman
or fsevents
errors. Check if you have either of them installed via NPM, remove them, remove watchman
from Homebrew and install it again.
Git
I installed git via Homebrew. That way it’s pretty easy to update it. I also installed bash-git-prompt similarly as I mentioned above.
VSTS
Another tool I installed via Homebrew is git-credential-manager
, which allowed me to login to Visual Studio Team Services (VSTS) git repositories in a slightly fancier way (using browser login, that’s later cached).
GUI Client
Coming from Windows, I originally cired for lack of a good alternative for Git Extensions. SourceTree was the closesth but wasn’t close enough, and GitHub for Mac was overly simplified for me, that it felt lacking.
Nowadays I’m pretty happy using Fork which I got using the official installer. It is a great git client for when I want to look as graphical log or mess with my changed files diff before committing. I still use the terminal for switching branching, pulling/fetching/pushing, etc.
Terminal Intgeration
In Fork preference, the Integration tab, you’ll find a button to install fork
as a command to your terminal. This allows me to type fork .
in the terminal in any folder in my git repository to open that repository in Fork.
Diff Tool
For some reason my head refuses to understand any 3-way diff tool other than Kdiff3, which used to come with Git Extensions. I still use it now, installed via brew cask
.
VS Code
Since we are talking about modifying files here, I’d like to talk about how I modify them. I use VS Code, please do not hate me Vim users!
I use the Insiders edition of VS Code. This is like a daily build of VS Code. You get all new features weeks before others do, which is awesome. The Insiders edition is fairly stable in my experience, and the update is very quick on Mac too (unlike Windows), so it’s awesome.
To get the Insiders edition, I just google VSCode Insiders
and click on first result.
VS Code To Terminal
The only caveat of frquently updating VS Code is: because I don’t know when I might want to restart my VS Code for update, I avoid running long running tasks in its terminal.
I can press CMD+SHIFT+P to show command palette, and type new t
then select Open New Terminal
to open a new tab of the default terminal app, started in the project folder. You’ll see later why this is so cool.
Terminal To VS Code
To easily open files and folders in VS Code from the terminal, I can press CMD+SHIFT+P to show command palette, and type install
then select the command that installs the code-insiders
command in my PATH.
Sometimes I feel that this is a long command though, completion works startingfrom code-
, I probably should add just code
as an alias.
Java
I wasn’t sure whether to go with installer or brew for this one. I knew I’ll have to fiddle with paths anyway, so, I thought I’d use Brew so it’ll be more transparent to me. I used brew cask
to install both java
(latest) and java8
.
In my user ~/.bashrc
file I added:
1 2 3 |
export JAVA_HOME=$(/usr/libexec/java_home) export PATH=$JAVA_HOME/bin:$JAVA_HOME/include:$JAVA_HOME:$PATH |
Java 8 for Android
In my project specific .bashrc
, I added:
1 2 3 4 5 |
JDK_ALL=/Library/Java/JavaVirtualMachines JDK_8=$(ls $JDK_ALL | grep jdk1.8 | sort | tail -1) export JAVA_HOME=$JDK_ALL/$JDK_8/Contents/Home export PATH=$JAVA_HOME/bin:$JAVA_HOME/include:$JAVA_HOME:$PATH |
That’s a lie. I only did when I was writing this post. I made up the first two lines. My real old code looked like:
1 2 3 |
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk//Contents/Home export PATH=$JAVA_HOME/bin:$JAVA_HOME/include:$JAVA_HOME:$PATH |
Note the jdk1.8.0_181
bit, which is whatever exact Java 8 version I got from brew cask
. I replaced it in the first snippet to be dynamic, and it might be worth mentioning that I made up the JDK_ALL
and JDK_8
variables.
To test that it still works, I opened my project in VS Code, and in the terminal I typed java -version
, must be a single -
BTW!
Installing Android Itself
After struggling a lot with Android and Homebrew on my new machine, knowing that I got it to work somehow before on my old MacBook (which was out for repair at the time of new machine setup), or did I? I decided to go with the official installer for it.
I googled for Android, downloaded Android Studio from it, and then ran it to install the actual Android SDK the first time it opened.
I later discovered issues (maybe) specific to my active project, like Java version as I mentioned, but also found myself having to set a few extra global variables to ny home ~/.bashrc
file – besides project-specific Java 8 which I mentioned separately above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
export ANT_HOME=/usr/local/opt/ant/libexec export MAVEN_HOME=/usr/local/opt/maven export GRADLE_HOME=/usr/local/opt/gradle export ANDROID_HOME=~/Library/Android/sdk export ANDROID_SDK_HOME=$ANDROID_HOME export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle export PATH=$ANT_HOME/bin:$PATH export PATH=$MAVEN_HOME/bin:$PATH export PATH=$GRADLE_HOME/bin:$PATH export PATH=$ANDROID_HOME/tools/bin:$PATH export PATH=$ANDROID_HOME/platform-tools:$PATH export PATH=$ANDROID_NDK_HOME:$PATH export PATH=$ANDROID_HOME/build-tools/$(ls $ANDROID_HOME/build-tools | sort | tail -1):$PATH |
All the usr/local
stuff is probably from Homebrew, could have been replaced with $(brew prefix)
, and all ~/Library/Android/sdk
is from official install.
Maybe next time I get a clean environment I’ll do it differently. I don’t know!
.NET Core
I used the official installer for .NET Core runtime and SDK as it allowed me to easily install multiple versions of the SDK, which Brew didn’t. I did have a project pinned to a specific SDK version via global.json
in its repository.
I went to dot.net, clicked on Download, looked at the end of the page for download archives
, checked for latest current
version for my machine, and for 2.0 for my project, then found the exact version fo the SDK in the 2.0 page.
Note that SDK 2.1.2
is for .NET Core is NOT the same as SDK 2.1.200
, and that both are for .NET Core 2.0
not 2.1
!
Node
I installed both Node and nvm (Node Version Manager) via Homebrew.
I don’t relying on NVM too much. Things can get funny with global NPM dependencies when you switch Node versions using it, so, I keep that to a minimum. Here’s the idea:
In my home ~/.bashrc
, I added the following lines:
1 2 3 4 5 6 |
# The default commands recommended by brew post-install caveats export NVM_DIR="$HOME/.nvm" . "/usr/local/opt/nvm/nvm.sh" > /dev/null 2>&1 # `system` means the version of Node I already have installed without nvm nvm use system --delete-prefix --silent > /dev/null 2>&1 |
The > /dev/null 2>&1
bit is to hide a false negative error from the console, that I get about NPM prefix. You see I try really hard to hide that error in other ways in vain!
Then, in the folder right above my project get repository, where I had another relative .bashrc
file as I mentioned above, I added:
1 2 |
nvm use "8.11.3" --delete-prefix v8.11.3 --silent |
I can confirm that I have a different version of Node by opening my project VS Code workspace, and typing node -v
in the terminal.
Upgrading NPM
With the above setup, it’s worth noting that I get 2 NPM installs, one that’s the global / default, and one that’s specific to my project.
Node 8 comes with a slightly outdated version of NPM, so, to update that, I opened the VS Code terminal inside my project workspace, ran node -v
to confirm it’s picking it correctly, then I upgraded NPM by running npm install -g npm
, and after it finished, I ran npm -v
to confirm it’s picking the new version.
I did the same thing with my global Node install BTW. I told you I love to use latest versions of my stuff.
Golang (Go)
This was another silly one. I remember struggling with code paths on my old machine after installing via Homebrew, so, like Android, I just went to the official website and installed it via the official installer.
After that, I learned how that if your Go code doesn’t live under ~/go/src/project-name
, you can get a lot of problems because of GOPATH
environment viable. I really wanted to fix this!
That annoying problem is what prompted me to finally blog about relative .bashrc
files, after posponing that for a very long time.
The following snippet, which I’m experiencing with in a project-specific .bashrc
file, but can also be in the home ~/.bashrc
file, fixes this problem:
1 2 3 4 5 6 7 8 |
# If there's an existing GOPATH, prepend it # If there isn't, prepend default location # Then add current directory as well export GOPATH=${GOPATH:-$HOME/go/}:$(pwd)/ # Do the same idea with GOBIN # This was required for `go get` commands to work export GOBIN=${GOBIN:-$HOME/go/bin/}:$(pwd)/bin/ |
Turns out you can set multiple GOPATH
folders, separating them by :
(or ;
in Windows). I also had to set GOBIN
as you see in the script comment.
RVM (Ruby Version Manager)
I don’t do Ruby really, but my current project had a script that ues it. There’s no homebrew option for RVM, and following the official instructions -which you get by googling RVM
– failed on my Mac.
The error is around a pre-install command you need to run, which uses some signing / signature verification tool called gpg
, which is not available on Mac.
I found this blog post which suggested another command to run before the installation:
1 2 |
brew install gnupg |
Now I was able to follow up with the official install.
I also made sure I have the following in my home dir ~/.bashrc
of course, this code is a copy-paste from instructions:
1 2 3 |
export PATH="$PATH:$HOME/.rvm/bin" [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function* |
Performance And Battery
Away from terminal really, more into the menu bar, but still especially useful for me as a developer and pro user, I have a few apps to ensure I ge the best performance and battery life I can from my Mac
Macs Fan Control
MacsFanControl is an almost-necessary free app to prevent my i9 MacBook 2018 pro from throttling while I’m doing heavy work.
Turbo Boost Switcher Pro
I bought the paid (Pro) version of Turbo Boost Switcher so that I can turn off Intel Turbo Boost automically when I’m running on batteries.
It’s a of the opposite of the previous app I know, but quite often when I’m running on battery I need it to stay alive longer. Or when I don’t, I just turn off the auto mode and explicitly turn turbo boost on, and continue working.
Battery Health
Battery Health is another paid app for the battery, but this time just for monitoring.
I like to know how my overall battery life going over the year, and this is the app for that. But I also LOVE to see an estimate time of how long it’s until I run out of battery, a feature that used to be built into macOS itself then got removed.
Touchbar Tweak
If you are like my, you probably hate a lot of things about the MacBook 2016+ keyboards, especially the touchbar, especially how the Esc key has no feedback. Yuck!
A few people thought that the best tweak for the touch bar is by giving it Haptic feedback (a vibration feel on touch). Until Apple implements that, we can use the free HapticKey app, which vibrates the touchpad when you tap the touchbar.
It’s not the real thing, but it’s close!
P.S.
Apple, if you ever see this, please in addition to haptic feedback on the entire touchbar, please also change the Esc key to the same kind of physical key you use for the power button, PLEASE!
Other Tools
I defintely missed a few more here. There are specific extensions and settings in VS Code, fixes for common Node package install issues, and more, but each feel like they should be their own blog post.
For now, I’ll mention that I also have these in my home .bashrc
folder, and I don’t remember now what problem they were fixing – I should have added comments, I know!
1 2 3 4 |
export PATH="/usr/local/opt/gettext/bin:$PATH" export LDFLAGS="-L/usr/local/opt/gettext/lib" export CPPFLAGS="-I/usr/local/opt/gettext/include" |
There’s also another tool I have called bash-it. It supposedly adds some nice stuff to Bash that makes it closer to Zsh, but I haven’d noticed any specific benefit yet. They also have customization bash prompt themes, but I use bash-git-prompt as I mentioned.
And I use SnagIt for taking screenshots, and Audacity for fine-tuning audio of my YouTube videos, which I haven’t posted in a while, so, that doesn’t matter.
I also use this old tool on Github called RDM when I want to experiment with non HiDPI resolutions (which seem to increase the battery life slightly, unless I’m imagining), but the effect is not confirmed, so who case.
And if it counts, I installed Docker using the official installer, and using Docker for Mac official support for Kubernetes to install that.
Conclusion & Updates
When I try to modularize my ~/.bashrc
file, and check my old Mac for anything else I had in the past that’s still useful, I might put it all on github and add it to the post. Make sure you follow me on Twitter for updates.
Cheers,
How did I learn that?
As a bonus for coming here, I'm giving away a free newsletter for web developers that you can sign up for from here.
It's not an anything-and-everything link list. It's thoughtfully collected picks of articles and tools, that focus on Angular 2+, ASP.NET (4.x/MVC5 and Core), and other fullstack developer goodies.
Take it for a test ride, and you may unsubscribe any time.
You might also want to support me by checking these out [Thanks]: