When I was first starting out in software engineering, it felt like there was a never-ending barrage of tooling to learn. After more than a decade in CLI environments, I still find myself constantly learning new features and fun facts - but it's fairly rare that I learn something new that I end up using day-to-day.
I wanted to share some things I learned at relatively late stages in the game that ended up being significant productivity boosters for me - perhaps some of them are well-known, but in the spirit of this XCKD, I hope that someone reading this might pick up something new.
1. Use command line editing
Perhaps this type of experience is familiar to you:
Manually moving around a long CLI command is no fun - even with keyboard shortcuts. But editing the command in your editor of choice can be much more pleasant:
Command line editing is a powerful feature that allows you to pop into your $EDITOR
to craft your
command. It's made the CLI much less frustrating for me whenever I have to deal with long commands
that I need to edit.
2. tmux
scripting
If you've ever found yourself:
- Waiting for something to happen in your terminal before reacting
- Recreating similar sessions/windows/layouts (e.g. identical sessions for different servers)
- Transferring output between terminal panes
Then tmux
scripting is for you! tmux
scripting allows you to program your terminal - you can
programmatically read terminal output, send input into the terminal, or control your terminal's
layout and contents.
I once had to work with a physical device that had a cumbersome development flow - you had to
manually reset it over a serial connection, interrupt its autoboot at a precise point in its flow,
modify its configuration, remember to wait at least one second because of some hardware
idiosyncrasy, input a "boot" command, and then wait for the rest of the flow. This was impossible
to traditionally script, but tmux
scripting allowed me to automate this process and was a force
multiplier for this work.
To illustrate how powerful this can be - I've prepared a small program that outputs random numbers,
and you need to type them back into the program for the program to continue. The program is running
in the left pane, and the right pane is running a tmux
script that's capturing the output of the
left pane, parsing it for the current number, and then sending that number back to the left pane:
And here's another script that creates a session with a specific layout and then prepopulates some of the session's panes with commands:
3. Use fzf
liberally in custom scripts
fzf
is a "general-purpose command-line fuzzy finder" - but this dry description can really hide
away just how useful fzf
can be if you incorporate it in your custom scripts. fzf
is generally
known for its nice Ctrl+R and other integrations, but focusing on just its
integrations can miss the potential of using it in other workflows.
To illustrate, here are two custom scripts I wrote that I use in my day-to-day - the first lets me
interactively git checkout
branches:
And the second lets me spin up EC2 instances - when running the command I choose the instance type, region, and distribution:
In both cases, I can alternatively type out a full command (git checkout branch1
or
ec2 create -r ap-east-1 -i t3.micro -d ubuntu
) but in practice the interactivity is usually a
quicker and nicer experience. fzf
can be used to supercharge any script that involves selecting
something out of a list of options - whether predefined or dynamically populated.
4. Use /dev/stdin
as a replacement for heredocs
Heredocs are a fairly well-known powerful feature that allow you to pass multiple lines of input into a command:
But they have two drawbacks for me - first, I find the syntax a bit clunky and often trip up on it,
and more importantly, they are not supported in fish
which is my shell of choice.
/dev/stdin
can be used to accomplish the same functionality in a cross-shell way, and personally
since it makes use of well-known Linux primitives and not shell-specific functionality I find it
much more intuitive to use and remember than heredocs:
There are some drawbacks to this /dev/stdin
trick - for instance, it can't replace heredocs in
scripts, and it doesn't keep the contents in your shell history. But so long as you keep its
drawbacks in mind it can be a powerful tool.
5. Use SSH multiplexing
SSH multiplexing lets you seamlessly use the same SSH connection for multiple sessions. Enabling it is as easy as placing these lines:
Host *
ControlPath /tmp/ssh-%r@%h:%p
ControlMaster auto
ControlPersist 10m
in your ~/.ssh/config
. If your workflow involves frequently SSHing into remote machines, this is
a lifechanger.
Bonus Tips
Here are some other tips that I think deserve an honorable shoutout:
- Use
git checkout -
for checking out the previous active branch (similar tocd -
). - Use either
$_
or!$
in bash to get the last argument of the last command. - Similarly, in both fish and bash you can type in Alt+. to directly paste in the last argument of the last command.
- Prepend a space before a command to exclude it from your history.
- Use
pushd
andpopd
to keep state of your directory traversal. If you're in fish you can just directly usenextd
andprevd
instead. - Use locate for indexing and instantaneously searching your files.
- When you do something like
sudo command > file
, the command runs as privileged but the redirection doesn't, so iffile
is a protected file you won't be able to write to it - usecommand | sudo tee file
as a workaround. - Similar to heredocs are herestrings -
<<<
- which can be used to pass a single string as input, e.g.cat - <<< "Hello, world!"
- You can edit remote files directly by writing
vim scp://<user>@<host>/<absolute-path>
. If you need to specify a private key then create a configuration for the host in your~/.ssh/config
and use that. - You can pipe the output of commands into
vim -
(similar to the/dev/stdin
tip above, but neater) for powerful searchability/editability. In practice I almost never pipe intoless
, only intovim -
.