Way to keep track of successful commands on command line?

Solution 1:

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.

Solution 2:

As user3439894 stated in the comment already, there is a history file recording all commands issued on the command line regardless of success or failure. This list of commands is finite, however, and will toss off the oldest command after having issued a number of commands. I have not checked, but I think on average that would be somewhere between 500-1000 commands (maybe more).

Whether a command was successful or not is not stored beyond the last command given. The exit code of the last command can be retrieved using the $? variable (non-zero is meant to indicate that an error occurred).

The reason this is not stored for longer is that in almost all cases it would not be useful.

Example:

  1. You opened Terminal just now and find yourself in your home directory (typically indicated by a simple tilde ~).
  2. Now you issue cd Downloads, which under normal circumstances shall be a successfully issued command.
  3. Finding yourself in Downloads now, you can try to recall the last command by pressing the up-arrow key and hit Enter one more time.
  4. However now, unless you manually created another directory called Downloads inside your Downloads directory, you will see that this time the command will fail.

So, in order to have a useful list of commands which were successfully issued, you would need to also record the environment, which includes the current path you were located in, the files which were available at the time you issued the command as well as their content and possibly other things I may have forgotten to list.

You therefore see that such a list is impossible to obtain or even worse to maintain.

If this is for you to learn how zsh works, it is better to look through manpages or online resources rather than tediously record successfully run commands along with their arguments in a text file as the next time you run them, they may no longer be successful or provide results you would not expect, effectively hampering your learning experience by confusing you.