You can always tell your shell to tell applications what shell code lead to their execution. For instance, with zsh
, by passing that information in the $SHELL_CODE
environment variable using the preexec()
hook (printenv
used as an example, you'd use getenv("SHELL_CODE")
in your program):
$ preexec() export SHELL_CODE=$1
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv SHELL_CODE
printenv CODE
$ $(echo printenv SHELL_CODE)
$(echo printenv SHELL_CODE)
$ for i in SHELL_CODE; do printenv "$i"; done
for i in SHELL_CODE; do printenv "$i"; done
$ printenv SHELL_CODE; : other command
printenv SHELL_CODE; : other command
$ f() printenv SHELL_CODE
$ f
f
All those would execute printenv
as:
execve("/usr/bin/printenv", ["printenv", "SHELL_CODE"],
["PATH=...", ..., "SHELL_CODE=..."]);
Allowing printenv
to retrieve the zsh code that lead to the execution of printenv
with those arguments. What you would want to do with that information is not clear to me.
With bash
, the feature closest to zsh
's preexec()
would be using its $BASH_COMMAND
in a DEBUG
trap, but note that bash
does some level of rewriting in that (and in particular refactors some of the whitespace used as delimiter) and that's applied to every (well, some) command run, not the whole command line as entered at the prompt (see also the functrace
option).
$ trap 'export SHELL_CODE="$BASH_COMMAND"' DEBUG
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv $(echo 'SHELL_CODE')
printenv $(echo 'SHELL_CODE')
$ for i in SHELL_CODE; do printenv "$i"; done; : other command
printenv "$i"
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printf '%s\n' "$(printenv "SHELL_CODE")"
$ set -o functrace
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printenv "SHELL_CODE"
$ print${-+env } $(echo 'SHELL_CODE')
print${-+env } $(echo 'SHELL_CODE')
See how some of the spaces that are delimiters in the shell language syntax have been squeezed into 1 and how not the full command line is not always passed to the command. So probably not useful in your case.
Note that I would not advise doing this kind of thing, as you're potentially leaking sensitive information to every command as in:
echo very_secret | wc -c | untrustedcmd
would leak that secret to both wc
and untrustedcmd
.
Of course, you could do that kind of thing for other languages than the shell. For instance, in C, you could use some macros that exports the C code that executes a command to the environment:
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define WRAP(x) (setenv("C_CODE", #x, 1), x)
int main(int argc, char *argv[])
{
if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (!fork()) WRAP(0 + execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
wait(NULL);
return 0;
}
Example:
$ ./a.out printenv C_CODE
execlp("printenv", "printenv", "C_CODE", NULL)
0 + execlp("printenv", "printenv", "C_CODE", NULL)
execvp(argv[1], &argv[1])
See how some spaces were condensed by the C pre-processor like in the bash case. In most if not all languages, the amount of space used in delimiters makes no difference, so it's not surprising that the compiler/interpreter takes some liberty with them here.