145

What would be the best way to check if a variable was passed along for the script:

try:
    sys.argv[1]
except NameError:
    startingpoint = 'blah'
else:
    startingpoint = sys.argv[1]
Serenity
  • 35,289
  • 20
  • 120
  • 115
Cmag
  • 14,946
  • 25
  • 89
  • 140
  • Possible duplicate of [How to check if an argument from commandline has been set?](http://stackoverflow.com/questions/4188467/how-to-check-if-an-argument-from-commandline-has-been-set) – Shani Shalgi Aug 17 '16 at 12:16

8 Answers8

163

Check the length of sys.argv:

if len(sys.argv) > 1:
    blah = sys.argv[1]
else:
    blah = 'blah'

Some people prefer the exception-based approach you've suggested (eg, try: blah = sys.argv[1]; except IndexError: blah = 'blah'), but I don't like it as much because it doesn't “scale” nearly as nicely (eg, when you want to accept two or three arguments) and it can potentially hide errors (eg, if you used blah = foo(sys.argv[1]), but foo(...) raised an IndexError, that IndexError would be ignored).

David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • 1
    As long as you're being atomic about it though, there's nothing to worry about with `try, except`. Just wait to `foo` your argument until the `else` clause. – senderle Mar 24 '11 at 18:07
  • 3
    Also, once you're thinking about scaling, it's time to move over to `argparse`. – senderle Mar 24 '11 at 18:09
  • 1
    +1 on argparse. I've got `argparse.py` in all of my command-line projects. – David Wolever Mar 24 '11 at 18:15
  • 2
    @senderle: sure, if you're diligent about it, that's fine. But in my experience most programmers aren't diligent and put all the logic in the `try` clause… So, given that it's no harder (and, IMO, a bit prettier), I prefer to just check the length (also, you avoid having people yell at you for using exceptions for flow control). – David Wolever Mar 24 '11 at 18:18
  • 3
    @David, yeah, I do see your point. I'm a bit of a `try` apologist, I must admit. I just feel that it sometimes expresses my intent more clearly than an `if` statement. – senderle Mar 24 '11 at 18:23
  • Just to clarify, why doesn't it works with `if len(sys.argv) > 0`? Well, the argument #1 `sys.argv[0]`is the call to the function itself. – RicHincapie Aug 25 '20 at 13:54
76

In the end, the difference between try, except and testing len(sys.argv) isn't all that significant. They're both a bit hackish compared to argparse.

This occurs to me, though -- as a sort of low-budget argparse:

arg_names = ['command', 'x', 'y', 'operation', 'option']
args = dict(zip(arg_names, sys.argv))

You could even use it to generate a namedtuple with values that default to None -- all in four lines!

Arg_list = collections.namedtuple('Arg_list', arg_names)
args = Arg_list(*(args.get(arg, None) for arg in arg_names))

In case you're not familiar with namedtuple, it's just a tuple that acts like an object, allowing you to access its values using tup.attribute syntax instead of tup[0] syntax.

So the first line creates a new namedtuple type with values for each of the values in arg_names. The second line passes the values from the args dictionary, using get to return a default value when the given argument name doesn't have an associated value in the dictionary.

senderle
  • 145,869
  • 36
  • 209
  • 233
  • I love Python more and more, all thanks to answers like that and Stack Overflow of course! Too bad we don't have an explanation of last two lines. I believe for beginners that would be great. – Goujon Nov 03 '17 at 12:47
  • 2
    Love it! Easily the best solution I've seen of this problem. For others: It wasn't immediately obvious to me (newb) that:1) the 'command' will pass as the first argument each time and 2) you can call results with args[0] etc. – Matt D Nov 28 '17 at 03:23
  • This is cool for sure, but what's hacky about testing the length? – colorlace Mar 20 '19 at 22:03
  • 1
    @timwiz I was only speaking in comparison to using argparse. Still, these days, I kick myself every time I come back to an old script and find I didn't use argparse. – senderle Jul 09 '19 at 19:47
29

Another way I haven't seen listed yet is to set your sentinel value ahead of time. This method takes advantage of Python's lazy evaluation, in which you don't always have to provide an else statement. Example:

startingpoint = 'blah'
if len(sys.argv) >= 2:
  startingpoint = sys.argv[1]

Or if you're going syntax CRAZY you could use Python's ternary operator:

startingpoint = sys.argv[1] if len(sys.argv) >= 2 else 'blah'
jathanism
  • 33,067
  • 9
  • 68
  • 86
  • 1
    The ternary operator answer is to my eyes the prettiest. I'm sitting over here quietly cursing the fact that some of the software that I maintain is tied to Python 2.4, which doesn't have it. – Richard Barrell Apr 07 '12 at 01:09
20

I use this - it never fails:

startingpoint = 'blah'
if sys.argv[1:]:
   startingpoint = sys.argv[1]
anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140
  • That is not to check if defined. Is more like define if exists, wich is not the same. I used that way too to assing fallback variables, but is not an answer to the current question. – m3nda Aug 22 '15 at 06:56
  • @erm3nda I can remove `startingpoint` variable from example, but the question is assigning a fallback variable, so I just made the same. – anatoly techtonik Aug 22 '15 at 07:27
  • 1
    If you remove it you will get a error about variable not defined. I've read again the question, and what him expect is that, a variable replacement :) so it's ok. Thank your for the advice of that short way if `sys.argv[1:]:`. This works with positional arguments while count does not. – m3nda Aug 22 '15 at 08:13
  • 2
    Why does index `[1:]` work and `[1]` fails? – Timo Apr 28 '22 at 12:23
10

It's an ordinary Python list. The exception that you would catch for this is IndexError, but you're better off just checking the length instead.

if len(sys.argv) >= 2:
  startingpoint = sys.argv[1]
else:
  startingpoint = 'blah'
Richard Barrell
  • 4,361
  • 2
  • 22
  • 16
3

Pretty close to what the originator was trying to do. Here is a function I use:

def get_arg(index):
    try:
        sys.argv[index]
    except IndexError:
        return ''
    else:
        return sys.argv[index]

So a usage would be something like:

if __name__ == "__main__":
    banner(get_arg(1),get_arg(2))
Cribber
  • 2,513
  • 2
  • 21
  • 60
J. Barber
  • 31
  • 1
2

A solution working with map built-in fonction !

arg_names = ['command' ,'operation', 'parameter']
args = map(None, arg_names, sys.argv)
args = {k:v for (k,v) in args}

Then you just have to call your parameters like this:

if args['operation'] == "division":
    if not args['parameter']:
        ...
    if args['parameter'] == "euclidian":
        ...
1

You can simply append the value of argv[1] to argv and then check if argv[1] doesn't equal the string you inputted Example:

from sys import argv
argv.append('SomeString')
if argv[1]!="SomeString":
            print(argv[1])
  • 1
    for those who down voted me, shame on you, for not clarifying the mistake. – Hassan Abdul-Kareem Jun 27 '16 at 20:26
  • I guess that's because it's hackish. What if someone pass SomeString as an argument? How to use that if you can accept lets say 1 to 5 arguments? – pbogut Jul 24 '16 at 12:42
  • Cool (but still hackish) for setting a default value in case the user does not supply the argument. Just append and then use argv[1]. – Felizett Jan 11 '17 at 17:25