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:
- You have an interactive shell (collecting the history)
- You run make from the shell
- 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
.