Home > Software design >  Can you run history within a Makefile?
Can you run history within a Makefile?

Time:12-07

Makefiles have been around for a while, so I wouldn't be surprised if this is a duplicate!

I've been using Makefiles to bundle bash operations with lots of flags or arguments (not their original purpose, I know)

Is it possible to implement the following history command in a Makefile?

history | tail -n 100 | cut -c 8- | sort | uniq

Makefile

#* Setup
.PHONY: $(shell sed -n -e '/^$$/ { n ; /^[^ .\#][^ ]*:/ { s/:.*$$// ; p ; } ; }' $(MAKEFILE_LIST))
.DEFAULT_GOAL := help

help: ## list make commands
    @echo ${MAKEFILE_LIST}
    @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-] :.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

#* Commands
get-history: ## custom history lookup command
    @history | tail -n 100 | cut -c 8- | sort | uniq

HIS ?= ${history}
test: ## test
    echo ${HIS}

^this obviously didn't work so any tips or doc links would be appreciated

CodePudding user response:

Assuming that you Makefile is run from an interactive bash, and that you want to access the history of this interactive shell from within the Makefile, I would approach it as follows:

As a setup, I would ensure that your interactive bash has its own history file, so that it does not get mixed up with the history of bash processes in other terminals.

HISTFILE=$HOME/.hi.$$
export HISTFILE_TO_USE=$HISTFILE

In practice, you would put these three lines into a shell function defined in your .bashrc, so that you can type it with a single command every time you need it.

From now on, commands in your shell will be added to your $HISTFILE. Right before make is invoked, you do a

history -a

which appends the history to your history file. Perhaps the best would be do create in your .bashrc a function wrapper or an alias for make, so that you don't have to do this manually everytime:

makhi() {
  history -a
  make "$@"
}

Now the problem of how to pick up the history inside a bash script; this includes the use in your makefile, since you can always run scripts from the makefile.

IMO, it should work like this:

#!/usr/bin/bash
PARENT_HISTFILE=${HISTFILE_TO_USE:-$HOME/.bash_history}
set -o history # turn on history
shopt -s histappend # Ensure that the history file is not overwritten
histfile -r "$PARENT_HISTFILE" # Read saved history into memory
history # should show the saved history
...

However, for reason I don't know, this did not work for as documented, in that histfile -r did not read the history.

UPDATE based on an idea by tripleee: The solution works if I make this bash interactive, i.e. have the shebang line #!/usr/bin/bash -i, but I still don't understand, why this is necessary in this case. If you follow this route, don't forget that this means that your .bashrc will be processed. If this causes trouble, add the option --norc.

However, you can alternatively access the history simply with cat:

#!/usr/bin/bash
PARENT_HISTFILE=${HISTFILE_TO_USE:-$HOME/.bash_history}
cat "$PARENT_HISTFILE" # shows the saved history   

Assuming that your script to display the history is named tailhist and has executable rights and is in your PATH, you can invoke it from your makefile as

get-history:
  @tailhist

Actually, for your use case, I could imagine a completely different approach useful. What you are doing is:

  1. You have an interactive shell (collecting the history)
  2. You run make from the shell
  3. You want to display part of your history from make

Since the make process itself is unrelated from your history (i.e. from the viewpoint of make, the history is frozen once make starts to run), you could equally well run the history command before you even enter make. The outcome will be the same. For instance, if you define your function as

makhi() {
  export makhi=$(history | tail -n 100 | cut -c 8- | sort | uniq)
  make "$@"
}

you just have to print from within make the environment variable makhi.

  • Related