Anthology of Effective Techniques in the Employment of Computing Machines
Here are some computer things that I’ve found handy, in case they’re helpful to you, or future-me:
Don’t forget pipes
At work, I often need to deal with rather verbose logs that originate on developement hardware and get to my computer via serial ports. Lately, I use tio
to receive the logs initially, then I either scan through the logs by eye or move them in to a text editor.
Sometimes though, I’m only looking for a particular type of message. For instance, a device I’m working on today periodically outputs the state of a state machine that I’m interested in, via a line that starts with “STATE”, but that is mixed in with tons of distracting irrelevant information. In this situation, it can be handy to pipe the output from the device through grep
(or, more recently, ripgrep), so only lines starting with STATE are displayed. Something like this on one terminal: $ cat /dev/ttyUSB3 | tee full_log.txt | grep '^STATE'
, then optionally in a different terminal: $ tail -f full_log.txt
. This captures the entire log to a file, and gives real-ish time output of both the complete log, and the lines that start with STATE.
Infinite command line history
I’ve found it handy to configure bash’s command history to be infinitely long; in conjunction with the history search offered by mapping fzf
to Ctrl+r
, this helps compensate for my lousy memory. Unfortunately, it’s not quite as straightforward as one might hope; see the answer by fotinakis for a more thorough explanation.
Basically, you want HISTSIZE=
and HISTFILESIZE=
in your .bashrc
(any other setting of those to be removed, including in the same file), and set your HISTFILE=~/.bash_eternal_history
or similar non-default name. Otherwise, some scenarios can result in your long history being unintentionally nuked.
Handy, and perhaps not obvious, command line tools
Commonly bundled with *NIX-style operating systems
watch
runs the specified command over and over again, updating the output on the screen as it does.
Might need to be installed separately
fzf
Fuzzy search for your shell; makes the command line history search fromCtrl+r
particularly nice.rg
Ripgrep is a great upgrade from grep, not least because it’s much faster when searching over lots of files.- tio is the tool to use for working with serial consoles/logging/etc - has completely replaced
screen
,minicom
, etc in my regular usage tmux
More below- bottom A cool update of the classic
top
Shell variables and aliases
In my home directory, I have a directory for work stuff. Often, I want to just cd in to a subdirectory of that - for instance in to the one that most of my git repos live under. CDPATH
lets me just type cd git/project
and get ~/Work/git/project
. The first entry in CDPATH is .
to avoid aliasing problems, like if your current directory is someproject
and both it and ~/Work
contain a temp
directory, you want cd temp
to go in to someproject/temp
not ~/Work/temp/
:
export CDPATH=.:/home/irees/Work:$CDPATH
Here’s one of my favourites aliases - a simple function that makes a directory then changes in to it. The ./
is subtle belt-and-braces in addition to the above note - it’s to cover the case when there is a directory in the CDPATH
environment that has the same name as the directory that mkcd
is making:
function mkcd {
mkdir "$1" && cd ./"$1"
}
git stuff
Aliases
If you frequently use git from the command line, git’s alias feature may be helpful. For instance, in my ~/.gitconfig, I have:
[alias]
cof = !git for-each-ref --format='%(refname:short)' refs/heads | fzf | xargs git checkout
newbranch = !git checkout -b $(date +"%Y%m%d")-$1 && :
git cof
launches a nice fzf-powered branch picker, then checks out the selected branch (from a HN comment).
git newbranch my-branch
makes a new branch, prepended with today’s date in the form YYYYMMDD. The trailing && :
is the secret sauce here; without it the alias would expand to git checkout -b YYYYMMDD-my-branch my-branch
, which fails because my-branch doesn’t yet exist in the git repo.
Distinct configurations by repo path
I use git for both personal and work projects, but don’t want to mix my personal and work email addresses between those. The conditional includes feature in git configuration allows for overriding the email setting for any repos stored under ~/Company, leaving the personal address for the rest.
In ~/.gitconfig:
[user]
name = My Name
email = my.name@personal.com
[includeIf "gitdir:~/Company/"]
path = .gitconfig-work
And a 2-line ~/.gitconfig-work:
[user]
email = my.name@company.com
tmux stuff
I started using tmux as an upgraded version of screen
, for running commands that I want to put in the background (my work VPN client) and for SSH sessions that should persist even if the connection fails, but have gradually used this more and more to replace having multiple terminal windows in my GUI. At this stage, the fact that tmux makes sessions persistent is just an occasionally-helpful side effect in most cases, it’s really more of a window manager for the stuff I do in a terminal. My normal arrangement has two terminals - one for each monitor - and have tmuxp configurations to set up a number of windows in each of them, for stuff like chat (weechat), system status (bottom), separate windows for the work projects I’ve got on the go, etc. Depending on what I’m working on, I’ll also have a terminal opened up in my text editor (codium) with a tmux session running in it.
Rename session: Ctrl+b + $ Session picker: Ctrl+b + s
Safer shell command substitution
A quick one I figured out for use with ttynamed: if you’re in the habit of using the classic $ some_command `other_command`
, or the safer $ some_command "$(other_command)"
, there could be problems if other_command
fails without writing anything to stdout, causing some_command
to run without an argument. To prevent some_command
from running when other_command
fails, use a construct like:
$ temp=$(other_command) && some_command $temp
Other resources
The Lost Art of Structure Packing is a very nicely done page on C structures.