Way to keep track of successful commands on command line

command lineitermterminalzsh

Usually I just copy/paste successful actions I do on the command line into a text file. That's a bit of an error prone and cumbersome process and I sometimes forget to do it.

Do you know of a way to automatically log commands that return successfully (I'm thinking of things like cmake, make etc. but also basic commands like cd) to a text file in order to keep a record of how you managed to finally successfully set up your environment to compile that one stubborn piece of code? For instance…

I'm on a MacBook Pro, MacOS Catalina, using iTerm2 with zsh and oh-my-zsh extension, as well as powerlevel10k.

Best Answer

Do you know of a way to automatically log commands that return successfully ... in order to keep a record of how you managed to finally successfully set up your environment to compile that one stubborn piece of code?

You first need to answer the question What constitutes a successful command?

You're probably thinking "that's easy! One that works and exits with a 0 code!" To get a better perspective of that, you need to define what a failed command is. The broadest of definitions will be any command that has an exit code of not zero. Does that mean the command has failed?

No. It doesn't. Commands fail for any number of reasons and that doesn't mean what you typed was in error. Let's use a very simple example: ssh. If you connect to a remote server and exit cleanly (type exit when your done), you will get an exit status of zero (0). However, if you poweroff the machine, you will get an exit status of one (1) when the connections is broken.

Did your command fail?

Try it. Connect via SSH to any remote. Exit cleanly then issue the command echo $?. Do it again and then issue poweroff or kill your session on the remote and issue the echo command again.

I also use Zsh and I created a custom prompt that prefaces my prompt with a green check green check or a "red X" red x depending on whether the last command failed or not.

Here's a sample with the SSH example used above.

Green Check Prompt

Red X Prompt

Did the command actually fail? No, it didn't but the result generated an error code because something in the process failed. This holds true for the commands you issue compiling software as well. Your command can be syntactically perfect, but the compile will still fail because you have an error in your code. Did you want that command ignored?

BASH_COMMAND Environment Variable

Out of curiosity, I investigated this and it’s not a trivial thing to do. You cannot use history because it’s designed to be interactive and not scripted.

In Bash, there is an environment varialbe called $BASH_COMMAND. It’s the command that is about to be or is being executed unless the command was called from a Trap, then its the command executing at the time of the trap.

In order to make use of this, you would have to execute all of your commands within a DEBUG trap:

trap ‘prev_command=$thiscmd; thiscmd=$BASH_CMD’ DEBUG
mycommand
if $? -eq 0; echo $prev_command >> somefile.txt; else echo “Command failed”; fi

This would be the logic, but in all practicality, it doesn’t work because you have to execute your command within a trap.

I haven’t found an equivalent in Zsh (yet... but I doubt I’ll continue my research).

Zsh Addons

I'm on a MacBook Pro, MacOS Catalina, using iTerm2 with zsh and oh-my-zsh extension, as well as powerlevel10k.

I'm not a fan of these things especially for the novice user. You're bringing in lots of extra complexity and add-ons to prettify your command prompt and shell environment. I encourage folks actually learn how to do this manually so you understand the underpinnings of your system.

The code for my above example is actually very simple:

PROMPT="%(?.%F{green}✓.%F{red}?)%fallan@%m %F{blue}%1~ %f%# "

In fact, I create a simple function (in ~/.zshrc or `~/.bash_profile) for when I have to cut/paste terminal output to public forums like this:

# (Re)sets the prompt on the fly
setprompt() {

  case "$1" in
    se) # Obfuscates username to "allan" for StackExchange posts
        PROMPT="%(?.%F{green}✓.%F{red}?)%fallan@%m %F{blue}%1~ %f%# "
        ;;
    *)  # Default is to set the prompt back to the original
        PROMPT=' %(?.%F{green}✓.%F{red}?)%f%n@%m %F{blue}%1~ %f%# '
    ;;
  esac
}  

Bottom line, this really isn’t feasible.

Avoid all those add-ons. It's just wrapping. Learn to use the core shell first, then add things as you go. This will help you in actually learning the system. You're looking for shortcuts to things and this will actually hinder your effectiveness.

TL;DR

Don't do this. A command that failed isn't necessarily a bad command. You can very easily "ignore" a perfectly good command because it failed due to an issue not related to the command itself.

Usually I just copy/paste successful actions I do on the command line into a text file. That's a bit of an error prone and cumbersome process and I sometimes forget to do it.

If you're finding copy/pasting to be error prone, you're doing it wrong. If it's cumbersome and you forget, then rethink your workflow. I have OneNote open on my second monitor at all times so I can refer to and take notes as soon as I need to. Find a flow that works for you. The built in shell history isn't a substitute for good note taking.

Not only is this not a good practice overall, it’s not feasible at due to how you would go about capturing the last successful command.