วิธีอ่าน / ประมวลผลอาร์กิวเมนต์บรรทัดคำสั่ง?


623

5
ใช้ docopt (ดูคำตอบของ @ ralbatross ที่stackoverflow.com/a/14790373/116891 ) ฉันลองมาแล้วและ docopt เป็นทางเดียวที่ฉันจะใช้ในอนาคต
Pat

2
ฉันไม่คิดว่าจะมีวิธีเดียวที่ดีที่สุด argparse เป็นมาตรฐานและมีคุณลักษณะ docopt นั้นสง่างามมาก แต่ไม่ได้อยู่ในห้องสมุดมาตรฐาน สำหรับการใช้งานที่มีน้ำหนักเบาง่ายมากที่คุณสามารถทำให้ค่าเริ่มต้นฟังก์ชั่นการจัดการค่าเริ่มต้นอาร์กิวเมนต์บรรทัด COMAND สำหรับคุณ
Simon Hibbs

คำตอบ:


456

วิธีการแก้ปัญหาที่ยอมรับในห้องสมุดมาตรฐานคือargparse( เอกสาร ):

นี่คือตัวอย่าง:

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
                    help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
                    action="store_false", dest="verbose", default=True,
                    help="don't print status messages to stdout")

args = parser.parse_args()

argparse สนับสนุน (เหนือสิ่งอื่นใด):

  • หลายตัวเลือกในลำดับใดก็ได้
  • ตัวเลือกระยะสั้นและระยะยาว
  • ค่าเริ่มต้น
  • การสร้างข้อความช่วยเหลือการใช้งาน

27
ใช่สิ่งเหล่านี้ดีที่สุด เนื่องจากเป็นส่วนหนึ่งของไลบรารีมาตรฐานคุณจึงมั่นใจได้ว่าจะพร้อมใช้งานและใช้งานง่าย การเลือกใช้เฉพาะอย่างมีประสิทธิภาพและใช้งานง่าย
Barry Wark

4
optparse เป็นหนึ่งในดีที่สุด getopt เก่าและควรได้รับการพิจารณาอย่างจริงจัง
jemfinch

12
ณ จุดนี้ (12/2011) ตอนนี้ argparse ถือเป็นตัวเลือกที่ดีกว่า optparse ใช่ไหม?
oob

54
Python Documentation แนะนำให้ใช้argparseแทน optparse
earthmeLon

7
เนื่องจากoptparseเลิกใช้แล้วผู้ถามของคำถามจึงไม่ได้เป็นสมาชิกในสแต็คล้นอีกต่อไปและนี่คือคำตอบที่ยอมรับได้สำหรับคำถามที่มองเห็นได้ชัดเจนโปรดพิจารณาการเขียนโค้ดตัวอย่างของคุณใหม่เพื่อใช้ stdlib argparseแทน
Wim

548
import sys

print("\n".join(sys.argv))

sys.argv เป็นรายการที่มีข้อโต้แย้งทั้งหมดที่ส่งผ่านไปยังสคริปต์ในบรรทัดคำสั่ง

โดยทั่วไป

import sys
print(sys.argv[1:])

83
สำหรับสิ่งที่ง่ายจริงๆนี่คือวิธีที่จะไปแม้ว่าคุณอาจต้องการใช้เท่านั้นsys.argv[1:](หลีกเลี่ยงชื่อสคริปต์)
Xiong Chiamiov

128

เพียงแค่ไปรอบ ๆ ประกาศข่าวประเสริฐสำหรับargparseซึ่งจะดีกว่าสำหรับเหล่านี้เหตุผล .. หลัก:

(คัดลอกมาจากลิงค์)

  • โมดูล argparse สามารถจัดการกับอาร์กิวเมนต์ตำแหน่งและตัวเลือกในขณะที่ optparse สามารถจัดการกับอาร์กิวเมนต์ตัวเลือกเพิ่มเติมเท่านั้น

  • argparse ไม่เชื่อฟังเกี่ยวกับสิ่งที่อินเตอร์เฟสบรรทัดคำสั่งของคุณควรมีลักษณะ - ตัวเลือกเช่น -file หรือ / file ได้รับการสนับสนุนเช่นเดียวกับตัวเลือกที่จำเป็น Optparse ปฏิเสธที่จะสนับสนุนคุณสมบัติเหล่านี้ต้องการความบริสุทธิ์มากกว่าการใช้งานจริง

  • argparse สร้างข้อความการใช้งานที่ให้ข้อมูลเพิ่มเติมรวมถึงการใช้บรรทัดคำสั่งที่พิจารณาจากอาร์กิวเมนต์ของคุณและข้อความช่วยเหลือสำหรับทั้งอาร์กิวเมนต์ตำแหน่งและตัวเลือก โมดูล optparse ต้องการให้คุณเขียนสตริงการใช้งานของคุณเองและไม่มีวิธีแสดงความช่วยเหลือเกี่ยวกับข้อโต้แย้งตำแหน่ง

  • argparse สนับสนุนการกระทำที่ใช้จำนวนตัวแปรของบรรทัดคำสั่ง args ขณะที่ optparse ต้องการให้ทราบจำนวนอาร์กิวเมนต์ที่แน่นอน (เช่น 1, 2 หรือ 3) ล่วงหน้า

  • argparse สนับสนุนตัวแยกวิเคราะห์ที่ส่งไปยังคำสั่งย่อยในขณะที่ optparse ต้องการการตั้งค่า allow_interspersed_argsและการส่งตัวแยกวิเคราะห์ด้วยตนเอง

และรายการโปรดส่วนตัวของฉัน:

  • argparse อนุญาตให้ระบุประเภทและพารามิเตอร์การกระทำadd_argument() ด้วย callables ง่าย ๆ ในขณะที่ optparse ต้องการแอตทริบิวต์แฮ็กคลาสเช่น STORE_ACTIONSหรือCHECK_METHODSรับการตรวจสอบอาร์กิวเมนต์ที่เหมาะสม

27
ตอนนี้เป็นส่วนหนึ่งของ Python มาตรฐานตั้งแต่วันที่ 2.7 และ 3.2 :)
jpswain

"อาร์กิวเมนต์ที่ไม่จำเป็น" คืออะไร คุณบอกว่าพวกเขาอยู่ใน optparse ฉันคิดว่าพวกเขาเป็นข้อโต้แย้งที่อาจจะหรืออาจจะไม่ได้ให้ แต่คุณบอกว่าพวกเขาอยู่ใน optparse ในขณะที่จะบอกว่า "optparse ต้องการให้ทราบจำนวนของข้อโต้แย้งที่แน่นอนล่วงหน้า" ดังนั้นคำจำกัดความของคุณของ "อาร์กิวเมนต์ทางเลือก" จึงแตกต่างจากที่ฉันคิดหรือคำตอบของคุณไม่สอดคล้องกับตัวเอง
ArtOfWarfare

1
เพียงแค่ Gripe: เอกสารโต้แย้งก็มีความซับซ้อนและบ้าอย่างบ้าคลั่ง คุณไม่สามารถหาคำตอบง่ายๆสำหรับ "ฉันจะสร้างอาร์กิวเมนต์บรรทัดคำสั่งในค่าเดียวได้อย่างไรและฉันจะเข้าถึงค่านั้นได้อย่างไร" </gripe>
osman

2
@osman กวดวิชาที่อ่อนโยนเกี่ยวกับการโต้แย้งนี้อาจช่วย ...
ชีวิตสมดุล

2
@ArtOfWarfare "อาร์กิวเมนต์ที่เป็นตัวเลือก" ในบริบทนี้น่าจะหมายถึงข้อโต้แย้งที่ระบุด้วยอาร์กิวเมนต์ตัวเลือกเช่น-fหรือ--fooในขณะที่ "ทราบจำนวนที่แน่นอนของข้อโต้แย้งล่วงหน้า" สันนิษฐานว่าเป็นข้อโต้แย้งตำแหน่งที่กำหนดโดยไม่มีธงตัวเลือกก่อนหน้า
mtraceur

67

นอกจากนี้ยังมีargparseโมดูล stdlib ("impovement" ในoptparseโมดูลของ stdlib ) ตัวอย่างจากบทนำสู่ข้อโต้แย้ง :

# script.py
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'integers', metavar='int', type=int, choices=range(10),
         nargs='+', help='an integer in the range 0..9')
    parser.add_argument(
        '--sum', dest='accumulate', action='store_const', const=sum,
        default=max, help='sum the integers (default: find the max)')

    args = parser.parse_args()
    print(args.accumulate(args.integers))

การใช้งาน:

$ script.py 1 2 3 4
4

$ script.py --sum 1 2 3 4
10

1
มันเป็นเพียงแค่การคัดลอกและวาง
blitu12345

3
@ blitu12345 ณ เวลาที่เผยแพร่คำตอบของฉันไม่มีคำตอบอื่นใดที่กล่าวถึงการโต้แย้งในทางใดทางหนึ่ง โมดูลตัวเองไม่ได้อยู่ใน stdlib คุณมีอะไรกับตัวอย่างรหัสจากเอกสารประกอบ? ทำไมคุณคิดว่าจำเป็นต้องมีตัวอย่างของคุณเองแทนที่จะเป็นตัวอย่างจากผู้เขียนโมดูล และฉันไม่ชอบคำตอบแบบลิงก์เท่านั้น (ฉันไม่ใช่คนเดียว)
jfs

1
ผู้คนที่มาที่นี่แล้วมีความคิดว่าอะไรอยู่ในเอกสารประกอบและจะมาที่นี่เพื่อการกวาดล้างเพิ่มเติมเกี่ยวกับหัวข้อเดียวกันเป็นกรณีของฉัน แต่สิ่งที่ฉันพบจริงๆที่นี่คือการคัดลอกและวางจาก docs.Peace ดั้งเดิม!
blitu12345

2
"คนที่มาที่นี่แล้วมีความคิดอะไรอยู่ในเอกสาร" - ฉันสงสัยอย่างมากว่าสมมติฐาน อย่างใด
sjas

49

sys.argvวิธีการหนึ่งที่จะทำคือการใช้ นี่จะพิมพ์ชื่อสคริปต์เป็นอาร์กิวเมนต์แรกและพารามิเตอร์อื่น ๆ ทั้งหมดที่คุณส่งไปให้

import sys

for arg in sys.argv:
    print arg

49

docoptห้องสมุดมันเนียน มันสร้างอาร์กิวเมนต์ dict จากสตริงการใช้งานสำหรับแอปของคุณ

เช่นจาก docopt readme:

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)

4
นี่เป็นวิธีที่ฉันโปรดปรานอย่างรวดเร็ว มันแยกสตริงดังนั้นมันเป็นชนิดของเปราะ แต่ก็เปราะทั้งหมดในที่เดียวและคุณสามารถดูตัวอย่างตรรกะของคุณที่try.docopt.org อาร์กิวเมนต์ที่เป็นทางเลือกและไม่เกิดร่วมกันนั้นทำด้วยวิธีที่สง่างามจริงๆ
gvoysey

4
ฉันหมดหวังที่จะเห็นโค้ดที่เหลือสำหรับ naval_fate.py
John Lawrence Aspden

48

หากคุณต้องการสิ่งที่รวดเร็วและไม่ยืดหยุ่นมาก

main.py:

import sys

first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)

จากนั้นเรียกใช้ python main.py James Smith

เพื่อสร้างผลลัพธ์ต่อไปนี้:

สวัสดีเจมส์สมิ ธ


การใช้ภาษาที่สมจริงมากขึ้นจะเป็นpython main.py "James Smith"ซึ่งทำให้James Smithในsys.argv[1]และผลิตเมื่อคุณพยายามที่จะใช้ดำรงIndexError sys.argv[2]พฤติกรรมการอ้างอิงจะขึ้นอยู่กับแพลตฟอร์มและเชลล์ที่คุณใช้งาน Python
tripleee

10
ฉันไม่เห็นด้วยว่าการใช้งานของฉันนั้นสมจริงน้อยกว่า แกล้งโปรแกรมของคุณจำเป็นต้องรู้ชื่อและนามสกุลของบุคคลเพื่อใช้งานสคริปต์ในธุรกิจที่ผู้คนสามารถมีชื่อและนามสกุลได้หลายชื่อใช่หรือไม่ ถ้าเจมส์สมิ ธ มีโจเซฟเป็นชื่อหรือนามสกุลพิเศษว่าจะแยกแยะความแตกต่างระหว่างว่าโจเซฟเป็นพิเศษชื่อหรือนามสกุลถ้าคุณจะทำอย่างไรpython main.py "James Joseph Smith"? หากคุณมีข้อกังวลเกี่ยวกับการจัดทำดัชนีนอกขอบเขตคุณสามารถเพิ่มการตรวจสอบจำนวนข้อโต้แย้งที่มีให้ ตัวอย่างของฉันเป็นจริงน้อยลงหรือไม่แสดงวิธีจัดการกับอาร์กิวเมนต์หลายตัว
Kent Munthe Caspersen

1
คำตอบอื่น ๆ ทั้งหมดสำหรับการวางแผนภารกิจขึ้นฝั่งดวงจันทร์ gmail-trash-msg.py MessageIDฉันเพียงแค่ใช้ คำตอบนี้ตรงไปตรงมาในการทดสอบพารามิเตอร์ได้รับการผ่านในMessageID sys.argv[1]
WinEunuuchs2Unix


19

ฉันใช้ optparse แต่ชอบทิศทางที่ Simon Willison ใช้กับoptfunc library ที่เพิ่งเปิดตัว มันทำงานโดย:

"การตรวจสอบคำจำกัดความของฟังก์ชัน (รวมถึงอาร์กิวเมนต์และค่าเริ่มต้น) และการใช้เพื่อสร้างเครื่องมือแยกวิเคราะห์บรรทัดคำสั่ง"

ตัวอย่างเช่นนิยามฟังก์ชันนี้:

def geocode(s, api_key='', geocoder='google', list_geocoders=False):

ถูกเปลี่ยนเป็นข้อความช่วยเหลือของ optparse นี้:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER

8

ฉันชอบ getopt จาก stdlib เช่น:

try:
    opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err: 
    usage(err)

for opt, arg in opts:
    if opt in ('-h', '--help'): 
        usage()

if len(args) != 1:
    usage("specify thing...")

เมื่อเร็ว ๆ นี้ฉันได้ห่อบางสิ่งที่คล้ายกันนี้เพื่อทำให้สิ่งที่ verbose น้อยลง (เช่น; ทำให้ "-h" โดยนัย)


8

การคลิกของ Pocoo นั้นใช้งานง่ายกว่านั้นต้องใช้หม้อต้มน้ำน้อยกว่าและอย่างน้อยก็มีประสิทธิภาพเทียบเท่ากับอาร์กิวเมนต์

จุดอ่อนเดียวที่ฉันพบจนถึงตอนนี้คือคุณไม่สามารถปรับแต่งได้มากนักเพื่อช่วยเหลือหน้าต่างๆ แต่โดยทั่วไปนั้นไม่ใช่ข้อกำหนดและdocoptดูเหมือนจะเป็นตัวเลือกที่ชัดเจนเมื่อเป็นเช่นนั้น


7

ดังที่คุณเห็นoptparse "โมดูล optparse เลิกใช้แล้วและจะไม่ถูกพัฒนาต่อไปการพัฒนาจะดำเนินต่อไปด้วยโมดูลargparse "


5
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                   help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                   const=sum, default=max,
                   help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h

Ref-link: https://docs.python.org/3.3/library/argparse.html

4

คุณอาจสนใจโมดูล Python เล็กน้อยที่ฉันเขียนเพื่อจัดการอาร์กิวเมนต์บรรทัดคำสั่งได้ง่ายขึ้น (โอเพ่นซอร์สและอิสระในการใช้) - Commando


1
มีอยู่แล้วโมดูลแยกบรรทัดคำสั่งอีกคนหนึ่งชื่อคอมมานโด: github.com/lakshmivyas/commando มันล้อมรอบการโต้เถียงโดยใช้มัณฑนากร
Roberto Bonvallet

1
หลามและวงล้อประดิษฐ์อีกครั้ง
ดีเร็ก

4

ฉันขอแนะนำให้ดูที่docoptเป็นทางเลือกที่ง่ายแก่ผู้อื่น

docopt เป็นโครงการใหม่ที่ทำงานโดยการแยกวิเคราะห์ข้อความ --help การใช้งานของคุณแทนที่จะกำหนดให้คุณใช้ทุกอย่างด้วยตัวเอง คุณเพียงแค่ใส่ข้อความการใช้งานของคุณในรูปแบบ POSIX


4

แต่ตัวเลือกอื่นโอ๊ะ มันสร้างบน argparse และช่วยให้คุณเขียนสิ่งที่ชอบ:

import argh

# declaring:

def echo(text):
    "Returns given word as is."
    return text

def greet(name, greeting='Hello'):
    "Greets the user with given name. The greeting is customizable."
    return greeting + ', ' + name

# assembling:

parser = argh.ArghParser()
parser.add_commands([echo, greet])

# dispatching:

if __name__ == '__main__':
    parser.dispatch()

มันจะสร้างความช่วยเหลือและอื่น ๆ โดยอัตโนมัติและคุณสามารถใช้มัณฑนากรเพื่อให้คำแนะนำเพิ่มเติมเกี่ยวกับวิธีการแยกวิเคราะห์หาเรื่อง


นี่คือทางออกที่ดีที่สุด ใช้arghง่ายกว่าที่ libs sysอื่นหรือโดยใช้
Juanjo Salvador

ฉันต้องการที่จะชอบarghแต่มันไม่เหมาะอย่างยิ่งสำหรับสถานการณ์ที่ความปรารถนาสูงสุดของคุณไม่ได้มีคำสั่งย่อยที่มีคำสั่งย่อย
tripleee

1
@tripleee YMMV แต่ฉันพบว่านี่เป็นข้อบกพร่องในเอกสารมากกว่าในห้องสมุด ดูเหมือนเป็นไปได้อย่างสมบูรณ์ที่จะdef frobnicate_spleches(...)กำหนดฟังก์ชั่นที่ทำในสิ่งที่สคริปต์ของคุณทำแล้วทำif __name__ == '__main__': argh.dispatch_command(frobnicate_spleches)ที่ส่วนท้ายของไฟล์
หนังสือเวียนทำลาย

0

วิธีการแก้ปัญหาของฉันคือentrypoint2 ตัวอย่าง:

from entrypoint2 import entrypoint
@entrypoint
def add(file, quiet=True): 
    ''' This function writes report.

    :param file: write report to FILE
    :param quiet: don't print status messages to stdout
    '''
    print file,quiet

ข้อความช่วยเหลือ:

usage: report.py [-h] [-q] [--debug] file

This function writes report.

positional arguments:
  file         write report to FILE

optional arguments:
  -h, --help   show this help message and exit
  -q, --quiet  don't print status messages to stdout
  --debug      set logging level to DEBUG
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.