1

I would like to configure my bash in a way so that I react on the event that the user enters a command. The moment they press Enter I would like my bash to run a script I installed first (analog to any PROMPT_COMMAND which is run each time a prompt is given out). This script should be able to

  • see what was entered,
  • maybe change it,
  • maybe even make the shell ignore it (i. e. make it not execute the line),
  • decide on whether the text shall be inserted in the history or not,
  • and maybe similar things.

I have not found a proper way to do this. My current implementations are all flawed and use things like debug traps to intervene before executing a command or (HISTTIMEFORMAT='%s '; history 1) to ask the history after the command execution is complete about things when the command was started etc (but that is only hindsight which is not really what I want).

I'd expect something like a COMMAND_INTERCEPTION variable which would work similar to PROMPT_COMMAND but I'm not able to find anything like it.

I also considered to use command line completion to achieve my goal but wasn't able to find anything about reacting on sending a finished command in this, but maybe I just didn't find it.

Any help appreciated :)

Alfe
  • 56,346
  • 20
  • 107
  • 159
  • 1
    A few days ago I sent an answer for a question about [measuring time of commands in bash using `PS0` and `PS1`](http://stackoverflow.com/questions/43201274/use-ps0-and-ps1-to-display-execution-time-of-each-bash-command/43203887#43203887). I believe you can do what you want "embedding" your action in `PS0`. So, the link might provide the necessary inspiration to you (though I'm really not sure about "intervention", "exchange the input command", etc. i.e. how to make this working.) – Scheff's Cat Apr 05 '17 at 10:24
  • 1
    `PS0`, nice :) Unfortunately my Ubuntu 16.04 (the most recent LTS version) does still contain a bash which doesn't have that patch, so it doesn't know anything about `PS0`. Also, from what I read this PS0 has no access to the given command, cannot read or change it. But thanks for the hint on `PS0` anyway! At least for future setups I might use this one. – Alfe Apr 05 '17 at 10:38
  • So, I guess your solution is somehow like [DEBUG trap and PROMPT_COMMAND in Bash](https://seasonofcode.com/posts/debug-trap-and-prompt_command-in-bash.html) (but probably more complicated). Btw. I even didn't notice that `PS0` is such a new bash feature. Thanks, again I've learnt something new... – Scheff's Cat Apr 05 '17 at 10:51
  • 1
    `PS0` is new added in bash-4.4. – pynexj Apr 05 '17 at 10:55

1 Answers1

1

You can use the DEBUG trap and the extdebug feature, and peek into BASH_COMMAND from the trap handler to see the running command. (Though as noted in comments, the debug trap is sprung on every simple command, not every command line. Also subshells elude it.)

The debug handler can prevent the command from running, but can't change it directly. Though of course you could run any command inside the debugger, possibly using BASH_COMMAND and eval to build it and then tell the shell to ignore the original command.

This would prevent running anything starting with ls:

$ preventls() { case "$BASH_COMMAND" in ls*) echo "no!"; return 1 ;; esac; }
$ shopt -s extdebug
$ trap preventls DEBUG
$ ls -l
no!

Use trap - DEBUG to remove the trap. Tested on Bash 4.3.30.

ilkkachu
  • 6,221
  • 16
  • 30
  • `\ls` or `'ls'` or `a= ls` etc. – gniourf_gniourf Apr 05 '17 at 16:41
  • 1
    @gniourf_gniourf, or `/bin/ls`? I didn't say it can't be worked around. Given it's meant for debugging, not security, I don't really see a problem there either. – ilkkachu Apr 05 '17 at 16:47
  • That's right, I'm not interested in security issues or sandboxing anything. I'm into comforting my work. But another issue troubles me much more: If I enter a pipe like `true | true | false | false`, this debug script gets called for each part of the pipe singly (once for each `true` and once for each `false`), and even once for my `PROMPT_COMMAND`. That's kind of problematic because I cannot change a command within the pipe context by executing something myself. – Alfe Apr 05 '17 at 21:25
  • Commands like `(exit 1)` do not call the debug script at all. For a line like `true | (exit 1) | false` I get two calls, one for `true`, one for `false`. A command like `{ date; }` doesn't call the script at all, `{ false; }` calls it with `false` as command. That's all in all too weird for my taste and eventually doesn't fit my usecase (which is e. g. fixing typing errors before executing command lines, asking some facility for permission before execution, preparing something before actually executing the command, etc.). – Alfe Apr 05 '17 at 21:34
  • `{ date | tr A E; }` → `date`, `tr A E` (ok, kind of), but `{ date | tr A E; } | tr E A` → `tr E A` (braces completely ignored). – Alfe Apr 05 '17 at 21:36
  • 1
    @Alfe, hmm, true, the doc says it traps for every _simple command_, so every part of a pipeline is separate. If you mainly care about commands entered interactively, it might be better to look at readline and the command line editing features. But I'm not sure what can be done with them, sorry. – ilkkachu Apr 05 '17 at 21:38
  • 1
    :) Thanks anyway for the hint with `extdebug` and its features. It has some more aspects which might come in handy at times and on the other hand can be a problem because I do not really want them to be active all the time :-} – Alfe Apr 05 '17 at 21:46