1

I'm just curious about the sys.argv behavior I find strange and inconsistent.

I expected sys.argv to only contain the script arguments (not the arguments that are parsed by the python3 executable).

In practice, I see that sys.argv contains the -c argument intended for python3. What surprises me then is that the next argument - the script body - is missing. I expect either 2 arguments or 4, but not 3.

What's the logic behind this?

$ python3 -c 'import sys;print(sys.argv)' 1 2
['-c', '1', '2']

Update: Ok. I got it. This only happens with the -c argument and not other arguments:

$ python3 -b -c 'import sys;print(sys.argv)' -d 1 2
['-c', '-d', '1', '2']

I still find it confusing and with that in the inline script case the argv[0] would contain the script text or -c <script text> or python3 -c <script text>

P.S. Here is the script that initially confused me:

python3 -c '
import argparse
import sys
parser = argparse.ArgumentParser()
print(parser.parse_known_args(sys.argv))
' -a 1 -b 2 -c 3
(Namespace(), ['-c', '-a', '1', '-b', '2', '-c', '3'])

I was surprised to see the first -c in the output. Turns out you should not just pass argv to parse_known_args.

Ark-kun
  • 6,358
  • 2
  • 34
  • 70
  • 2
    `argv[0]` is the name *of* the script. Everything else is an argument *to* the script. So, if you had both `-c` and the script text, then you couldn't use `argparse` normally, because the script text would be unexpected. – Charles Duffy Oct 10 '18 at 18:17
  • But `-c` is not an argument to a script. It's an argument to `python3`. `pythohn3` "consumes" the `-c` and the script text, so `-c` should not be in the arguments list. – Ark-kun Oct 10 '18 at 18:20
  • You can find explanation here: https://docs.python.org/3/using/cmdline.html – Nir Alfasi Oct 10 '18 at 18:21
  • 1
    Yes, it's an argument to python3, but there still has to be *something* there in the argv array describing how the script was invoked (since there *is* no script filename that could be put there; if one put the text of the script in that position, poorly-written usage-error-handling code could end up making a user-confusion mess by printing that source code out to end users as if it were a command name). `-c` is a very reasonable choice; it's short and clear to the reader... and well, *some* choice had to be made, even if it was a decision to make `argv[0]` be `None`. – Charles Duffy Oct 10 '18 at 18:22
  • BTW, there's history for closing "why did the designers of language X choose to do Y?" questions as outside of the site's scope -- see [Is asking “why” on language specifications still considered as “primarily opinion-based” if it can have official answers?](https://meta.stackoverflow.com/questions/323334/is-asking-why-on-language-specifications-still-considered-as-primarily-opinio) on [meta]. – Charles Duffy Oct 10 '18 at 18:25
  • Not an exact duplicate, but closely related: [Is `argv[0]` = name-of-executable an accepted standard or just a common convention?](https://stackoverflow.com/questions/2050961/is-argv0-name-of-executable-an-accepted-standard-or-just-a-common-conventi) – Daniel Pryden Oct 10 '18 at 18:37

1 Answers1

4

Something has to be there (so programs that expect user-provided arguments to start at argv[1] still work correctly), and -c is as sensible a choice as any.


The logic is that there's only one special argv element, and that's the first one -- argv[0] -- used to identify how the script was invoked. Sometimes that's a name it was invoked under; in this case, -c tells you it was invoked by passing python -c '...script text...'.

All the other elements are expected to be arguments to the script itself. If the script text were there as an argument to that script itself, you couldn't use the same command-line parsing tools for scripts passed as text as you do for every other case.


As a point of comparison, look at bash -c '...script text...' "$0" "$1", which tends to cause a fair bit of confusion (it's conventional to pass _ in the $0 position, but then people who see it are always asking "what's that placeholder for?"; it adds very little value). Python's approach of always using -c in the placeholder position and passing further arguments in position 1 and onwards makes sense.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Ok. I got it. This only happens with the `-c` argument and not other arguments: ` $ python3 -b -c 'import sys;print(sys.argv)' -d 1 2` ` ['-c', '-d', '1', '2']` I still find it confusing and wish that in the inline script case the `argv[0]` would contain the script text or `-c – Ark-kun Oct 10 '18 at 18:31
  • >"-c is as sensible a choice as any." I find it a bit less sensible, because it's not *that* `-c` I've passed. It's an arbitrary inline script indicator that could as well be `""` or `python3` or `python3 -c – Ark-kun Oct 10 '18 at 18:34
  • @Ark-kun: I think the key takeaway here is that you should almost always be ignoring `argv[0]`. It's never reliably *anything*. Don't expect it to be meaningful in this case, or indeed really in any case. – Daniel Pryden Oct 10 '18 at 18:35
  • Yes. I was just confused by it being `-c`. My train of thought was as follows: "`argv[0]` == `-c` looks like an argument I've passed, not like a program name. That means that Python only has arguments in `argv`, not the program. But why does it include the `-c` arg that it has parsed?" I won't have been fooled if `argv[0]` contained anything other that `-c`. – Ark-kun Oct 10 '18 at 18:47
  • `` would have been another reasonable choice, I agree. – Charles Duffy Oct 10 '18 at 19:33
  • I found my original source of confusion: It's `argparse`: calling `argparse.ArgumentParser().parse_known_args(sys.argv)` outputs the `-c` in its list of extra arguments. I.e. `(Namespace(), ['-c', '-a', '1', '-b', '2', '-c', '3'])` for `python3 -c – Ark-kun Oct 10 '18 at 22:44