Guard Rails for CLI Autopilot

The Linux command prompt is faster and more powerful than a GUI will ever be for moving files around and navigating a file system. Once you've spent enough time with cd, ls, mv, cp, and rm, you develop muscle memory that lets you make things happen to your files without even thinking about it, and there's where the danger lies. It's too easy to get caught up in a flow and not realize you just deleted a whole directory of important files until it's too late.

After nuking files I actually needed one too many times during mindless terminal sessions, I decided to put some guardrails in place that either force me to think before doing anything irrevocable, or give me a chance to fix my mistake after I've done something stupid.

Moving and Copying Files Without Overwriting

So here I am organizing some photos from a memory card I've just downloaded onto my computer. I have the JPEG files in an ./inbox/ directory. I'm moving the keepers into a date-labeled directory at the same level as ./inbox/, and I'm moving the ones I'm pretty sure I will trash but want to look at again with fresh eyes before I do that into another directory also on the same level. I've been doing this for about 10 minutes up to this point, so everything is automatic by now and I'm banging out commands without thinking about them. I've gone through all but two of the photos. They're both keepers. I issue the following command to move them both to the "keepers" directory:

% mv inbox/*.jpg

I immediately realized what I had done once I hit "enter". I forgot to supply the destination directory, but since I did supply a glob that expanded to two different files, so I ended up overwriting the second file with the first.

This was the last straw that made me finally start looking into idiot-proofing my shell. It turns out mv has all you need to prevent situations like this, but it isn't the default behavior. The -i or --interactive option will require you to confirm that you want to overwrite a file if the command you issued would do so. If the command wouldn't overwrite anything, then it requires no confirmation, and works like normal mv. cp uses the same option to enable this behavior too.

After adding the next few lines to my ~/.zshrc, I'll get a prompt that will (hopefully) make me snap out of it when I try to do something stupid with mv or cp on autopilot again:

## confirm before trying to overwrite a file with mv or cp
alias mv='mv --interactive'
alias cp='cp --interactive'

Reversible Deletes

rm is the most dangerous command and requires the most care. Once you hit "enter", the file is gone and you can't get it back without resorting to unreliable and cumbersome file system tricks.

I want to be able to easily undelete a file after I realize I've done something stupid. The easiest way to do this would be to move files to a purgatory folder when I delete them so that I can clear them later when I'm sure I want them gone, or restore them if I've made a mistake. Luckily, the XDG trash spec defines a protocol with exactly this behavior that plenty of applications and desktop environments implement.

I want something that I can alias to rm and behaves the same way I'm used to. I know a lot of people advise against using an alias for rm like this because I might be on a different machine without the alias set, but I rarely use different machines, and I still want to use the same workflow as before, but with a just-in-case back-up plan if I need it.

trash-d is a drop-in replacement for rm that implements the XDG trash spec written in D. It uses the same options as rm along with a few extra ones specifically related to managing the trash can. After installing the trash-d package from the AUR I get the trash command. Adding the following lines to ~/.zshrc, then gives me reversible deletes:

## use trash-d instead of rm to allow for reversible deletes
alias rm='trash'

Now rm --list shows me what's in the trash can, rm --restore <file> let's me restore a file I've deleted by mistake, and rm --empty clears the trash can.

Automatically Clear Trash

I can empty the trash can manually with the options provided by trash-d, but I don't want to have to think about it if nothing has gone wrong. Most of the times I delete files I actually want them deleted, so I would hate to always have to do it twice in the normal case.

I could set up a cron job to automatically clear the trash every week or so, but I could delete an important file just before the job runs and not have time to realize I made a mistake before the file goes away permanently.

Instead of automatically clearing the whole trash can periodically, I need something that automatically removes individual files from the trash can, but only if those files have been in the trash longer than a set time period. That's where autotrash comes in. It's a python script that removes files from the trash can older than a configurable age, and it runs automatically on a per-user basis with either a system-d unit or cron job.

After installing it with the AUR package, there's an easy setup command to install a system-d user unit that runs daily and automatically removes files that have been in the trash longer than a set number of days. I have mine set up to nuke all the files older than 2 weeks:

% autotrash -d 14 --install

Don't Clobber Existing Files When Redirecting

Once in a while I need to save the output of some command into a file with

% chatty-command > saved_file

The few times I need to do this, I almost never check if the file already exists, and the output would happily clobber whatever was there before and I might not ever realize what happened. The last guardrail I put in place prevents me from doing this unless I'm sure I want to. ZSH has an option to configure this behavior called CLOBBER. Adding the following line to my \~/.zshrc causes the > shell redirection to fail if the destination file exists.

# don't overwrite existing files with  >. require >! instead.
unsetopt CLOBBER

If I'm sure I want to overwrite the file, I need to use >! instead. This is exactly what I want, as long as I don't develop the habit of using >! by default.

Be Careful

These changes are not a replacement for being careful! Just because you have guard rails on the side of a highway doesn't mean you drive around smashing into them like you're in a pinball machine. You hopefully never need to use them, but they're there for the rare case when something goes wrong.

The first line of defense against doing something stupid at the command prompt should still be paying attention to what you're doing. Sometimes muscle memory takes over though, so hopefully these config changes will get you out of a jam after that happens.

Date: 2023-10-01 Sun 00:00

Created: 2023-12-09 Sat 15:01