关于python:用-Python-内置库创建一个命令行应用程序

35次阅读

共计 10139 个字符,预计需要花费 26 分钟才能阅读完成。

当创立一个应用程序时,通常心愿可能通知你的应用程序如何做某事。有两种风行的办法来实现这项工作,你能够让应用程序接受命令行参数,或者创立一个图形化的用户接口。有些应用程序两者都反对。

当你须要在服务器上运行你的代码时,命令行接口很有帮忙。大多数服务器没有图形化界面,特地当它们是 Linux 服务器时。在这种状况下,即便你想运行图形用户界面,你也可能无奈运行。

Python 有一个叫做 argparse 的内置库,能够用它来创立一个命令行界面。在这篇文章中,你将理解到以下内容。

  • 解析实参
  • 创立有用的信息
  • 增加别名
  • 应用互相排挤的参数
  • 创立一个简略的搜寻工具

argparse 模块的内容比本文要介绍的多得多。如果你想晓得更多对于它的信息,你能够查看文档。

当初是时候开始从命令行中解析参数了。

解析参数

在学习如何应用 argparse 之前,最好晓得还有一种办法能够向 Python 脚本传递参数。你能够向 Python 脚本传递任何参数,并通过应用 sys 模块拜访这些参数。

为了理解它是如何工作的,创立一个名为 sys_args.py 的文件,并在其中输出以下代码:

# sys_args.py
   import sys
   def main():
       print('You passed the following arguments:')
       print(sys.argv)
   if __name__ == '__main__':
       main()

这段代码导入 sys 并打印出 sys.argv 中的任何内容。argv 属性蕴含了所有传递给脚本的货色的列表,其中第一项是脚本自身。

上面是一个例子,阐明当你运行这段代码和几个参数的时候会产生什么。

$ python3 sys_args.py --s 45
   You passed the following arguments:
   ['sys_args.py', '--s', '45']

应用 sys.argv 的问题是,你无法控制能够传递给你的应用程序的参数。

  • 不能疏忽参数
  • 不能创立默认参数
  • 无奈判断什么是无效参数

这就是为什么应用 argparse 是应用 Python 规范库的办法。argparse 模块是十分弱小和有用的。让咱们想一想,一个命令行应用程序所遵循的常见过程。

pass:传入一个文件

do:在你的程序中对该文件做一些解决

output:输入后果

上面是一个对于如何工作的通用例子。持续创立 file_parser.py 并增加以下代码。

# file_parser.py
   import argparse
   def file_parser(input_file, output_file=''):
       print(f'Processing {input_file}')
       print('Finished processing')
       if output_file:
           print(f'Creating {output_file}')
   def main():
       parser = argparse.ArgumentParser('File parser')
       parser.add_argument('--infile', help='Input file')
       parser.add_argument('--out', help='Output file')
       args = parser.parse_args()
       if args.infile:
           file_parser(args.infile, args.out)
   if __name__ == '__main__':
       main()

file_parser() 函数是进行解析的逻辑所在。在这个例子中,它只接管一个文件名,并将其打印进去。output_file 参数的默认值是一个空字符串。

程序的重点在 main() 中。在这里你创立了一个 argparse.ArgumentParser() 的实例,并给你的解析器起了一个名字。而后你增加两个参数,–infile 和 –out。为了应用这个解析器,你须要调用 parse_args(),它将返回传递给你的程序的任何无效参数。最初,你要检查用户是否应用了 –infile 标记。如果他们应用了,那么你就运行 file_parser()。

上面是你如何在你的终端中运行代码。

$ python file_parser.py --infile something.txt
   Processing something.txt
   Finished processing

在这里,你用 –infile 标记和一个文件名来运行你的脚本。这将运行 main(),而后调用 file_parser()。

下一步是应用你在代码中申明的两个命令行参数尝试运行你的应用程序。

$ python file_parser.py --infile something.txt --out output.txt
   Processing something.txt
   Finished processing
   Creating output.txt

这一次,你失去了一个额定的输入行,提到了输入文件名。这代表你的代码逻辑中的一个分支。当你指定一个输入文件时,你能够让你的代码通过应用一个新的代码块或一个函数来生成该文件。如果你不指定一个输入文件,那么那个代码块就不会运行。

当你应用 argparse 创立你的命令行工具时,你能够很容易地增加信息,当你的用户不确定如何正确地与你的程序互动时,能够帮忙他们。

当初是时候找出如何从你的应用程序中取得帮忙了

创立帮忙信息

argparse 库将应用你在创立每个参数时提供的信息,主动为你的应用程序创立一个有用的信息。这里是代码:

# file_parser.py
   import argparse
   def file_parser(input_file, output_file=''):
       print(f'Processing {input_file}')
       print('Finished processing')
       if output_file:
           print(f'Creating {output_file}')
   def main():
       parser = argparse.ArgumentParser('File parser')
       parser.add_argument('--infile', help='Input file')
       parser.add_argument('--out', help='Output file')
       args = parser.parse_args()
       if args.infile:
           file_parser(args.infile, args.out)
   if __name__ == '__main__':
       main()

当初试着用 - h 标记运行这段代码,你应该看到以下内容。

$ file_parser.py -h
   usage: File parser [-h] [--infile INFILE] [--out OUT]
   optional arguments:
     -h, --help       show this help message and exit
     --infile INFILE  Input file
     --out OUT        Output file

add_argument() 的帮忙参数被用来创立下面的帮忙信息。argparse 会主动增加 - h 和 -help 选项。你能够通过给它一个形容和后记来使你的帮忙信息更丰盛。

让咱们用它们来改良你的帮忙信息。首先,把下面的代码复制到一个新的文件中,命名为 file_parser_with_description.py,而后把它批改成上面的样子。

# file_parser_with_description.py
   import argparse
   def file_parser(input_file, output_file=''):
       print(f'Processing {input_file}')
       print('Finished processing')
       if output_file:
           print(f'Creating {output_file}')
   def main():
       parser = argparse.ArgumentParser(
               'File parser',
               description='PyParse - The File Processor',
               epilog='Thank you for choosing PyParse!',
               )
       parser.add_argument('--infile', help='Input file for conversion')
       parser.add_argument('--out', help='Converted output file')
       args = parser.parse_args()
       if args.infile:
           file_parser(args.infile, args.out)
   if __name__ == '__main__':
       main()

在这里,把 description 和 epilog 参数传递给 ArgumentParser。还更新了 add_argument() 的帮忙参数,使其更具描述性。

在做了这些批改之后,当你用 - h 或 –help 运行这个脚本时,你会看到以下输入。

$ python file_parser_with_description.py -h
   usage: File parser [-h] [--infile INFILE] [--out OUT]
   PyParse - The File Processor
   optional arguments:
     -h, --help       show this help message and exit
     --infile INFILE  Input file for conversion
     --out OUT        Converted output file
   Thank you for choosing PyParse!

当初能够在你的帮忙输入中看到新的 description 和 epilog。这给了你的命令行程序一些额定的润饰。

你也能够通过 ArgumentParser 的 add_help 参数在你的应用程序中齐全禁用帮忙。如果你认为你的帮忙文本过于简短,你能够像这样禁用它。

# file_parser_no_help.py
   import argparse
   def file_parser(input_file, output_file=''):
       print(f'Processing {input_file}')
       print('Finished processing')
       if output_file:
           print(f'Creating {output_file}')
   def main():
       parser = argparse.ArgumentParser(
               'File parser',
               description='PyParse - The File Processor',
               epilog='Thank you for choosing PyParse!',
               add_help=False,
               )
       parser.add_argument('--infile', help='Input file for conversion')
       parser.add_argument('--out', help='Converted output file')
       args = parser.parse_args()
       if args.infile:
           file_parser(args.infile, args.out)
   if __name__ == '__main__':
       main()

通过将 add_help 设置为 False,你将禁用 - h 和 –help 标记。

你能够看到上面的演示。

$ python file_parser_no_help.py --help
   usage: File parser [--infile INFILE] [--out OUT]
   File parser: error: unrecognized arguments: --help

在下一节中,你将学习如何为你的参数增加别名!

增加别名

别名是一个花哨的词,指的是应用一个代替的标记来做同样的事件。例如,你晓得你能够应用 - h 和 –help 来拜访程序的帮忙信息。- h 是 –help 的别名,反之亦然。

看看 main() 外面的 parser.add_argument() 办法有什么变动。

# file_parser_aliases.py
   import argparse
   def file_parser(input_file, output_file=''):
       print(f'Processing {input_file}')
       print('Finished processing')
       if output_file:
           print(f'Creating {output_file}')
   def main():
       parser = argparse.ArgumentParser(
               'File parser',
               description='PyParse - The File Processor',
               epilog='Thank you for choosing PyParse!',
               add_help=False,
               )
       parser.add_argument('-i', '--infile', help='Input file for conversion')
       parser.add_argument('-o', '--out', help='Converted output file')
       args = parser.parse_args()
       if args.infile:
           file_parser(args.infile, args.out)
   if __name__ == '__main__':
       main()

这里你扭转了第一个 add_argument(),除了承受 -infile 之外,还承受了 -i,你还在第二个 add_argument() 中退出了 -o。这样就能够应用两个新的快捷标记来运行你的代码。

上面是一个例子。

$ python3 file_parser_aliases.py -i something.txt -o output.txt
   Processing something.txt
   Finished processing
   Creating output.txt

如果你去看 argparse 文档,你会发现也能够给子解析器增加别名。子解析器是一种在你的应用程序中创立子命令的办法,这样它就能够做其余事件。一个很好的例子是 Docker,一个虚拟化或容器应用程序。它有一系列的命令,你能够在 docker 下运行,以及 docker compose 等等。这些命令中的每一个都有独立的子命令,你都能够应用。

上面是一个典型的 docker 命令,用于运行一个容器。
docker exec -it container_name bash

这将用 docker 启动一个容器。而如果你要应用 docker compose,你将应用一组不同的命令。exec 和 compose 是 subparsers 的例子。

应用互相排挤的参数

有时你须要让你的应用程序承受一些参数,但不承受其余参数。例如,你可能想限度你的应用程序,使它只能创立或删除文件,而不是同时创立和删除。

argparse 模块提供了 add_mutually_exclusive_group() 办法,它就是这样做的。

将你的两个参数增加到一个组对象中,使其互相排挤,如上面的例子。

# file_parser_exclusive.py
   import argparse
   def file_parser(input_file, output_file=''):
       print(f'Processing {input_file}')
       print('Finished processing')
       if output_file:
           print(f'Creating {output_file}')
   def main():
       parser = argparse.ArgumentParser(
               'File parser',
               description='PyParse - The File Processor',
               epilog='Thank you for choosing PyParse!',
               add_help=False,
               )
       group = parser.add_mutually_exclusive_group()
       group.add_argument('-i', '--infile', help='Input file for conversion')
       group.add_argument('-o', '--out', help='Converted output file')
       args = parser.parse_args()
       if args.infile:
           file_parser(args.infile, args.out)
   if __name__ == '__main__':
       main()

首先,你创立了一个互相排挤的组。而后,你把 - i 和 - o 参数增加到组中,而不是增加到解析器对象中。当初这两个参数是互斥的。

上面是当你试图用这两个参数运行你的代码时产生的状况。

$ python3 file_parser_exclusive.py -i something.txt -o output.txt
   usage: File parser [-i INFILE | -o OUT]
   File parser: error: argument -o/--out: not allowed with argument -i/--infile

用这两个参数运行你的代码,会使你的解析器向用户显示一条错误信息,解释他们做错了什么。

在涵盖了所有这些与应用 argparse 无关的信息之后,你曾经筹备好利用你的新技能来创立一个简略的搜寻工具了

创立一个简略的搜寻工具

在开始创立一个应用程序之前,弄清楚你要实现的工作总是好的。你在本节中想要建设的应用程序应该可能搜寻特定文件类型的文件。为了使它更乏味,你能够增加一个额定的参数,让你也能选择性地搜寻特定的文件大小。

你能够应用 Python 的 glob 模块来搜寻文件类型。你能够在这里浏览对于这个模块的所有信息。

https://docs.python.org/3/library/glob.html

还有一个 fnmatch 模块,glob 本人也应用它。你当初应该应用 glob,因为它更容易应用,然而如果你有趣味写一些更业余的货色,那么 fnmatch 可能是你正在寻找的。

然而,因为你心愿可能通过文件大小来选择性地过滤返回的文件,你能够应用 pathlib,它包含一个相似 glob 的接口。glob 模块自身并不提供文件大小的信息。

你能够先创立一个名为 pysearch.py 的文件并输出以下代码。

# pysearch.py
   import argparse
   import pathlib
   def search_folder(path, extension, file_size=None):
       """Search folder for files"""
       folder = pathlib.Path(path)
       files = list(folder.rglob(f'*.{extension}'))
       if not files:
           print(f'No files found with {extension=}')
           return
       if file_size is not None:
           files = [
                   f
                   for f in files
                   if f.stat().st_size >= file_size]
       print(f'{len(files)} *.{extension} files found:')
       for file_path in files:
           print(file_path)

在下面的代码片段中,首先导入了 argparse 和 pathlib。接下来,创立了 search_folder() 函数,它接管了三个参数。

  • path – 要搜寻的文件夹
  • extension – 要寻找的文件扩展名
  • file_size – 要过滤的文件大小,以字节为单位。

把门路变成 pathlib.Path 对象,而后应用其 rglob() 办法在文件夹中搜寻用户传入的扩展名。如果没有找到文件,就向用户打印一个有意义的信息,而后退出。

如果找到了任何文件,就查看是否曾经设置了 filesize。如果它被设置了,就用一个 list comprehension 来过滤出小于指定的 filesize 的文件。

接下来,打印出找到的文件的数量,最初在这些文件上循环,打印出它们的名字。

为了使这一切正常工作,须要创立一个命令行界面。你能够通过增加一个蕴含 argparse 代码的 main() 函数来做到这一点,像这样。

def main():
       parser = argparse.ArgumentParser(
               'PySearch',
               description='PySearch - The Python Powered File Searcher',
               )
       parser.add_argument('-p', '--path',
                           help='The path to search for files',
                           required=True,
                           dest='path')
       parser.add_argument('-e', '--ext',
                           help='The extension to search for',
                           required=True,
                           dest='extension')
       parser.add_argument('-s', '--size',
                           help='The file size to filter on in bytes',
                           type=int,
                           dest='size',
                           default=None)
       args = parser.parse_args()
       search_folder(args.path, args.extension, args.size)
   if __name__ == '__main__':
       main()

这个 ArgumentParser() 有三个参数,与你传递给 search_folder() 的参数绝对应。让 –path 和 –ext 参数成为必须的,而让 –size 参数成为可选的。留神,–size 参数被设置为 type=int,这意味着你不能把它传成字符串。

add_argument() 函数有一个新的参数。它是 dest 参数,能够用它来通知你的参数分析器在哪里保留传递给它们的参数。

上面是一个脚本运行的例子。

$ python3 pysearch.py -p /Users/michael/Dropbox/python101code/chapter32_argparse -e py -s 650
   6 *.py files found:
   /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_aliases2.py
   /Users/michael/Dropbox/python101code/chapter32_argparse/pysearch.py
   /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_aliases.py
   /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_with_description.py
   /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_exclusive.py
   /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_no_help.py

当初试试用 - s 和一个字符串来运行它。

$ python3 pysearch.py -p /Users/michael/Dropbox/python101code/chapter32_argparse -e py -s python
   usage: PySearch [-h] -p PATH -e EXTENSION [-s SIZE]
   PySearch: error: argument -s/--size: invalid int value: 'python'

这次咱们收到了一个谬误,因为 - s 和 -size 只承受整数。在你本人的机器上运行一下这段代码,看看当你应用 - s 和整数时,它是否按你想要的形式工作。

这里有一些想法,你能够用来改良你的代码版本。

  • 更好地解决扩大文件。当初,它将承受 *.py,这不会像你冀望的那样工作。
  • 更新代码,以便你能够一次搜寻多个扩展名
  • 更新代码,以便对文件大小的范畴进行过滤(例如,1MB-5MB)。

还有很多其余的性能和改良,你能够增加到这个代码中,比方增加错误处理或单元测试。

总结

argparse 模块功能齐全,能够用来创立宏大、灵便的命令行应用程序。在本章中,你理解了以下内容。

  • 解析参数
  • 创立有用的信息
  • 增加别名
  • 应用互相排挤的参数
  • 创立一个简略的搜寻工具

你能够用 argparse 模块做更多的事件,不齐全包含本章所讲的。请务必查看文档以理解全副细节。当初去试试吧。你会发现,一旦你把握了应用 argparse 的诀窍,你就能够创立一些十分整洁的应用程序了。

以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈 ,每日干货分享

正文完
 0