Switching Shells

I’m not sure how I made it this far into my career without knowing I could quickly switch between fish and bash and zsh and any other shell I have on my system by simply typing the name of the shell I want to use.

Since I switched from zsh to fish around a year and a half ago, I’ve kept the stock macOS Terminal app (I normally use iTerm) set up to use zsh just in case I wanted to test something in a POSIX-compatible shell. All this time I’ve thought I’d have to do the following if I wanted to switch from fish to zsh:

  1. Type chsh -s (which zsh)
  2. Enter my account password
  3. Open a new terminal tab


Just type zsh. Done.

A Modest Proposal on Naming Files

Trying to decide if you should replace spaces in your files names with dashes or underscores? Choose dashes. Why? If you use dashes, when you are editing the file name you can jump from word to word by tapping the left or right arrow key while holding down Option. Said another way, macOS recognizes the dash as a word separator; not so with the underscore.

VS Code Quick Tip: Exit Preview Mode

If you’ve got VS Code’s enablePreview options turned on, and they are by default, you might like to know that you can just hit ⌘-S to take it out of “Preview Mode” to prevent it from being replaced by the next file you open. In my opinion this is better than the other methods to make a file stay open: double-clicking the file’s tab (eww, touching your mouse or trackpad) or making the file “dirty” by entering some text and immediately deleting it.

I’ve never used this “preview” feature in any editor. I’ve recently gotten sick of ending up with dozens of tabs open in a project so I’ve been giving it a shot. This little tip is making me actually enjoy the feature.

Speaking of preview modes in text editors, I’d kill for Sublime Text’s legit preview mode the would instantly show you the contents of the file you had selected in the Quick Open menu. So good!

Global npm Module Weirdness

I just installed node 8.9.1 with nodenv. Then I wanted to get my global packages installed to more-or-less match what I had with my previous version of node (v6.11.3).

Running npm list -g --depth=0 with node 6.11.3 active gives me:

❯ npm list -g --depth=0
├── eslint@4.7.1
├── eslint-config-airbnb-base@12.0.0
├── eslint-config-prettier@2.5.0
├── eslint-plugin-import@2.7.0
├── eslint-plugin-json@1.2.0
├── eslint-plugin-prettier@2.3.1
├── generator-code@1.1.20
├── gulp@3.9.1
├── npm@3.10.10
├── prettier@1.7.3
├── prettier-eslint@8.2.0
├── vsce@1.31.1
└── yo@2.0.0

So with node 8.9.1 active I ran npm i -g eslint gulp prettier prettier-eslint yo generator-code to start installing a bunch of — but not all of — the global packages that I wanted.

Now for the weird part: Running npm list -g --depth=0 to see the current state of the world gives me this:

❯ ng # <-- a handy alias for `npm list -g --depth=0`
├── generator-code@1.1.22
├── gulp@3.9.1
├── npm@5.5.1
├── prettier-eslint@8.2.2
└── yo@2.0.0

eslint isn’t listed as a global package! But I have it installed:

❯ which eslint; and eslint -v

I looked a little deeper and found eslint installed as a dependency of prettier-eslint (which, by the way, is not a module that I actually wanted to have installed globally):

❯ npm list -g --depth=1
├─┬ prettier-eslint@8.2.2
│ ├── ...
│ ├── eslint@4.11.0

And further, digging into where nodenv actually stores the globally installed modules, in this case ~/.nodenv/versions/8.9.1/lib/node_modules, I see that eslint is indeed there at the top level!


So the question is:

Why doesn’t npm list eslint as a top-level globally installed module when it seems as though it is one?

The follow up question is: Why does it bother me as much as it does that eslint doesn’t show up in that list of global modules even though everything works as though it is a top-level global module?

The Fix

How to fix this “problem”? One option is to just blow away all of the globally installed packages by running this a couple of times:

npm ls -gp --depth=0 | awk -F/ '/node_modules/ && !/\/npm$/ {print $NF}' | xargs npm -g rm

That’s extreme and is likely something that you wouldn’t want to do in most situations.

The other option is to remove the package that has eslint as a dependency, which is a package I didn’t want to have installed in the first place: npm -g rm prettier-eslint

Now the world is happy again:

❯ ng
├── eslint@4.11.0
├── generator-code@1.1.22
├── gulp@3.9.1
├── npm@5.5.1
├── prettier@1.8.2
└── yo@2.0.0

Final Note

At this point reinstalling prettier-eslint works and eslint still shows up as a global package.

BTT: Three Finger Swipes

This is a quickie. One of my most-used BetterTouchTool configurations is setting up three finger swipes. Three of my most used global shortcuts are:

  • Three finger swipe left — ⇧⌘[ (Show Previous Tab)
  • Three finger swipe right — ⇧⌘] (Show Next Tab)
  • Three finger swipe down — ⌘W (Close Current Tab (or Window))

Being able to use three fingers to swipe between an app’s tabs, and then quickly close a tab with a swipe down, feels like an entirely new way to casually do light work.

`git push` Without Complaints

On my work computer, I can make a new git branch and push it up to GitHub with a simple git push. I don’t need to manually set or create the upstream branch. Not so on my home computer. At least not when working on my dotfiles repo. When I try to push up a new branch, I get this error:

fatal: The current branch new-branch has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin new-branch

It didn’t take too long to figure out the problem. Somewhere along the line I’d renamed the default remote from origin to github. I saw this by looking at the repo’s .git/.gitconfig file, which had the following lines:

[remote "github"]
    url = git@github.com:erikphansen/bin.git
    fetch = +refs/heads/*:refs/remotes/github/*
[branch "master"]
    remote = github
    merge = refs/heads/master

I changed github to origin on the first and fifth lines, so it read:

[remote "origin"]
    url = git@github.com:erikphansen/bin.git
    fetch = +refs/heads/*:refs/remotes/github/*
[branch "master"]
    remote = origin
    merge = refs/heads/master

After that, no more complaints when trying to push up a branch that didn’t yet exist on GitHub.

I’m pretty sure that tomorrow’s post will be a follow up explaining how I could keep my remote branch named github and still allow for auto-creation of remote branches. Stay tuned for that excitement.

P.S. You might also need to check the value of git’s push.default setting with $ git config --global push.default. It it’s not current, then run git config --global push.default current. In my case, this was already set, but I don’t believe it’s like this out of the box.

Faster GitHub PRs, Take 2

I just updated the function I wrote about yesterday, making two big changes:

  • Vim is no longer opened just so that I can reuse the last commit message for the PR Message
  • The browser no longer opens the PR page; instead the URL for the PR is copied to the clipboard so that I can paste it into Slack.

The new usage is simply $ pr if I want to PR into develop or $ pr target-branch if I want to PR into target-branch.

Here is the new version, which can also be found on GitHub:

function pr -d 'Quickly make a PR of the current branch into `develop` or the branch of your choosing'
  # Reuse the last commit message as the PR message
  git log -1 --pretty=%B > msg.txt
  set targetbranch 'develop'
  if test -n "$argv"
    set targetbranch $argv[1]
  git push; and git pull-request -b $targetbranch -F msg.txt | pbcopy
  rm msg.txt
  echo 'You can find your PR at' (pbpaste)
  echo ' (That URL has been copied to your clipboard, as well)'

I’m outputting the last commit message to a temp file, msg.txt, because this seems like a good way to support multi-line messages. The message is then used with the pull-request -F flag. In my first attempt I stored the value of git log -1 --pretty=%B in a var to then pass to pull-request -m but that choked on commits that were more than a single line.

Faster GitHub PRs

NOTE: The day after writing this, I made it better

After some setup, I can create a GitHub PR with two commands in the terminal: Entering pr at the command line followed by :x in Vim. After that I’m staring at the webpage for the PR in my browser.

How? With a command line function that leverages Hub for GitHub.

Want to do the same?

First, install Hub for GitHub. With Homebrew it’s as simple as $ brew install hub. You’ll also want to follow the simple instructions to alias git to hub. In effect this just gives git some extra powers.

Now you can type $ git pull-request -b develop -o which will:

  1. Prompt you to enter a message for the PR in whatever editor you’ve told Git to use
  2. Open the webpage for the newly created PR in your browser

But who wants to type that much? The logical next step is to make a shell function so you can just type pr. In Fish, it looks like this:

function pr
  set targetbranch 'develop'
  if test -n "$argv"
    set targetbranch $argv[1]
  git push; and git pull-request -b $targetbranch -o

Now creating PRs into develop is as simple as $ pr and making PRs into my-big-feature can be done with $ pr my-big-feature. And with Fish’s autosuggestions you likely won’t even have to type more than a letter or two of your target branch’s name.


One way this could be better is if the URL for the PR page on GitHub could be copied to my clipboard so I could easily paste it into my team’s Slack channel. In fact, the only reason I want to open the PR page at all is so that I can grab its URL. It doesn’t seem like the pull-request command has a way to just give me the URL. Let me know if I missed that. But since this process ends with me looking at the page I want to share it just takes a quick ⌘L ⌘C to get the URL on my clipboard. Not too shabby.

Run ffmpeg On a Folder of Videos

The Problem:

I had roughly fifty video files across four different folders that I wanted to extract the audio from. You can use ffmpeg to pull the audio out of a single video file, but I didn’t want to type in a slightly different ffmpeg command fifty times.

The Solution:

Write a Node.js script that loops over all the files in a directory and runs the correct ffmpeg command to attempt to extract the audio from each file. And, of course, spend more time writing the script than it would have taken to just manually run the command dozens of times.

You can grab the script from GitHub. It was a fun learning experience. When you run the ffmpeg command, you need to make sure that the file extension for the audio file matches the format that’s actually in the movie file. This is kind of a drag. It would be nice if ffmpeg was smart enough to extract whatever audio was in the video, in whatever format it was in. It’s entirely possible it is that smart, I’m just not smart enough to know how to make it do what I want. At any rate, my script allows you to pass in an argument to set what format the audio is in.

Oh, all you care about is the ffmpeg command that’ll pull the existing audio out of a video file? No problem: ffmpeg -i input-video.avi -vn -acodec copy output-audio.mp3