1

I have a piece of code that looks like this. The real code is of course much longer. I have shortened it to condense it to the idea.

#!/usr/bin/python

from sys import argv

class Base():
    def whoami(self):
        print(self.__class__.__name__)

    def foo(self, a, b):
        print(a+b)

class A(Base):
    def bar(self, a):
        print(a)

class B(Base):
    def fizz(self, a, b):
        print(a+b)

def main():
    eval(argv[1] + '().' + argv[2] + '(' + ', '.join(argv[3:]) + ')')

if __name__ == "__main__":
    main()

Expected output:

$ ./myScript.py A whoami
A
$ ./myScript.py B fizz 5 7
12
$ ./myScript.py B fizz '5' '7'
57

But the actual output for the last line is:

$ ./myScript.py B fizz '5' '7'
12

The quotes does not get passed. My goal is to be able to call the various class methods easily from command line for debugging. I don't know if my approach is wrong from the beginning.

And yes, I'm aware of security risks with eval. I intend to remove it when the code goes into production.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 2
    The quotes are never preserved, since these are "directives" for the shell how to interpret the parameters. It is usually used to add spaces to the parameters, like `command 'foo bar' qux`, versus `command foo bar qux` – Willem Van Onsem Jul 13 '19 at 09:48
  • May be [this answer](https://stackoverflow.com/a/19120324/6699447) will help. – Abdul Niyas P M Jul 13 '19 at 09:50
  • You can however add quotes in your parameters, like `myscripty.py B fizz '"5"' '"7"'`. – Willem Van Onsem Jul 13 '19 at 09:50
  • 2
    Using `eval` like that would give a security engineer a heart attack and a penetration tester a field day – Tim Jul 13 '19 at 09:51
  • @WillemVanOnsem That's is a viable solution. You could post it as an answer. – klutt Jul 13 '19 at 09:54

1 Answers1

3

The quotes does not get passed. My goal is to be able to call the various class methods easily from command line for debugging.

The quotes are not part of the parameters you used. The quotes are interpreted by the shell. Usually quotes are used to add spaces to a parameter.

For example:

command foo bar

will pass two parameters (foo and bar) to the command, but if you want to add spaces, you can use:

command 'foo bar'

Now the shell will interpret this as a single parameter, with a space.

You can however add quotes in your parameters, like:

command '"foo"' '"bar"'

or:

command "'foo'" "'bar'"

to pass values with quotes (double quotes " in the first example and single quotes ' in the second one) to the command. You can escape parts of the parameters as well, like command "\"Thank you\", she said.", and thus use double quotes and single quotes in the same parameter.

That being said, please do not use eval(..) to evaluate strings. eval(..) has severe security risks, see Security of Python's eval() on untrusted strings? for more information. You can use ast.literal_eval [python-doc] to evaluate literals. Furthermore you can use a dictionary, etc. to route the call to the correct function(s).

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Nice answer. I'll wait a few days with accepting it in case something better comes up. I have upvoted it. – klutt Jul 13 '19 at 10:59
  • How could anything better come up? This is the truth, in terms of how the shell parses command strings into argument vectors. – Charles Duffy Jul 13 '19 at 15:08