حجج سطر الأوامر في بايثون

نبذة

نظرًا لكون لغة Python لغة برمجة شائعة جدًا، فضلاً عن دعمها لمعظم أنظمة التشغيل والعديد من المكتبات التي تجعل معالجة وسيطات سطر الأوامر أمرًا سهلاً، فقد أصبحت تُستخدم على نطاق واسع لإنشاء أدوات سطر الأوامر للعديد من الأغراض. يمكن أن تتراوح هذه الأدوات من تطبيقات CLI البسيطة إلى التطبيقات الأكثر تعقيدًا، مثل AWS أوسكلي الأداة.

عادةً ما يتم التحكم في الأدوات المعقدة مثل هذه بواسطة المستخدم عبر وسائط سطر الأوامر، والذي يسمح للمستخدم باستخدام أوامر محددة، وتعيين الخيارات، والمزيد. على سبيل المثال، يمكن لهذه الخيارات أن تطلب من الأداة إخراج معلومات إضافية، أو قراءة البيانات من مصدر محدد، أو إرسال المخرجات إلى موقع معين.

بشكل عام، يتم تمرير الوسائط إلى أدوات واجهة سطر الأوامر (CLI) بشكل مختلف، اعتمادًا على نظام التشغيل الخاص بك:

  • يونكس مثل: - يليه حرف مثل -hالطرق أو -- تليها كلمة مثل --help
  • نوافذ: / يليه إما حرف، أو كلمة، مثل /help

توجد هذه الأساليب المختلفة لأسباب تاريخية. تدعم العديد من البرامج الموجودة على الأنظمة المشابهة لنظام Unix كلاً من تدوين الشرطة المفردة والمزدوجة. يتم استخدام تدوين الشرطة المفردة في الغالب مع خيارات الحرف المفرد، بينما تقدم الشرطات المزدوجة قائمة خيارات أكثر قابلية للقراءة، وهو أمر مفيد بشكل خاص للخيارات المعقدة التي تحتاج إلى أن تكون أكثر وضوحًا.

ملاحظات: في هذه المقالة سنركز فقط على التنسيق المشابه لنظام Unix - و --.

ضع في اعتبارك أن اسم ومعنى الوسيطة خاصان بالبرنامج - لا يوجد تعريف عام، بخلاف بعض الاتفاقيات الشائعة مثل --help لمزيد من المعلومات حول استخدام الأداة. باعتبارك مطور برنامج Python النصي، ستقرر أي الحجج ستقدمها للمتصل وما سيفعله. وهذا يتطلب التقييم السليم.

مع نمو قائمة الوسائط المتاحة لديك، ستصبح التعليمات البرمجية الخاصة بك أكثر تعقيدًا في محاولة تحليلها بدقة. لحسن الحظ، يوجد في بايثون عدد من المكتبات المتاحة لمساعدتك في ذلك. سنغطي بعضًا من الحلول الأكثر شيوعًا، والتي تتراوح بين "افعل ذلك بنفسك" باستخدام sys.argv، إلى نهج "تم إنجازه من أجلك" مع argparse.

التعامل مع وسيطات سطر الأوامر مع بايثون

يدعم إصدار Python 3+ والنظام البيئي المحيط به عددًا من الطرق المختلفة للتعامل مع وسيطات سطر الأوامر. هناك كثير المكتبات التي تسهل تحليل وسيطات سطر الأوامر.

الطريقة المضمنة هي استخدام sys وحدة. من حيث الأسماء واستخدامها فهي تتعلق مباشرة بمكتبة C (libc).

الطريقة الثانية هي getopt الوحدة، التي تتعامل مع الخيارات القصيرة والطويلة، بما في ذلك تقييم قيم المعلمات.

وحدة argparse، وهو مشتق من optparse الوحدة (متوفرة حتى Python 2.7).

docopt الوحدة النمطية، وهي متاح على جيثب، كما يسمح بنفس الوظيفة.

في الآونة الأخيرة، absl اكتسبت المكتبة أيضًا قوة، كوسيلة للاستبدال optparse و getopt().

كل من هذه الطرق لها إيجابياتها وسلبياتها، لذا من المفيد تقييم كل منها لمعرفة أيها يناسب احتياجاتك بشكل أفضل.

وحدة النظام

هذه وحدة أساسية تم شحنها مع بايثون منذ الأيام الأولى. يتطلب الأمر نهجًا مشابهًا جدًا لاستخدام مكتبة C argc/argv للوصول إلى الحجج. ال وحدة النظام ينفذ وسيطات سطر الأوامر في بنية قائمة بسيطة تسمى sys.argv.

يمثل كل عنصر في القائمة وسيطة واحدة. العنصر الأول في القائمة، sys.argv[0]، هو اسم البرنامج النصي بايثون. باقي عناصر القائمة sys.argv[1] إلى sys.argv[n]، هي وسيطات سطر الأوامر من 2 إلى n.

كمحدد بين الوسائط، يتم استخدام مسافة. يجب أن تكون قيم الوسيطة التي تحتوي على مسافة محاطة بعلامات اقتباس حتى يتم تحليلها بشكل صحيح sys.

أي ما يعادل argc هو مجرد عدد العناصر في القائمة. للحصول على هذه القيمة، استخدم Python len() المشغل أو العامل. سنعرض ذلك في مثال التعليمات البرمجية لاحقًا.

طباعة وسيطة CLI الأولى

في هذا المثال الأول، سيحدد البرنامج النصي الطريقة التي تم استدعاؤه بها. يتم الاحتفاظ بهذه المعلومات في وسيطة سطر الأوامر الأولى، المفهرسة بالرقم 0. يوضح الكود أدناه كيفية حصولك على اسم برنامج Python النصي الخاص بك:

import sys

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

احفظ هذا الكود في ملف اسمه arguments-program-name.py، ثم قم بتسميته كما هو موضح أدناه. الإخراج كما يلي ويحتوي على اسم الملف، بما في ذلك مساره الكامل:

$ 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

كما ترون من الاستدعاء الثاني أعلاه، فإننا لا نحصل على اسم ملف Python فحسب، بل نحصل أيضًا على المسار الكامل المستخدم لاستدعائه.

حساب عدد الحجج

في هذا المثال الثاني، نقوم ببساطة بحساب عدد وسيطات سطر الأوامر باستخدام الملف المدمج len() الأسلوب. sys.argv هي القائمة التي يتعين علينا فحصها. في الكود أدناه، نحصل على عدد الوسائط ثم نطرح 1 لأن إحدى هذه الوسائط (أي الوسيطة الأولى) يتم تعيينها دائمًا كاسم للملف، وهو ما لا يكون مفيدًا لنا دائمًا. وبالتالي، فإن العدد الفعلي للوسائط التي مررها المستخدم هو len(sys.argv) - 1:

import sys


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

احفظ هذا الملف وقم بتسميته وسيطات-count.py. بعض الأمثلة على استدعاء هذا البرنامج النصي موضحة أدناه. يتضمن ذلك ثلاثة سيناريوهات مختلفة:

  • مكالمة بدون أي وسيطات سطر أوامر أخرى
  • دعوة ذات حجتين
  • استدعاء يحتوي على وسيطتين، حيث تكون الوسيطة الثانية عبارة عن سلسلة نصية مقتبسة تحتوي على مسافة
$ 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
التكرار من خلال الحجج

يقوم المثال الثالث بإخراج كل وسيطة تم إرسالها إلى برنامج Python النصي، باستثناء اسم البرنامج نفسه. لذلك، نقوم بالتكرار خلال وسيطات سطر الأوامر بدءًا من ثان عنصر القائمة. تذكر أن هذا هو الفهرس 1 نظرًا لأن القوائم تعتمد على 0 في Python:

import sys


arguments = len(sys.argv) - 1


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

أدناه نسمي الكود الخاص بنا، والذي تم حفظه في الملفarguments-output.py. كما حدث مع مثالنا السابق، يوضح الناتج ثلاثة استدعاءات مختلفة:

  • دعوة دون أي حجج
  • دعوة ذات حجتين
  • استدعاء ذو ​​وسيطتين، حيث تكون الوسيطة الثانية عبارة عن سلسلة مقتبسة تحتوي على مسافة
$ 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

تذكر أن الهدف من عرض مثال السلسلة المقتبسة هو أن المعلمات عادة ما تكون محددة بمسافة، ما لم إنهم محاطون بالاقتباسات.

أعلام أبسيل (absl)

تهدف مكتبة Abseil's Flags إلى جلب وسائط سطر الأوامر إلى الإنتاج، مع وسائط سطر الأوامر الموزعة. عندما تستخدم الوحدة إشارات سطر الأوامر، ويتم استيرادها إلى وحدة أخرى - الوحدة الأخرى تستورد الأعلام كذلكويمكن معالجتها عن طريق إعادة توجيهها إلى الوحدة المستوردة.

وهذا يجعل وسيطات سطر الأوامر المعقدة المشتركة بين الوحدات أسهل وأقل تفصيلاً.

بالإضافة إلى ذلك، تتيح لك المكتبة تحديد القيم الافتراضية للوسيطات وأوصافها ونوع بياناتها، لذلك لا يلزم إجراء عمليات فحص وتحويلات إضافية.

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}!")

أنواع البيانات المدعومة هي:

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

إضافة إلى DEFINE_multi_integer(), DEFINE_multi_string() و DEFINE_multi_enum() لإدخال الوسائط المتعددة. بالإضافة إلى ذلك، تشغيل --help, --helpfullإلخ. طباعة الأعلام الموجودة وأوصافها بتنسيقات مختلفة.

تحقق من دليلنا العملي العملي لتعلم Git ، مع أفضل الممارسات ، والمعايير المقبولة في الصناعة ، وورقة الغش المضمنة. توقف عن أوامر Googling Git وفي الواقع تعلم ذلك!

تتيح لك المكتبة أيضًا تحديد عمليات التحقق من الصحة - سواء من حيث النطاق، مثل القيم المستندة إلى الأعداد الصحيحة التي تحتوي على upper_bound or lower_bound هذا مقبول، ويتم تشغيل أساليب عشوائية للتحقق من القيم:

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

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

جمع هذه في مثال ملموس:

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
...

وحدة argparse

وحدة argparse أصبح متاحًا منذ إصدار Python 3.2، وتحسينًا لـ optparse الوحدة الموجودة حتى Python 2.7. تحتوي وثائق Python على وصف لواجهة برمجة التطبيقات (API) وبرنامج تعليمي يغطي جميع الطرق بالتفصيل.

توفر الوحدة واجهة سطر أوامر بمخرجات موحدة، في حين أن الحلين السابقين يتركان الكثير من العمل بين يديك. argparse يسمح بالتحقق من الوسائط الثابتة والاختيارية، مع التحقق من الاسم إما بنمط قصير أو طويل. كوسيطة اختيارية افتراضية، فهي تتضمن -h، إلى جانب نسخته الطويلة --help. تكون هذه الوسيطة مصحوبة برسالة تعليمات افتراضية تصف الوسائط المقبولة.

يعرض الكود أدناه تهيئة المحلل اللغوي، ويوضح الإخراج أدناه المكالمة الأساسية، متبوعة برسالة المساعدة. على عكس استدعاءات بايثون التي استخدمناها في الأمثلة السابقة، ضع في اعتبارك استخدام بايثون 3 مع هذه الأمثلة:


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

في الخطوة التالية، سنضيف وصفًا مخصصًا لرسالة المساعدة لمستخدمينا. تتيح تهيئة المحلل اللغوي بهذه الطريقة نصًا إضافيًا. يخزن الكود أدناه الوصف في ملف text المتغير، والذي يُعطى صراحةً لـ argparse الطبقة كما description معامل. باستدعاء هذا الكود أدناه، يمكنك رؤية الشكل الناتج:


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

كخطوة أخيرة، سنضيف وسيطة اختيارية مسماة -V، والتي تحتوي على وسيطة نمط طويل مقابلة مسماة --version. للقيام بذلك نستخدم الطريقة add_argument() التي نسميها بثلاث معلمات (يتم عرضها لـ --version، فقط):

  • اسم المعامل: --version
  • نص المساعدة للمعلمة: help="show program version"
  • الإجراء (بدون قيمة إضافية): action="store_true"

يتم عرض الكود المصدري لذلك أدناه. قراءة الوسائط في المتغير المسمى args يتم عبر parse_args() طريقة من parser هدف. لاحظ أنك ترسل كلا من النسخة القصيرة والطويلة في مكالمة واحدة. وأخيرا، عليك التحقق مما إذا كانت السمات args.V or args.version يتم ضبطها وإخراج رسالة الإصدار:


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

--version لا تتطلب الوسيطة إعطاء قيمة في سطر الأوامر. لهذا السبب قمنا بتعيين حجة العمل على "store_true". وفي حالات أخرى، قد تحتاج إلى قيمة إضافية معينة، على سبيل المثال، إذا حددت حجمًا أو ارتفاعًا أو عرضًا معينًا. ويظهر هذا في المثال التالي. كحالة افتراضية، يرجى ملاحظة أنه يتم تفسير جميع الوسائط كسلاسل:


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)

نعرض هنا ما يحدث عند إرسال قيم وسيطات مختلفة. يتضمن ذلك الإصدارين القصير والطويل، بالإضافة إلى رسالة المساعدة:

$ 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

وحدة getopt

كما لاحظتم من قبل، فإن sys تقوم الوحدة النمطية بتقسيم سلسلة سطر الأوامر إلى جوانب فردية فقط. بايثون وحدة getopt يذهب أبعد قليلاً ويوسع فصل سلسلة الإدخال عن طريق التحقق من صحة المعلمة. على أساس getopt C، فهي تتيح خيارات قصيرة وطويلة، بما في ذلك تعيين القيمة.

ومن الناحية العملية، فإنه يتطلب sys وحدة لمعالجة بيانات الإدخال بشكل صحيح. للقيام بذلك، كل من sys الوحدة النمطية و getopt يجب تحميل الوحدة مسبقًا. بعد ذلك، من قائمة معلمات الإدخال، نقوم بإزالة عنصر القائمة الأول (انظر الكود أدناه)، وتخزين القائمة المتبقية من وسائط سطر الأوامر في المتغير المسمى argument_list:


import getopt, sys


full_cmd_arguments = sys.argv


argument_list = full_cmd_arguments[1:]

print argument_list

الحجج في argument_list يمكن الآن تحليلها باستخدام getopts() طريقة. ولكن قبل أن نفعل ذلك، علينا أن نقول getopts() حول المعلمات الصالحة. يتم تعريفها على النحو التالي:

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

وهذا يعني أن هذه الحجج هي التي نعتبرها صالحة، إلى جانب بعض المعلومات الإضافية:

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

ربما لاحظت أن ملف o تم تنفيذ الخيار القصير بواسطة القولون، :. هذا يروي getopt أنه يجب تعيين قيمة لهذا الخيار.

يتيح لنا هذا الآن معالجة قائمة الوسائط. ال getopt() تتطلب الطريقة تكوين ثلاث معلمات - قائمة الوسائط الفعلية من argv، بالإضافة إلى الخيارين القصير والطويل الصالحين (الموضحين في مقتطف الشفرة السابق).

يتم الاحتفاظ باستدعاء الأسلوب نفسه في بيان محاولة الالتقاط لتغطية الأخطاء أثناء التقييم. يتم ظهور استثناء إذا تم اكتشاف وسيطة ليست جزءًا من القائمة كما تم تعريفها من قبل. سيقوم برنامج Python النصي بطباعة رسالة الخطأ على الشاشة، ويخرج برمز الخطأ 2:

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

وأخيرًا، يتم تخزين الوسيطات ذات القيم المقابلة في المتغيرين المذكورين arguments و values. الآن، يمكنك بسهولة تقييم هذه المتغيرات في التعليمات البرمجية الخاصة بك. يمكننا استخدام أ for-حلقة للتكرار من خلال قائمة الوسائط المعترف بها، إدخال واحد بعد الآخر.


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))

أدناه يمكنك رؤية الإخراج من تنفيذ هذا الرمز. سنوضح كيف يتفاعل البرنامج مع كل من وسيطات البرنامج الصالحة وغير الصالحة:

$ 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

قد يبدو النداء الأخير لبرنامجنا مربكًا بعض الشيء في البداية. لفهم ذلك، عليك أن تعرف أنه يمكن استخدام خيارات الاختصار (التي تسمى أحيانًا الأعلام) مع شرطة واحدة. يتيح ذلك لأداتك قبول العديد من الخيارات بسهولة أكبر. على سبيل المثال، الدعوة python arguments-getopt.py -vh هو نفس الدعوة python arguments-getopt.py -v -h. لذلك في المكالمة الأخيرة أعلاه، getopt اعتقدت الوحدة أن المستخدم كان يحاول المرور -e كخيار، وهو غير صالح.

وفي الختام

لقد أظهرنا في هذه المقالة العديد من الطرق المختلفة لاسترداد وسيطات سطر الأوامر في بايثون، بما في ذلك استخدام sys, getoptو argparse. تختلف هذه الوحدات في وظائفها، حيث يوفر بعضها أكثر بكثير من غيرها. sys مرنة تماما، في حين أن كليهما getoptو argparse تتطلب بعض الهيكل. وفي المقابل، فإنها تغطي معظم الأعمال المعقدة التي sys يترك الأمر لك. بعد العمل على الأمثلة المقدمة، يجب أن تكون قادرًا على تحديد الوحدة التي تناسب مشروعك بشكل أفضل.

في هذه المقالة لم نتحدث عن حلول أخرى مثل docopts الوحدة النمطية، ذكرنا ذلك للتو. تتبع هذه الوحدة نهجًا مختلفًا تمامًا، وسيتم شرحها بالتفصيل في إحدى المقالات التالية.

مراجع حسابات

الطابع الزمني:

اكثر من ستاكابوز