Beyond Aliases — define your development workflow with custom bash scripts

Being a Linux user for just over 10 years now, I can’t imagine my life with my aliases.

Aliases help with removing the repetition of commonly-used commands on a system.

For example, here’s some of my own that I use with the Laravel framework:

Bash
alias a="php artisan"
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
alias stan="./vendor/bin/phpstan analyse"

You can set these in your ~/.bashrc file. See mine in my dotfiles as a fuller example.

However, I recently came to want greater control over my development workflow. And so, with the help of videos by rwxrob, I came to embrace the idea of learning bash, and writing my own little scripts to help in various places in my workflow.

A custom bash script

For the example here, I’ll use the action of wanting to “exec” on to a local docker container.

Sometimes you’ll want to get into a shell within a local docker container to test / debug things.

I found I was repeating the same steps to do this and so I made a little script.

Here is the script in full:

Bash
#!/bin/bash

docker container ls | fzf | awk '{print $1}' | \
xargs -o -I % docker exec -it % bash

Breaking it down

In order to better understand this script I’ll assume no prior knowledge and explain some bash concepts along the way.

Sh-bang line.

the first line is the “sh-bang”. It basically tells your shell which binary should execute this script when ran.

For example you could write a valid php script and add #!/usr/bin/php at the top, which would tell the shell to use your php binary to interpret the script.

So #!/usr/bash means we are writing a bash script.

Pipes

The pipe symbol: |.

In brief, a “pipe” in bash is a way to pass the output of the left hand command to the input of the right hand command.

So the order of the commands to be ran in the script is in this order:

  1. docker container ls
  2. fzf
  3. awk ‘{print $1}’
  4. xargs -o -I % docker exec -it % bash

docker container ls

This gives us the list of currently-running containers on our system. The output is the list like so (I’ve used an image as the formatting gets messed up when pasting into a post as text) :

fzf

So the output of the docker container ls command above is the table in the image above, which is several rows of text.

fzf is a “fuzzy finder” tool, which can be passed a list of pretty much anything, which can then be searched over by “fuzzy searching” the list.

In this case the list is each row of that output (header row included)

When you select (press enter) on your chosen row, that row of text is returned as the output of the command.

In this image example you can see I’ve typed in “app” to search for, and it has highlighted the closest matching row.

awk ‘{print $1}’

awk is an extremely powerful tool, built into linux distributions, that allows you to parse structured text and return specific parts of that text.

'{print $1}' is saying “take whatever input I’m given, split it up based on a delimeter, and return the item that is 1st ($1).

The default delimeter is a space. So looking at that previous image example, the first piece of text in the docker image rows is the image ID: `”df96280be3ad” in the app image chosen just above.

So pressing enter for that row from fzf, wil pass it to awk, which will then split that row up by spaces and return you the first element from that internal array of text items.

xargs -o -I % docker exec -it % bash

xargs is another powerful tool, which enables you to pass what ever is given as input, into another command. I’ll break it down further to explain the flow:

The beginning of the xargs command is as so:

Bash
xargs -o -I %

-o is needed when running an “interactive application”. Since our goal is to “exec” on to the docker container we choose, interactive is what we need. -o means to “open stdin (standard in) as /dev/tty in the child process before executing the command we specify.

Next, -I % is us telling xargs, “when you next see the ‘%’ character, replace it with what we give you as input. Which in this case will be that docker container ID returned from the awk command previously.

So when you replace the % character in the command that we are giving xargs, it will read as such:

Bash
docker exec -it df96280be3ad bash

This is will “exec” on to that docker container and immediately run “bash” in that container.

Goal complete.

Put it in a script file

So all that’s needed now, is to have that full set of piped commands in an executable script:

Bash
#!/bin/bash

docker container ls | fzf | awk '{print $1}' | xargs -o -I % docker exec -it % bash

My own version of this script is in a file called d8exec, which after saving it I ran:

Bash
chmod +x ./d8exec

Call the script

In order to be able to call your script from anywhere in your terminal, you just need to add the script to a directory that is in your $PATH. I keep mine at ~/.local/bin/, which is pretty standard for a user’s own scripts in Linux.

You can see how I set my own in my .bashrc file here. The section that reads $HOME/.local/bin is the relevant piece. Each folder that is added to the $PATH is separated by the : character.

Feel free to explore further

You can look over all of my own little scripts in my bin folder for more inspiration for your own bash adventures.

Have fun. And don’t put anything into your scripts that you wouldn’t want others seeing (api keys / secrets etc)

Setting up a GPG Key with git to sign your commits

Signing your git commits with GPG is really easy to set up and I’m always surprised by how many developers I meet that don’t do this.

Of course it’s not required to push commits and has no baring on quality of code. But that green verified message next to your commits does feel good.

Essentially there are three parts to this:

  1. Create your GPG key
  2. Tell git to use your GPG key to sign your commits
  3. Upload the public part of your GPG key to Gitlab / Github / etc

Creating the GPG key if needed

gpg --full-generate-key

In the interactive guide, I choose:

  1. (1) RSA and RSA (default)
  2. 4096 bits long
  3. Does not expire
  4. Fill in Name, Email, Comment and Confirm.
  5. Enter passphrase when prompted.

Getting the Key ID

This will list all of your keys:

gpg --list-secret-keys --keyid-format=long

Example of the output:

sec   rsa4096/THIS0IS0YOUR0KEY0ID 2020-12-25 [SC]
      KGHJ64GHG6HJGH5J4G6H5465HJGHJGHJG56HJ5GY
uid                 [ultimate] Bob GPG Key<mail@your-domain.co.uk>

In that example, the key id that you would need next is “THIS0IS0YOUR0KEY0ID” from the first line, after the forward slash.

Tell your local git about the signing key

To set the gpg key as the signing key for all of your git projects, run the following global git command:

git config --global user.signingkey THIS0IS0YOUR0KEY0ID

If you want to do it on a repository by repository basis, you can run it from within each project, and omit the --global flag:

git config user.signingkey THIS0IS0YOUR0KEY0ID

Signing your commits

You can either set commit signing to true for all projects as the default, or by a repo by repo basis.

# global
git config --global commit.gpgsign true

# local
git config commit.gpgsign true

If you wanted to, you could even decide to sign commits per each commit, by not setting it as a config setting, but passing a flag on every commit:

git commit -S -m "My signed commit message"

Adding your public key to gitlab / github / wherever

Firstly export the public part of your key using your key id. Again, using the example key id from above:

# Show your public key in terminal
gpg --armor --export THIS0IS0YOUR0KEY0ID

# Copy straight to your system clipboard using "xclip"
gpg --armor --export THIS0IS0YOUR0KEY0ID | xclip -sel clipboard

This will spit out a large key text block begining and ending with comments. Copy all of the text that it gives you and paste it into the gpg textbox in your git forge of choice – gitlab / github / gitea / etc.

How I use vimwiki in neovim

This post is currently in-progress, and is more of a brain-dump right now. But I like to share as often as I can otherwise I’d never share anything 🙂

Please view the official Vimwiki Github repository for up-to-date details of Vimwiki usage and installation. This page just documents my own processes at the time.

Installation

Add the following to plugins.lua

use "vimwiki/vimwiki"

Run the following two commands separately in the neovim command line:

:PackerSync
:PackerInstall

Close and re-open Neovim.

How I configure Vimwiki

I have 2 separate wikis set up in my Neovim.

One for my personal homepage and one for my commonplace site.

I set these up by adding the following in my dotfiles, at the following position: $NEOVIM_CONFIG_ROOT/after/plugin/vimwiki.lua. So for me that would be ~/.config/nvim/after/plugin/vimwiki.lua.

You could also put this command inside the config function in your plugins.lua file, where you require the vimwiki plugin. I just tend to put all my plugin-specific settings in their own “after/plugin” files for organisation.

vim.cmd([[
  let wiki_1 = {}
  let wiki_1.path = '~/vimwiki/website/'
  let wiki_1.html_template = '~/vimwiki/website_html/'
  let wiki_2 = {}
  let wiki_2.path = '~/vimwiki/commonplace/'
  let wiki_2.html_template = '~/vimwiki/commonplace_html/'
  let g:vimwiki_list = [wiki_1, wiki_2]
  call vimwiki#vars#init()
]])

The path keys tell vimwiki where to plave the root index.wiki file for each wiki you configure.

The html_template keys tell vimwiki where to place the compiled html files (when running the :VimwikiAll2HTML command).

I keep them separate as I am deploying them to separate domains on my server.

When I want to open and edit my website wiki, I enter 1<leader>ww.

When I want to open and edit my commonplace wiki, I enter 2<leader>ww.

Pressing those key bindings for the first time will ask you if you want the directories creating.

How I use vimwiki

At the moment, my usage is standard to what is described in the Github repository linked at the top of this page.

When I develop any custom workflows I’ll add them here.

Deployment

Setting up a server to deploy to is outside the scope of this post, but hope to write up a quick guide soon.

I run the following command from within vim on one of my wiki index pages, to export that entire wiki to html files:

:VimwikiAll2HTML

I then SCP the compiled HTML files to my server. Here is an example scp command that you can modify with your own paths:

scp -r ~/vimwiki/website_html/* your_user@your-domain.test:/var/www/website/public_html

For the best deployment experience, I recommend setting up ssh key authentication to your server.

For bonus points I also add a bash / zsh alias to wrap that scp command.

General plugins I use in Neovim

I define a “general plugin” as a plugin that I use regardless of the filetype I’m editing.

These will add extra functionality for enhancing my Neovim experience.


I use Which-key for displaying keybindings as I type them. For example if I press my <leader> key and wait a few milliseconds, it will display all keybindings I have set that begin with my <leader> key.

It will also display any marks and registers I have set, when only pressing ' or @ respectively.

use "folke/which-key.nvim"


Vim-commentary makes it super easy to comment out lines in files using vim motions. So in normal mode you can enter gcc to comment out the current line; or 5gcc to comment out the next 5 lines.

You can also make a visual selection and enter gc to comment out that selected block.

use "tpope/vim-commentary"


Vim-surround provides me with an extra set of abilities on text objects. It lets me add, remove and change surrounding elements.

For example I can place my cursor over a word and enter ysiw" to surround that word with double quotes.

Or I can make a visual selection and press S" to surround that selection with double quotes.

use "tpope/vim-surround"


Vim-unimpaired adds a bunch of extra mappings that tpope had in his own vimrc, which he extracted to a plugin.

They include mappings for the [ and ] keys for previous and next items. For example using [b and ]b moves backwards and forwards through your open buffers. Whilst [q and ]q will move you backwards and forwards respectively through your quickfist list items.

use "tpope/vim-unimpaired"

Passive plugins I use in Neovim

These plugins I use in Neovim are ones I consider “passive”. That is, they just sit there doing their thing in the background to enhance my development experience.

Generally they wont offer extra keybindings or commands I will use day to day.

You can view all the plugins I use in my plugins.lua file in my dotfiles.


Vim-lastplace will remember the last edit position of each file you’re working with and place your cursor there when re-entering.

use "farmergreg/vim-lastplace"


Nvim-autopairs will automatically add closing characters when opening a “pair”, such as {, [ and (. It will then place your cursor between the two.

use "windwp/nvim-autopairs"


Neoscroll makes scrolling smooth in neovim.

use "karb94/neoscroll.nvim"


Vim-pasta will super-charge your pasting in neovim to preserve indents when pasting contents in with “p” and “P“.

use({
  "sickill/vim-pasta",
  config = function()
    vim.g.pasta_disabled_filetypes = { 'fugitive' }
  end,
})

Here I am passing a config function to disable vim-pasta for “fugitive” filetypes. “Fugitive” is in reference to the vim-fugitive plugin that I will explain in another post.


Nvim-colorizer will highlight any colour codes your write out.

use "norcalli/nvim-colorizer.lua"

How I use Neovim

I try to use Neovim for as much development-related work as possible.

This page serves as a point of reference for me, and other people interested, for what I use and how I use it.

Feedback is welcome and would love to know how you use Neovim too!

My complete Neovim configuration files can be found on Github.

  1. How I organise my Neovim configuration
  2. Passive plugins I use in Neovim
  3. General plugins I use in Neovim
  4. Development plugins I use in Neovim – coming soon
  5. Database client in Neovim (vim-dadbod and vim-dadbod-ui) – coming soon
  6. REST client in Neovim (vim-rest-client) – coming soon
  7. Personal Wiki in Neovim (vim-wiki) – coming soon