Skip to content

Commit 85b9e3e

Browse files
committed
Honor HISTCONTROL ignorespace and ignoreboth
After 3458480 Remove ignorespace from $HISTCONTROL and after 7e55ac1 Follow up commit for issue #6 -Replace ignoreboth with simpley ignoredups this script would remove 'ignorespace' and would replace 'ignoreboth' with 'ignoredups'. This effectively disables the functionality of not adding space prefixed commands into history. It used to happen siliently and could be quite confusing to users who use this feature. This script relies on the command to be in the history, but we can mostly fix the issue by "manual" removing a whitespace prefixed command from the history after reading it from there.
1 parent da2dbb5 commit 85b9e3e

File tree

3 files changed

+72
-13
lines changed

3 files changed

+72
-13
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ curl https://raw.githubusercontent.com/rcaloras/bash-preexec/master/bash-preexec
3030
echo '[[ -f ~/.bash-preexec.sh ]] && source ~/.bash-preexec.sh' >> ~/.bashrc
3131
```
3232

33+
NOTE: this script may change your `HISTCONTROL` value by removing `ignorespace` and/or replacing `ignoreboth` with `ignoredups`. See [`HISTCONTROL` interaction](#histcontrol-interaction) for details.
34+
3335
## Usage
3436
Two functions **preexec** and **precmd** can now be defined and they'll be automatically invoked by bash-preexec if they exist.
3537

@@ -91,6 +93,10 @@ export __bp_enable_subshells="true"
9193
```
9294
This is disabled by default due to buggy situations related to to `functrace` and Bash's `DEBUG trap`. See [Issue #25](https://github.com/rcaloras/bash-preexec/issues/25)
9395

96+
## `HISTCONTROL` interaction
97+
98+
In order to be able to provide the last command text to the `preexec` hook, this script will remove `ignorespace` and/or will replace `ignoreboth` with `ignoredups` in your `HISTCONTROL` variable. It will remember if `HISTCONTROL` has been modified and will remove the last command from the history "manually", after reading the last command from the history list. This may cause issues when you have scripts that rely on the literal value of `HISTCONTROL` or manipulate history in their own ways.
99+
94100
## Tests
95101
You can run tests using [Bats](https://github.com/bats-core/bats-core).
96102
```bash

bash-preexec.sh

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ __bp_imported="defined"
5151
__bp_last_ret_value="$?"
5252
BP_PIPESTATUS=("${PIPESTATUS[@]}")
5353
__bp_last_argument_prev_command="$_"
54+
__bp_ignorespace=
5455

5556
__bp_inside_precmd=0
5657
__bp_inside_preexec=0
@@ -70,17 +71,22 @@ __bp_require_not_readonly() {
7071
done
7172
}
7273

73-
# Remove ignorespace and or replace ignoreboth from HISTCONTROL
74-
# so we can accurately invoke preexec with a command from our
75-
# history even if it starts with a space.
74+
# Remove "ignorespace" and/or replace "ignoreboth" in HISTCONTROL so we can
75+
# accurately invoke preexec with a command from our history even if it starts
76+
# with a space. We then remove commands that start with a space from the
77+
# history "manually", if either "ignorespace" or "ignoreboth" was part of
78+
# HISTCONTROL.
7679
__bp_adjust_histcontrol() {
7780
local histcontrol
78-
histcontrol="${HISTCONTROL//ignorespace}"
79-
# Replace ignoreboth with ignoredups
80-
if [[ "$histcontrol" == *"ignoreboth"* ]]; then
81-
histcontrol="ignoredups:${histcontrol//ignoreboth}"
82-
fi;
83-
export HISTCONTROL="$histcontrol"
81+
if [[ ":$HISTCONTROL:" == *":ignorespace:"* || ":$HISTCONTROL:" == *":ignoreboth:"* ]]; then
82+
__bp_ignorespace=yes
83+
fi
84+
histcontrol=:${HISTCONTROL//:/::}:
85+
histcontrol=${histcontrol//:ignorespace:}
86+
histcontrol=${histcontrol//:ignoreboth:/:ignoredups:}
87+
histcontrol=${histcontrol//::/:}
88+
histcontrol=${histcontrol#:}
89+
export HISTCONTROL=${histcontrol%:}
8490
}
8591

8692
# This variable describes whether we are currently in "interactive mode";
@@ -239,6 +245,23 @@ __bp_preexec_invoke_exec() {
239245
return
240246
fi
241247

248+
# If we have removed "ignorespace" or "ignoreboth" from HISTCONTROL
249+
# during setup, we need to remove commands that start with a space from
250+
# the history ourselves.
251+
252+
# With bash 5.0 or above, we could have just ran
253+
#
254+
# builtin history -d -1
255+
#
256+
# Negative indices for `-d` are not supported before 5.0, so we compute the
257+
# length of the history list explicit, to delete the last entry.
258+
if [[ -n "$__bp_ignorespace" && "$this_command" == " "* ]]; then
259+
builtin history -d "$(
260+
export LC_ALL=C
261+
HISTTIMEFORMAT= history 1 | sed '1 s/^ *\([0-9][0-9]*\).*/\1/'
262+
)"
263+
fi
264+
242265
# Invoke every function defined in our function array.
243266
local preexec_function
244267
local preexec_function_ret_value

test/bash-preexec.bats

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,18 +306,17 @@ test_preexec_echo() {
306306
# Should remove ignorespace
307307
HISTCONTROL="ignorespace:ignoredups:*"
308308
__bp_adjust_histcontrol
309-
[ "$HISTCONTROL" == ":ignoredups:*" ]
309+
[ "$HISTCONTROL" == "ignoredups:*" ]
310310

311311
# Should remove ignoreboth and replace it with ignoredups
312312
HISTCONTROL="ignoreboth"
313313
__bp_adjust_histcontrol
314-
[ "$HISTCONTROL" == "ignoredups:" ]
314+
[ "$HISTCONTROL" == "ignoredups" ]
315315

316316
# Handle a few inputs
317317
HISTCONTROL="ignoreboth:ignorespace:some_thing_else"
318318
__bp_adjust_histcontrol
319-
echo "$HISTCONTROL"
320-
[ "$HISTCONTROL" == "ignoredups:::some_thing_else" ]
319+
[ "$HISTCONTROL" == "ignoredups:some_thing_else" ]
321320

322321
}
323322

@@ -340,6 +339,7 @@ test_preexec_echo() {
340339

341340
run '__bp_preexec_invoke_exec'
342341
[ $status -eq 0 ]
342+
echo "__bp_preexec_invoke_exec: output: '$output'"
343343
[ "$output" == " this command has whitespace " ]
344344
}
345345

@@ -362,3 +362,33 @@ a multiline string'" ]
362362
[ $status -eq 0 ]
363363
[ "$output" == '-n' ]
364364
}
365+
366+
@test "HISTCONTROL is updated, but ignorespace functionality is honoured" {
367+
preexec_functions+=(test_preexec_echo)
368+
HISTCONTROL=ignorespace:ignoreboth
369+
370+
__bp_adjust_histcontrol
371+
372+
[[ "$HISTCONTROL" == "ignoredups" ]]
373+
374+
__bp_interactive_mode
375+
376+
command1="this command is in the history"
377+
378+
history -s "$command1"
379+
run '__bp_preexec_invoke_exec'
380+
[[ $status == 0 ]]
381+
[[ "$output" == "$command1" ]]
382+
last_history=$(HISTTIMEFORMAT= history 1 | sed '1 s/^ *[0-9][0-9]* *//')
383+
[[ "$last_history" == "$command1" ]]
384+
385+
command2=" this should not be in the history"
386+
387+
history -s "$command2"
388+
# we need to extract command history in the subshell, as the parent shell
389+
# history is actually not affected.
390+
output=$(__bp_preexec_invoke_exec && \
391+
printf "last_history: %s\n" "$(HISTTIMEFORMAT= history 1 | sed '1 s/^ *[0-9][0-9]* *//')" )
392+
[[ $status == 0 ]]
393+
[[ "$output" == "$command2"$'\n'"last_history: $command1" ]]
394+
}

0 commit comments

Comments
 (0)