Processing command-line arguments in prefix notation in Python

I'm trying to parse a command-line in Python which looks like the following:

$ ./command -o option1 arg1 -o option2 arg2 arg3

In other words, the command takes an unlimited number of arguments, and each argument may optionally be preceded with an -o option, which relates specifically to that argument. I think this is called a "prefix notation".

In the Bourne shell I would do something like the following:

while test -n "$1"
    if test "$1" = '-o'
        shift 2
    # Work with $1 (the argument) and $option (the option)
    # ...

Looking around at the Bash tutorials, etc. this seems to be the accepted idiom, so I'm guessing Bash is optimized to work with command-line arguments this way.

Trying to implement this pattern in Python, my first guess was to use pop(), as this is basically a stack operation. But I'm guessing this won't work as well on Python because the list of arguments in sys.argv is in the wrong order and would have to be processed like a queue (i.e. pop from the left). I've read that lists are not optimized for use as queues in Python.

So, my ideas are: convert argv to a collections.deque and use popleft(), reverse argv using reverse() and use pop(), or maybe just work with the int list indices themselves.

Does anyone know of a better way to do this, otherwise which of my ideas would be best-practise in Python?

Asked by: Ryan541 | Posted: 30-11-2021

Answer 1

You can do


which pulls off the first element and returns it. That's probably inefficient, though. Maybe. I'm not sure how argv is implemented under the hood. (Then again, if efficiency is that important, why are you using Python?)

A more Pythonic solution, though, would be to iterate through the list without popping the elements. Like so:

o_flag = False
for a in argv:
    if a == '-o':
        o_flag = True
    # do whatever
    o_flag = False

Also, I think the optparse module deserves a mention; it's pretty standard for handling options and arguments in Python programs, although it might be overkill for this task since you already have several perfectly functional solutions.

Answered by: Vanessa803 | Posted: 01-01-2022

Answer 2

A Python functional equivalent of "shift" in Bourne/Bash can be had by importing sys, then assigning sys.argv[1:] to a variable, e.g. "args". Then you can use args = args[1:] to shift left once, or use a higher number to shift multiple times. The argument index will start from 0, rather than 1. The example above could look look like:

import sys
args = sys.argv[1:]
while len(args):
    if args[0] == '-o':
        option = args[1]
        args = args[2:] # shift 2
    # Work with args[0] (the argument) and option (the option)
    # ...
    args = args[1:] # shift

Answered by: Blake637 | Posted: 01-01-2022

Answer 3

another stdlib module: argparse

p = argparse.ArgumentParser()
p.add_argument('-o', action='append')
for i in range(1, 4): p.add_argument('arg%d' % i)
args = p.parse_args('-o option1 arg1 -o option2 arg2 arg3'.split())
print args
# -> Namespace(arg1='arg1', arg2='arg2', arg3='arg3', o=['option1', 'option2'])

Answered by: Edgar483 | Posted: 01-01-2022

Answer 4

Something like:

for arg in sys.argv[1:]:
  # do something with arg

should work well for you. Unless you are expecting an extremely large number of arguments, I would go for whatever code is most simple (and not worry too much about performance). The argv[1:] ignores that first argv value, which will be the name of the script.

Answered by: Dexter126 | Posted: 01-01-2022

Answer 5

No need to reinvent the wheel: the getopt module is designed for exactly this. If that doesn't suit your needs, try the optparse module, which is more flexible but more complicated.

Answered by: Daniel576 | Posted: 01-01-2022

Answer 6

In python del sys.argv[1] does the same operation as shift in bash. In python sys.argv is a list. Deleting the element on index 1 is same as shift in bash.

Below sample python script will help to skip -o in arguments and consider all other arguments.

import sys

if __name__ == '__main__':
    while len(sys.argv) > 1:
        if sys.argv[1] != "-o":
            print sys.argv[1]
        del sys.argv[1]

Answered by: Kevin634 | Posted: 01-01-2022

Similar questions

linux - How to make a python, command-line program autocomplete arbitrary things NOT interpreter

I am aware of how to setup autocompletion of python objects in the python interpreter (on unix). Google shows many hits for explanations on how to do this. Unfortunately, there are so many references to that it is difficult to find what I need to do, which is slightly different. I need to know how to enable, tab/auto completion of arbitrary items in a command-line program written in ...

java - OCSP command-line test tool?

Closed. This question does not meet Stack Overflow guid...

python - How to prompt for user input and read command-line arguments

Closed. This question needs to be more focused. It ...

eclipse - Integrating command-line generated python .coverage files with PyDev

My build environment is configured to compile, run and create coverage file at the command line (using Ned Batchelder tool). I'm using Eclipse with PyDev as my editor, but for practical reasons, it's not possible/convenient for me to convert my whole build environment to Eclipse (and thus generate the coverage data directly from the IDE, as it's designed to do) PyDev seems to be using the same ...

c++ - How do I compile a Visual Studio project from the command-line?

I'm scripting the checkout, build, distribution, test, and commit cycle for a large C++ solution that is using Monotone, CMake, Visual Studio Express 2008, and custom tests. All of the other parts seem pretty straight-forward, but I don't see how to compile the Visu...

How to make a simple command-line chat in Python?

I study network programming and would like to write a simple command-line chat in Python. I'm wondering how make receving constant along with inputing available for sending at any time. As you see, this client can do only one job at a time: from socket import * HOST = 'localhost' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(A...

interpreter - Persistent Python Command-Line History

I'd like to be able to "up-arrow" to commands that I input in a previous Python interpreter. I have found the readline module which offers functions like: read_history_file, write_history_file, and set_startup_hook. I'm not quite savvy enough to put this into practice though, so could someone please help? My thoughts on the solution are: (1) Modify .login PYTHO...

Display constantly updating information in-place in command-line window using python?

I am essentially building a timer. I have a python script that monitors for an event and then prints out the seconds that have elapsed since that event. Instead of an ugly stream of numbers printed to the command line, I would like to display only the current elapsed time "in-place"-- so that only one number is visible at any given time. Is there a simple way to do this? If possible I'd like to u...

python - How to write script output to file and command-line?

I have a long-running Python script that I run from the command-line. The script writes progress messages and results to the standard output. I want to capture everything the script write to the standard output in a file, but also see it on the command line. Alternatively, I want the output to go to the file immediately, so I can use tail to view the progress. I have tried this: python MyLongRu...

python - Linux command-line call not returning what it should from os.system?

I need to make some command line calls to linux and get the return from this, however doing it as below is just returning 0 when it should return a time value, like 00:08:19, I am testing the exact same call in regular command line and it returns the time value 00:08:19 so I am confused as to what I am doing wrong as I thought this was how to do it in python. import os...

Still can't find your answer? Check out these communities...

PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python