0

I have a custom 'runner'-script that I need to use to run all of my terminal commands. Below you can see the general idea in the script.

#!/usr/bin/env bash
echo "Running '$@'"
# do stuff before running cmd
$@
echo "Done"
# do stuff after running cmd

I can use the script in bash as follows:

$ ./run.sh echo test
Running 'echo test'
test
Done
$

I would like to use it like this:

$ echo test
Running 'echo test'
test
Done
$

Bash has the trap ... DEBUG and PROMPT_COMMAND, which lets me execute something before and after a command, but is there something that would allow me to execute instead of the command?

There is also the command_not_found_handle which would work if I had an empty PATH env variable, but that seems too dirty.

Al Hoo
  • 528
  • 5
  • 11
  • 1
    As an aside, you are missing obligatory quotes around `"$@"`. Without the double quotes, your code is broken - it cannot handle quoted arguments correctly. – tripleee Mar 19 '19 at 10:59
  • If the purpose of your script is to run arbitrary code, then this is the case where you *should* use `eval`. Just use `eval "$1"` instead of `"$@"`, and run the script as `./run.sh 'echo test'` instead. (And make sure that the string you pass to the script is properly quotes, e.g., `./run.sh 'echo "This is an asterisk: *"'`. – chepner Mar 19 '19 at 11:23
  • Possible duplicate of [detecting command not found in bash script](https://stackoverflow.com/questions/16918272/detecting-command-not-found-in-bash-script) – vfalcao Mar 19 '19 at 15:32

2 Answers2

0

After some digging, I ended up looking at the source code and found that bash does not support custom executors. Below is a patch to add a new handle that works similarly as the command_not_found_handler.

diff --git a/eval.c b/eval.c
index f02d6e40..8d32fafa 100644
--- a/eval.c
+++ b/eval.c
@@ -52,6 +52,10 @@
 extern sigset_t top_level_mask;
 #endif

+#ifndef EXEC_HOOK
+#  define EXEC_HOOK "command_exec_handle"
+#endif
+
 static void send_pwd_to_eterm __P((void));
 static sighandler alrm_catcher __P((int));

@@ -172,7 +176,15 @@ reader_loop ()
              executing = 1;
              stdin_redir = 0;

-             execute_command (current_command);
+             SHELL_VAR *hookf = find_function (EXEC_HOOK);
+              if (hookf == 0) {
+                  execute_command (current_command);
+              } else {
+                  char *command_to_print = make_command_string (current_command);
+                  WORD_LIST *og = make_word_list(make_word(command_to_print), (WORD_LIST *)NULL);
+                  WORD_LIST *wl = make_word_list(make_word(EXEC_HOOK), og);
+                  execute_shell_function (hookf, wl);
+              }

            exec_done:
              QUIT;

One can then define function command_exec_handle() { eval $1; } which will be executed instead of the original command given in the prompt. The original command is fully in the first parameter. The command_exec_handle can be given in .bashrc and it works as expected.

Notice: this is very dangerous! If you mess up and put a bad command_exec_handler in your .bashrc, you might end up with a shell that does not execute commands. It will be quite hard to fix without booting from a live cd.

Al Hoo
  • 528
  • 5
  • 11
-2

It seems you have the same problem listed here. If you want to run some commands if your original command was not found, the Bash 4's command_not_found_handler will certainly fit your needs.

Try to be more specific, maybe with some code snippets that do or do not work, in order to help us to help you...

vfalcao
  • 332
  • 1
  • 3
  • 12