Modify all bash commands through a program before executing them

I am trying to create a program which require such feature. The flow will be like:

  • User enter a bash command
  • User hit the enter
  • My script will get command, current directory,.. as variables. Program can optionally modify the command.
  • Modified command will get executed normally.

Is there any way to do this?

Note: I need this for my personal use, I am not going to distribute this program.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I did some research on it. We can use bash TRAP and shopt option to achieve this.

Add this to .bash_profile

shopt -s extdebug

preexec_invoke_exec () {
    [ -n "$COMP_LINE" ] && return  # do nothing if completing
    [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return # don't cause a preexec for $PROMPT_COMMAND
    local this_command=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//"`;

    # So that you don't get locked accidentally
    if [ "shopt -u extdebug" == "$this_command" ]; then
        return 0
    fi

    # Modify $this_command and then execute it
    return 1 # This prevent executing of original command
}
trap 'preexec_invoke_exec' DEBUG

It works like this:

trap 'function_name' DEBUG causes function_name to execute before executing bash commands. But by default return value have no effect over original command.

shopt -s extdebug enables some debugging features with one of them checks for return value before executing original command.

Note: shopt -u extdebug disable this feature so original command always get executed.

Documentation of extdebug (See second feature):

If set, behavior intended for use by debuggers is enabled:

The -F option to the declare builtin (see Bash Builtins) displays the source file name and line number corresponding to each function name supplied as an argument.
If the command run by the DEBUG trap returns a non-zero value, the next command is skipped and not executed.
If the command run by the DEBUG trap returns a value of 2, and the shell is executing in a subroutine (a shell function or a shell script executed by the . or source builtins), a call to return is simulated.
BASH_ARGC and BASH_ARGV are updated as described in their descriptions (see Bash Variables).
Function tracing is enabled: command substitution, shell functions, and subshells invoked with ( command ) inherit the DEBUG and RETURN traps.
Error tracing is enabled: command substitution, shell functions, and subshells invoked with ( command ) inherit the ERR trap.

Method 2

You can get some way towards your goal with a simple bash script that uses the built-in readline system to get a line. For example:

#!/bin/bash -i
while read -e -p '$ ' line
do    echo "your cmd: $line"
      eval "$line"
done

The script reads a line of input (unless end-of-file) using readline editing (-e) and then echoes and executes it. Note the -i on the #! to ensure the script is interactive. You can build your code to manipulate the input command based on this. For example,

#!/bin/bash -i

myfn(){
  echo "in dir $1. doing: $2" >&2
  echo "$2" # manipulate command here and echo the result
}

while read -e -p "$PS1" line
do    newcmd=$(myfn "$PWD" "$line")
      eval "$newcmd"
done

Method 3

Install bash-preexec – preexec and precmd functions for Bash just like Zsh

Two functions preexec and precmd can now be defined and they’ll be automatically invoked by bash-preexec if they exist.

  • preexec Executed just after a command has been read and is about to be executed. The string that the user typed is passed as the first
    argument.
  • precmd Executed just before each prompt. Equivalent to PROMPT_COMMAND, but more flexible and resilient.

.

source ~/.bash-preexec.sh
preexec() { echo "just typed $1"; }
precmd() { echo "printing the prompt"; }

Should output something like:

<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="76131a131b1318020c363d17051e1b1f04">[email protected]</a>:~/git/bash-preexec (master)$ ls
just typed ls
bash-preexec.sh  README.md  test
printing the prompt

You can also define functions to be invoked by appending them to two different arrays. This is great if you want to have many functions invoked for either hook.

Method 4

In any shell script, the currently executing command is available as $0, and the current directory can be easily retrieved with a call to pwd. In order to do what you want, you will have to write a program that can be used as a shell, and then set your user’s shell value to that program.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x