Skip to content

Commit f647318

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 9811ba8 commit f647318

File tree

3 files changed

+62
-9
lines changed

3 files changed

+62
-9
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/sstephenson/bats).
96102
```bash

bash-preexec.sh

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ __bp_imported="defined"
4646
__bp_last_ret_value="$?"
4747
BP_PIPESTATUS=("${PIPESTATUS[@]}")
4848
__bp_last_argument_prev_command="$_"
49+
__bp_ignorespace=
4950

5051
__bp_inside_precmd=0
5152
__bp_inside_preexec=0
@@ -61,15 +62,25 @@ __bp_require_not_readonly() {
6162
done
6263
}
6364

64-
# Remove ignorespace and or replace ignoreboth from HISTCONTROL
65-
# so we can accurately invoke preexec with a command from our
66-
# history even if it starts with a space.
65+
# Remove ignorespace and or replace ignoreboth from HISTCONTROL so we can
66+
# accurately invoke preexec with a command from our history even if it starts
67+
# with a space. We then remove commands that start with a space from the
68+
# history "manually", if either "ignorespace" or "ignoreboth" was part of
69+
# HISTCONTROL.
6770
__bp_adjust_histcontrol() {
6871
local histcontrol
69-
histcontrol="${HISTCONTROL//ignorespace}"
72+
if [[ "$HISTCONTROL" == *"ignorespace"* || "$HISTCONTROL" == *"ignoreboth"* ]]; then
73+
__bp_ignorespace=yes
74+
fi
75+
histcontrol="${HISTCONTROL//ignorespace:}"
76+
histcontrol="${histcontrol//:ignorespace}"
77+
histcontrol="${histcontrol//ignorespace}"
7078
# Replace ignoreboth with ignoredups
7179
if [[ "$histcontrol" == *"ignoreboth"* ]]; then
72-
histcontrol="ignoredups:${histcontrol//ignoreboth}"
80+
histcontrol="${histcontrol//ignoreboth:}"
81+
histcontrol="${histcontrol//:ignoreboth}"
82+
histcontrol="${histcontrol//ignoreboth}"
83+
histcontrol="ignoredups${histcontrol:+:}${histcontrol}"
7384
fi;
7485
export HISTCONTROL="$histcontrol"
7586
}
@@ -212,6 +223,13 @@ __bp_preexec_invoke_exec() {
212223
return
213224
fi
214225

226+
# If we have remove "ignorespace" or "ignoreboth" from HISTCONTROL during
227+
# setup, we need to remove commands that start with a space from the
228+
# history ourselves.
229+
if [[ -n "$__bp_ignorespace" && "$this_command" == " "* ]]; then
230+
builtin history -d -1
231+
fi
232+
215233
# If none of the previous checks have returned out of this function, then
216234
# the command is in fact interactive and we should invoke the user's
217235
# preexec functions.

test/bash-preexec.bats

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,18 +236,17 @@ test_preexec_echo() {
236236
# Should remove ignorespace
237237
HISTCONTROL="ignorespace:ignoredups:*"
238238
__bp_adjust_histcontrol
239-
[[ "$HISTCONTROL" == ":ignoredups:*" ]]
239+
[[ "$HISTCONTROL" == "ignoredups:*" ]]
240240

241241
# Should remove ignoreboth and replace it with ignoredups
242242
HISTCONTROL="ignoreboth"
243243
__bp_adjust_histcontrol
244-
[[ "$HISTCONTROL" == "ignoredups:" ]]
244+
[[ "$HISTCONTROL" == "ignoredups" ]]
245245

246246
# Handle a few inputs
247247
HISTCONTROL="ignoreboth:ignorespace:some_thing_else"
248248
__bp_adjust_histcontrol
249-
echo "$HISTCONTROL"
250-
[[ "$HISTCONTROL" == "ignoredups:::some_thing_else" ]]
249+
[[ "$HISTCONTROL" == "ignoredups:some_thing_else" ]]
251250

252251
}
253252

@@ -292,3 +291,33 @@ a multiline string'" ]]
292291
[[ $status == 0 ]]
293292
[[ "$output" == '-n' ]]
294293
}
294+
295+
@test "HISTCONTROL is updated, but ignorespace functionality is honoured" {
296+
preexec_functions+=(test_preexec_echo)
297+
HISTCONTROL=ignorespace:ignoreboth
298+
299+
__bp_adjust_histcontrol
300+
301+
[[ "$HISTCONTROL" == "ignoredups" ]]
302+
303+
__bp_interactive_mode
304+
305+
command1="this command is in the history"
306+
307+
history -s "$command1"
308+
run '__bp_preexec_invoke_exec'
309+
[[ $status == 0 ]]
310+
[[ "$output" == "$command1" ]]
311+
last_history=$(HISTTIMEFORMAT= history 1 | sed '1 s/^ *[0-9][0-9]*[* ] //')
312+
[[ "$last_history" == "$command1" ]]
313+
314+
command2=" this should not be in the history"
315+
316+
history -s "$command2"
317+
# we need to extract command history in the subshell, as the parent shell
318+
# history is actually not affected.
319+
output=$(__bp_preexec_invoke_exec && \
320+
printf "last_history: %s\n" "$(HISTTIMEFORMAT= history 1 | sed '1 s/^ *[0-9][0-9]*[* ] //')" )
321+
[[ $status == 0 ]]
322+
[[ "$output" == "$command2"$'\n'"last_history: $command1" ]]
323+
}

0 commit comments

Comments
 (0)