0

I'm looking for a way to access the full command from shell script, e.g.

Assume I have a script called test.sh. When I run it, the command line is passed to ruby as is (except the script itself is removed).

$ test.sh print ENV['HOME']

Is equivalent to

$ ruby -e "print ENV['HOME']"
3
  • Why is this an external script? Could you use a shell function instead? Commented Jan 8, 2015 at 1:23
  • 1
    If subprocesses could access the original command, ruby wouldn't need shell quoting for a command passed with ruby -e; instead, it could parse it out from something like ruby -e -- print ENV['HOME']. Being a shell script doesn't give a subprocess any additional access to the parent shell's state that it wouldn't have if it were a Ruby program, a Python program, etc; thus, your child shell script is subject to the exact same constraints that Ruby itself is. Commented Jan 8, 2015 at 1:25
  • 1
    Also, consider the security implications of what you're asking for. If $(rm -rf /) was part of a syntactically valid (and harmless) Ruby program, you still wouldn't want to risk your shell interpreting it before passing that content through to the Ruby interpreter -- meaning there's a need for a way to delimit data that shouldn't be parsed as syntax by the outer shell. Hence, quoting. Commented Jan 8, 2015 at 1:28

1 Answer 1

1

When you run:

test.sh print ENV['HOME']

...then, before test.sh is started, the shell runs string-splitting, expansion, and similar processes. Thus, what's eventually run is (assuming no glob expansion):

execvp("test.sh", {"test.sh", "print", "ENV[HOME]"});

If you have a file named ENVH in the current directory, the shell may treat ENV['HOME'] as a glob, expanding it by replacing the glob expression with the filename, and thus running:

execvp("test.sh", {"test.sh", "print", "ENVH"});

...in any event, what exists on the other side of the execv*-series call done to run the new program has no information which was local to the original shell -- and thus no way of knowing what the original command was before parsing and expansion. Thus, it is impossible to retrieve the original string unless the outer shell is modified to expose it out-of-band (as via an environment variable).

This is why your calling convention should instead require:

test.sh "print ENV['HOME']"

or, allowing even more freedom from shell quoting/escaping syntax, passing program text via stdin, as with:

test.sh <<'EOF'
print ENV['HOME']
EOF

Now, if you want to modify your shell to do that, I'd suggest a function that exposes BASH_COMMAND. For instance:

shopt -s extdebug
expose_command() {
  export SHELL_COMMAND="$BASH_COMMAND"
  return 0
}
trap expose_command DEBUG

...then, inside test.sh, you can refer to SHELL_COMMAND. Again, however: This will only work if the calling shell had that trap configured, as within a user's ~/.bashrc; you can't simply put the above content in a script and expect it to work, because it's only the interactive shell -- the script's parent process -- that has access to this information and is thus able to expose it.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the detailed answer. I'll try out the trap approach.
If you have control over your user's ~/.bashrc, you could also make the thing you're currently exposing as an executable be a shell function instead (sourced from .bashrc), and then it could access BASH_COMMAND directly.
I've played with it and could do exactly what you said in Bash. However my primary shell is Zsh and extdebug does not work there. I've tried preexec hook and got no luck. What alternatives are available in zsh? Thanks again!
Sorry -- I gave up zsh upwards of a decade ago (when I discovered that its use was making me sloppy when trying to write code for POSIX shells). You might ask a variant of the question tagged for zsh specifically to try to find someone who can help there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.