Đối số dòng lệnh trong Python

Giới thiệu chung

Với Python là ngôn ngữ lập trình rất phổ biến, cũng như hỗ trợ hầu hết các hệ điều hành và nhiều thư viện giúp xử lý đối số dòng lệnh dễ dàng – nó được sử dụng rộng rãi để tạo các công cụ dòng lệnh cho nhiều mục đích. Những công cụ này có thể bao gồm từ các ứng dụng CLI đơn giản đến những ứng dụng phức tạp hơn, như AWS' awscli công cụ.

Các công cụ phức tạp như thế này thường được người dùng điều khiển thông qua đối số dòng lệnh, cho phép người dùng sử dụng các lệnh cụ thể, đặt tùy chọn, v.v. Ví dụ: các tùy chọn này có thể yêu cầu công cụ xuất thông tin bổ sung, đọc dữ liệu từ một nguồn được chỉ định hoặc gửi đầu ra đến một vị trí nhất định.

Nói chung, các đối số được chuyển đến các công cụ CLI khác nhau, tùy thuộc vào hệ điều hành của bạn:

  • Giống Unix: - theo sau là một lá thư, như -h, hoặc là -- theo sau là một từ, như --help
  • Windows: / theo sau là một chữ cái hoặc một từ, như /help

Những cách tiếp cận khác nhau tồn tại vì lý do lịch sử. Nhiều chương trình trên hệ thống tương tự Unix hỗ trợ cả ký hiệu dấu gạch ngang đơn và dấu gạch ngang kép. Ký hiệu dấu gạch ngang đơn chủ yếu được sử dụng với các tùy chọn chữ cái đơn, trong khi dấu gạch ngang kép trình bày danh sách tùy chọn dễ đọc hơn, đặc biệt hữu ích cho các tùy chọn phức tạp cần rõ ràng hơn.

Chú thích: Trong bài viết này chúng tôi sẽ chỉ tập trung vào định dạng giống Unix của ---.

Hãy nhớ rằng cả tên và ý nghĩa của đối số đều dành riêng cho một chương trình – không có định nghĩa chung nào ngoài một số quy ước phổ biến như --help để biết thêm thông tin về cách sử dụng công cụ. Với tư cách là nhà phát triển tập lệnh Python, bạn sẽ quyết định cung cấp đối số nào cho người gọi và những gì họ làm. Điều này đòi hỏi sự đánh giá thích hợp.

Khi danh sách các đối số có sẵn của bạn tăng lên, mã của bạn sẽ trở nên phức tạp hơn khi cố gắng phân tích chúng một cách chính xác. May mắn thay, trong Python có sẵn một số thư viện để giúp bạn thực hiện việc này. Chúng tôi sẽ đề cập đến một số giải pháp phổ biến nhất, từ “tự làm” đến sys.argv, theo cách tiếp cận “làm cho bạn” với argparse.

Xử lý đối số dòng lệnh bằng Python

Python 3+ và hệ sinh thái xung quanh hỗ trợ một số cách xử lý đối số dòng lệnh khác nhau. Có nhiều các thư viện hỗ trợ phân tích cú pháp các đối số dòng lệnh.

Cách tích hợp là sử dụng sys mô-đun. Về mặt tên và cách sử dụng, nó liên quan trực tiếp đến thư viện C (libc).

Cách thứ hai là getopt mô-đun, xử lý cả tùy chọn ngắn và dài, bao gồm cả việc đánh giá các giá trị tham số.

Sản phẩm mô-đun argparse, bắt nguồn từ optparse mô-đun (có sẵn cho Python 2.7).

Sản phẩm docopt mô-đun, đó là có sẵn trên GitHub, cũng cho phép chức năng tương tự.

Gần đây, absl thư viện cũng đang dần phát triển, như một phương tiện để thay thế optparsegetopt().

Mỗi cách đều có ưu và nhược điểm, vì vậy bạn nên đánh giá từng cách để xem cách nào phù hợp nhất với nhu cầu của mình.

Mô-đun hệ thống

Đây là mô-đun cơ bản đã được cung cấp cùng với Python từ những ngày đầu. Nó có cách tiếp cận rất giống với thư viện C bằng cách sử dụng argc/argv để truy cập các đối số. Các mô-đun hệ thống thực hiện các đối số dòng lệnh trong cấu trúc danh sách đơn giản có tên sys.argv.

Mỗi phần tử danh sách đại diện cho một đối số duy nhất. Mục đầu tiên trong danh sách, sys.argv[0], là tên của tập lệnh Python. Các phần tử còn lại của danh sách, sys.argv[1] đến sys.argv[n], là các đối số dòng lệnh từ 2 đến n.

Là dấu phân cách giữa các đối số, một khoảng trắng được sử dụng. Các giá trị đối số chứa khoảng trắng phải được bao quanh bởi dấu ngoặc kép để được phân tích cú pháp chính xác bởi sys.

Tương đương với argc chỉ là số phần tử trong danh sách. Để có được giá trị này, hãy sử dụng Python len() nhà điều hành. Chúng tôi sẽ hiển thị điều này trong một ví dụ về mã sau này.

In đối số CLI đầu tiên

Trong ví dụ đầu tiên này, tập lệnh của chúng tôi sẽ xác định cách nó được gọi. Thông tin này được lưu giữ trong đối số dòng lệnh đầu tiên, được lập chỉ mục bằng 0. Đoạn mã bên dưới cho biết cách bạn lấy tên tập lệnh Python của mình:

import sys

print("The script has the name %s" % (sys.argv[0])

Lưu mã này vào một tệp có tên arguments-program-name.py, và sau đó gọi nó như hiển thị bên dưới. Đầu ra như sau và chứa tên tệp, bao gồm cả đường dẫn đầy đủ của nó:

$ python arguments-program-name.py
The script has the name arguments-program-name.py
$ python /home/user/arguments-program-name.py
The script has the name /home/user/arguments-program-name.py

Như bạn có thể thấy từ lệnh gọi thứ hai ở trên, chúng ta không chỉ lấy được tên của tệp Python mà còn lấy được đường dẫn đầy đủ được sử dụng để gọi nó.

Đếm số lượng đối số

Trong ví dụ thứ hai này, chúng ta chỉ cần đếm số lượng đối số dòng lệnh bằng cách sử dụng hàm tích hợp len() phương pháp. sys.argv là danh sách mà chúng ta phải kiểm tra. Trong mã bên dưới, chúng ta lấy số lượng đối số rồi trừ đi 1 vì một trong các đối số đó (tức là đối số đầu tiên) luôn được đặt làm tên của tệp, điều này không phải lúc nào cũng hữu ích đối với chúng ta. Do đó, số lượng đối số thực tế được người dùng truyền vào là len(sys.argv) - 1:

import sys


arguments = len(sys.argv) - 1
print ("The script is called with %i arguments" % (arguments))

Lưu và đặt tên tệp này là đối số-count.py. Một số ví dụ về cách gọi tập lệnh này được hiển thị bên dưới. Điều này bao gồm ba kịch bản khác nhau:

  • Cuộc gọi không có thêm bất kỳ đối số dòng lệnh nào
  • Cuộc gọi có hai đối số
  • Cuộc gọi có hai đối số, trong đó đối số thứ hai là một chuỗi trích dẫn chứa khoảng trắng
$ python arguments-count.py
The script is called with 0 arguments
$ python arguments-count.py --help me
The script is called with 2 arguments
$ python arguments-count.py --option "long string"
The script is called with 2 arguments
Lặp lại thông qua các đối số

Ví dụ thứ ba của chúng tôi xuất ra mọi đối số được gửi tới tập lệnh Python, ngoại trừ chính tên chương trình. Do đó, chúng tôi lặp qua các đối số dòng lệnh bắt đầu bằng 2 phần tử danh sách. Hãy nhớ lại rằng đây là chỉ mục 1 vì danh sách dựa trên 0 trong Python:

import sys


arguments = len(sys.argv) - 1


position = 1
while (arguments >= position):
    print ("Parameter %i: %s" % (position, sys.argv[position]))
    position = position + 1

Dưới đây chúng tôi gọi mã của mình, mã này đã được lưu vào tệp đối số-output.py. Như đã thực hiện với ví dụ trước của chúng tôi, kết quả đầu ra minh họa ba lệnh gọi khác nhau:

  • Một cuộc gọi không có bất kỳ đối số nào
  • Cuộc gọi có hai đối số
  • Cuộc gọi có hai đối số, trong đó đối số thứ hai là một chuỗi trích dẫn chứa khoảng trắng
$ python arguments-output.py
$ python arguments-output.py --help me
Parameter 1: --help
Parameter 2: me
$ python arguments-output.py --option "long string"
Parameter 1: --option
Parameter 2: long string

Hãy nhớ rằng mục đích hiển thị ví dụ về chuỗi được trích dẫn là các tham số thường được phân cách bằng dấu cách, trừ khi chúng được bao quanh bởi dấu ngoặc kép.

Cờ Abseil (absl)

Thư viện Flags của Abseil nhằm mục đích đưa các đối số dòng lệnh vào sản xuất, với các đối số dòng lệnh được phân phối. Khi một mô-đun sử dụng cờ dòng lệnh và được nhập vào một mô-đun khác – mô-đun khác nhập khẩu cờ là tốtvà có thể xử lý chúng bằng cách chuyển tiếp chúng đến mô-đun đã nhập.

Điều này làm cho các đối số dòng lệnh phức tạp được chia sẻ giữa các mô-đun trở nên dễ dàng hơn và ít dài dòng hơn.

Ngoài ra, thư viện cho phép bạn xác định các giá trị mặc định, mô tả và kiểu dữ liệu của các đối số, do đó không cần phải kiểm tra và chuyển đổi bổ sung.

from absl import flags
import sys


flags.DEFINE_string('name', 'User', 'The name of the user.')


FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"Hello {FLAGS.name}!")

Các loại dữ liệu được hỗ trợ là:

  • DEFINE_integer()
  • DEFINE_string()
  • DEFINE_bool()
  • DEFINE_enum()
  • DEFINE_list()
  • DEFINE_float()

Cũng như DEFINE_multi_integer(), DEFINE_multi_string()DEFINE_multi_enum() cho đầu vào nhiều đối số. Ngoài ra, việc chạy --help, --helpfull, v.v. in các cờ hiện có và mô tả của chúng ở các định dạng khác nhau.

Xem hướng dẫn thực hành, thực tế của chúng tôi để học Git, với các phương pháp hay nhất, các tiêu chuẩn được ngành công nghiệp chấp nhận và bảng lừa đảo đi kèm. Dừng lệnh Googling Git và thực sự học nó!

Thư viện cũng cho phép bạn xác định các xác thực - cả về phạm vi, chẳng hạn như các giá trị dựa trên số nguyên có upper_bound or lower_bound điều đó có thể chấp nhận được và chạy các phương thức tùy ý để kiểm tra các giá trị:

def validate_name(value):
    return len(value) > 15

flags.register_validator('name',
                         validate_name,
                         message='Name is over 15 characters long.',
                         flag_values=FLAGS)

Thu thập những điều này thành một ví dụ cụ thể:

from absl import flags
import sys

flags.DEFINE_string('name', 'User', 'The name of the user.')
flags.DEFINE_integer('tasks', 0, 'The number of tasks a user has.', lower_bound=0)

FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"{FLAGS.name} has {FLAGS.tasks} tasks to work on.")
$ python flags.py --name=John --tasks=5
John has 5 tasks to work on.
$ python flags.py --name=John --tasks=-1

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_flag.py", line 180, in _parse
    return self.parser.parse(argument)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_argument_parser.py", line 168, in parse
    raise ValueError('%s is not %s' % (val, self.syntactic_help))
ValueError: -1 is not a non-negative integer
...

Mô-đun argparse

Sản phẩm mô-đun argparse đã có sẵn kể từ Python 3.2 và là bản nâng cao của optparse mô-đun tồn tại lên tới Python 2.7. Tài liệu Python chứa mô tả API và hướng dẫn bao gồm tất cả các phương thức một cách chi tiết.

Mô-đun này cung cấp giao diện dòng lệnh với đầu ra được tiêu chuẩn hóa, trong khi hai giải pháp trước đó giao phần lớn công việc cho bạn. argparse cho phép xác minh các đối số cố định và tùy chọn, với việc kiểm tra tên theo kiểu ngắn hoặc dài. Là một đối số tùy chọn mặc định, nó bao gồm -h, cùng với phiên bản dài của nó --help. Đối số này đi kèm với thông báo trợ giúp mặc định mô tả các đối số được chấp nhận.

Mã bên dưới hiển thị quá trình khởi tạo trình phân tích cú pháp và đầu ra bên dưới hiển thị lệnh gọi cơ bản, theo sau là thông báo trợ giúp. Ngược lại với các lệnh gọi Python mà chúng tôi đã sử dụng trong các ví dụ trước, hãy nhớ sử dụng Python 3 với các ví dụ sau:


import argparse


parser = argparse.ArgumentParser()
parser.parse_args()
$ python3 arguments-argparse-basic.py 
$ python3 arguments-argparse-basic.py -h
usage: arguments-argparse-basic.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$ python3 arguments-argparse-basic.py --verbose
usage: arguments-argparse-basic.py [-h]
arguments-argparse-basic.py: error: unrecognized arguments: --verbose

Trong bước tiếp theo, chúng tôi sẽ thêm mô tả tùy chỉnh vào thông báo trợ giúp cho người dùng của chúng tôi. Việc khởi tạo trình phân tích cú pháp theo cách này sẽ cho phép có thêm văn bản. Đoạn mã dưới đây lưu trữ mô tả trong text biến, được đưa ra một cách rõ ràng cho argparse lớp học như description tham số. Gọi mã này bên dưới, bạn có thể thấy đầu ra trông như thế nào:


import argparse


text = 'This is a test program. It demonstrates how to use the argparse module with a program description.'


parser = argparse.ArgumentParser(description=text)
parser.parse_args()
$ python3 arguments-argparse-description.py --help
usage: arguments-argparse-description.py [-h]

This is a test program. It demonstrates how to use the argparse module with a
program description.

optional arguments:
  -h, --help  show this help message and exit

Bước cuối cùng chúng ta sẽ thêm một đối số tùy chọn có tên -V, có đối số kiểu dài tương ứng có tên --version. Để làm được điều đó chúng tôi sử dụng phương pháp add_argument() mà chúng tôi gọi với ba tham số (được hiển thị cho --version, chỉ một):

  • Tên của thông số: --version
  • Văn bản trợ giúp cho tham số: help="show program version"
  • Hành động (không có giá trị bổ sung): action="store_true"

Mã nguồn cho điều đó được hiển thị bên dưới. Đọc các đối số vào biến được gọi là args được thực hiện thông qua parse_args() phương pháp từ parser sự vật. Lưu ý rằng bạn gửi cả phiên bản ngắn và dài trong một cuộc gọi. Cuối cùng, bạn kiểm tra xem các thuộc tính args.V or args.version được đặt và xuất thông báo phiên bản:


import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-V", "--version", help="show program version", action="store_true")


args = parser.parse_args()


if args.version:
    print("This is myprogram version 0.1")
$ python3 arguments-argparse-optional.py -V
This is myprogram version 0.1
$ python3 arguments-argparse-optional.py --version
This is myprogram version 0.1

Sản phẩm --version đối số không yêu cầu đưa ra giá trị trên dòng lệnh. Đó là lý do tại sao chúng tôi đặt đối số hành động thành "store_true". Trong các trường hợp khác, bạn có thể cần một giá trị được gán bổ sung, ví dụ: nếu bạn chỉ định âm lượng, chiều cao hoặc chiều rộng nhất định. Điều này được thể hiện trong ví dụ tiếp theo. Là trường hợp mặc định, xin lưu ý rằng tất cả các đối số được hiểu là chuỗi:


import argparse


parser = argparse.ArgumentParser()


parser.add_argument("--width", "-w", help="set output width")


args = parser.parse_args()


if args.width:
    print("Set output width to %s" % args.width)

Ở đây chúng tôi trình bày điều gì sẽ xảy ra khi gửi các giá trị đối số khác nhau. Điều này bao gồm cả phiên bản ngắn và dài, cũng như thông báo trợ giúp:

$ python3 arguments-argparse-optional2.py -w 10
Set output width to 10
$ python3 arguments-argparse-optional2.py --width 10
Set output width to 10
$ python3 arguments-argparse-optional2.py -h
usage: arguments-argparse-optional2.py [-h] [--width WIDTH]

optional arguments:
  -h, --help            show this help message and exit
  --width WIDTH, -w WIDTH
                        set output width

Mô-đun getopt

Như bạn có thể đã nhận thấy trước đây, sys mô-đun chỉ chia chuỗi dòng lệnh thành các khía cạnh duy nhất. con trăn mô-đun getopt đi xa hơn một chút và mở rộng việc phân tách chuỗi đầu vào bằng cách xác thực tham số. Dựa vào getopt C, nó cho phép cả tùy chọn ngắn và dài, bao gồm cả việc gán giá trị.

Trong thực tế, nó đòi hỏi sự sys mô-đun để xử lý dữ liệu đầu vào đúng cách. Để làm được như vậy, cả sys mô-đun và getopt mô-đun phải được tải trước. Tiếp theo, từ danh sách các tham số đầu vào, chúng tôi xóa phần tử danh sách đầu tiên (xem mã bên dưới) và lưu danh sách đối số dòng lệnh còn lại trong biến có tên argument_list:


import getopt, sys


full_cmd_arguments = sys.argv


argument_list = full_cmd_arguments[1:]

print argument_list

Các đối số trong argument_list bây giờ có thể được phân tích bằng cách sử dụng getopts() phương pháp. Nhưng trước khi làm điều đó, chúng ta cần phải nói getopts() về những tham số nào là hợp lệ. Chúng được định nghĩa như thế này:

short_options = "ho:v"
long_options = ["help", "output=", "verbose"]

Điều này có nghĩa là những đối số này là những đối số mà chúng tôi coi là hợp lệ, cùng với một số thông tin bổ sung:

------------------------------------------
long argument   short argument  with value
------------------------------------------
--help           -h              no
--output         -o              yes
--verbose        -v              no
------------------------------------------

Bạn có thể nhận thấy rằng o tùy chọn ngắn được thực hiện bằng dấu hai chấm, :. Điều này nói getopt rằng tùy chọn này nên được gán một giá trị.

Điều này bây giờ cho phép chúng tôi xử lý một danh sách các đối số. Các getopt() phương thức yêu cầu cấu hình ba tham số – danh sách các đối số thực tế từ argv, cũng như cả tùy chọn ngắn và dài hợp lệ (hiển thị trong đoạn mã trước đó).

Bản thân lệnh gọi phương thức được giữ trong câu lệnh try-catch để che các lỗi trong quá trình đánh giá. Một ngoại lệ sẽ được đưa ra nếu một đối số được phát hiện không nằm trong danh sách như được xác định trước đó. Tập lệnh Python sẽ in thông báo lỗi ra màn hình và thoát với mã lỗi 2:

try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    
    print (str(err))
    sys.exit(2)

Cuối cùng, các đối số có giá trị tương ứng được lưu trữ trong hai biến có tên argumentsvalues. Bây giờ, bạn có thể dễ dàng đánh giá các biến này trong mã của mình. Chúng ta có thể sử dụng một for-loop để lặp qua danh sách các đối số được nhận dạng, hết mục này đến mục khác.


for current_argument, current_value in arguments:
    if current_argument in ("-v", "--verbose"):
        print ("Enabling verbose mode")
    elif current_argument in ("-h", "--help"):
        print ("Displaying help")
    elif current_argument in ("-o", "--output"):
        print (("Enabling special output mode (%s)") % (current_value))

Dưới đây bạn có thể thấy kết quả từ việc thực thi mã này. Chúng tôi sẽ chỉ ra cách chương trình phản ứng với cả đối số chương trình hợp lệ và không hợp lệ:

$ python arguments-getopt.py -h
Displaying help
$ python arguments-getopt.py --help
Displaying help
$ python arguments-getopt.py --output=green --help -v
Enabling special output mode (green)
Displaying help
Enabling verbose mode
$ python arguments-getopt.py -verbose
option -e not recognized

Lệnh gọi cuối cùng đến chương trình của chúng ta ban đầu có vẻ hơi khó hiểu. Để hiểu nó, bạn cần biết rằng các tùy chọn tốc ký (đôi khi còn được gọi là cờ) có thể được sử dụng cùng với một dấu gạch ngang. Điều này cho phép công cụ của bạn dễ dàng chấp nhận nhiều tùy chọn hơn. Ví dụ, gọi python arguments-getopt.py -vh cũng giống như việc gọi python arguments-getopt.py -v -h. Vì vậy, trong lệnh gọi cuối cùng ở trên, getopt mô-đun nghĩ rằng người dùng đang cố gắng vượt qua -e như một tùy chọn, không hợp lệ.

Kết luận

Trong bài viết này, chúng tôi đã trình bày nhiều phương pháp khác nhau để truy xuất các đối số dòng lệnh trong Python, bao gồm cả việc sử dụng sys, getoptargparse. Các mô-đun này khác nhau về chức năng, một số cung cấp nhiều hơn những mô-đun khác. sys hoàn toàn linh hoạt, trong khi cả hai getoptargparse yêu cầu một số cấu trúc. Ngược lại, chúng bao gồm hầu hết các công việc phức tạp sys tùy thuộc vào bạn. Sau khi xem qua các ví dụ được cung cấp, bạn sẽ có thể xác định mô-đun nào phù hợp nhất với dự án của mình.

Trong bài viết này chúng tôi không nói về các giải pháp khác như docopts module, chúng tôi vừa đề cập đến nó. Mô-đun này tuân theo một cách tiếp cận hoàn toàn khác và sẽ được giải thích chi tiết ở một trong các bài viết tiếp theo.

dự án

Dấu thời gian:

Thêm từ xếp chồng lên nhau