命令行--help 输出、手动和交互执行
一、自定义命令解析类
1、先定义一个命令行解析类
class CommandLineParser:
def __init__(self):
self.arguments = {}
self.valid_options = {
"help", "mode", "interval", "export", "wave_data_export", "wave_set",
"ip", "serial_path", "rate", "devid", "url", "topic", "user", "passw",
}
def parse(self, args):
current_name = ""
values = []
invalid_options = []
for arg in args:
if arg.startswith("--"):
if current_name != "" and values:
self.arguments[current_name] = values
values = []
current_name = arg[2:]
elif arg.startswith("-"):
if current_name != "" and values:
self.arguments[current_name] = values
values = []
current_name = arg[1:]
else:
values.append(arg)
if current_name != "":
self.arguments[current_name] = values
for key in self.arguments.keys():
if key not in self.valid_options:
invalid_options.append(key)
if invalid_options:
raise ValueError(f"Invalid option(s): {' '.join(invalid_options)}")
def contains(self, name):
return name in self.arguments
def get_help_message(self):
script_name = sys.argv[0]
return f"""
Usage: python {script_name} [options]
Options:
--help Show this help message and exit
--mode Choose connection mode (1 for MIB RS232, 2 for LAN)
--interval Data Transmission interval (1-5)
--export Data export option (1-4)
--wave_set Waveform data export priority option (0-12)
--wave_data_export Choose Waveform data export scale option (1-2)
--ip Target IP address for LAN
--serial_path Target Serial port path for MIB
--rate Serial baudrate for MIB
--devid Device ID/Name for export
--url Data export URL (JSON or MQTT WebSocket)
--topic MQTT Topic
--user MQTT Username
--passw MQTT Password
Examples:
python {script_name} --mode 1 --interval 1 --export 1 --wave_set 0 --wave_data_export 1 --serial_path /dev/ttyS5 --rate 115200
python {script_name} --mode 2 --interval 1 --export 1 --wave_set 0 --wave_data_export 1 --ip localhost
"""
2、定义一个获取交互的函数,命令参数没有就从交互中取
def get_input(parser, key, options=None, prompt="", default=None):
if options is None:
options = []
if parser.arguments.get(key):
return parser.arguments[key][0]
else:
if default is not None:
return default
if options:
for option in options:
print(option)
return input(prompt)
3、使用get_input取值
ip_address_remote = get_input(parser, 'ip', prompt="Enter the target IP address of the monitor assigned by DHCP: ",
default="localhost")
4、程序入口
def main(args):
parser = CommandLineParser()
try:
parser.parse(args)
if parser.contains('help'):
print(parser.get_help_message())
return
except ValueError as e:
print(e)
print(parser.get_help_message())
return
ser_connect_set = get_input(parser, 'mode', ["1. Connect via MIB RS232 port", "2. Connect via LAN port"],
"Choose connection mode (1-2): ", default='2')
if ser_connect_set == "1":
connect_via_MIB(args)
elif ser_connect_set == "2":
connect_via_lan(args)
else:
print("Invalid mode selected!")
if __name__ == '__main__':
main(sys.argv[1:])
二、getopt
1、定义函数
def print_help():
print("python {} ".format(__file__))
print("python {} --db-type=mysql --db-host=192.168.1.205".format(__file__))
print("python {} --db-type=mysql --db-host=192.168.1.205 --db-port=3310".format(__file__))
print("python {} --db-type=oracle --db-host=192.168.1.205".format(__file__))
print("python {} --db-type=oracle --db-host=192.168.1.205 --db-port=1522 --db-oracle-sid=orac".format(__file__))
def main(argv):
try:
options, args = getopt.getopt(argv, "h", ["help", "db-type=", "db-host=", "db-oracle-sid=", "db-port="])
except getopt.GetoptError:
sys.exit()
db_type, db_host, db_oracle_sid, db_port = None, None, None, None
for option, value in options:
if option in ("-h", "--help"):
print_help()
sys.exit()
if option in "--db-type":
db_type = value
if option in "--db-host":
db_host = value
if option in "--db-oracle-sid":
db_oracle_sid = value
if option in "--db-port":
db_port = value
if not db_type or db_type not in (DB_TYPE_MYSQL, DB_TYPE_ORACLE) or not db_host:
print_help()
sys.exit()
check_backup_yaml_format(SERVICE_ROOT_PATH)
check_backup_yaml_format(SERVICES_TEMPLATE_PATH)
do_work(db_type, db_host, db_oracle_sid, db_port)
if __name__ == "__main__":
main(sys.argv[1:])
2、关于getopt模块的介绍
1、基本语法
getopt.getopt(args, shortopts, longopts=[])
# 如下
try:
options, args = getopt.getopt(
argv,
"h", # 短选项:支持 -h
["help", # 长选项列表
"db-type=",
"db-host=",
"db-oracle-sid=",
"db-port="]
)
except getopt.GetoptError:
sys.exit()
argv |
要解析的参数列表
|
"h" |
支持的短选项:
-h(不需要等号,因为无值) |
|
第三个参数
|
支持的长选项列表:<br>
=表示该选项需要值(如--db-type=mysql) |
参数说明:
print("python {} ".format(__file__)) 把脚本路径插入到字符串中,相对路径下只有文件名
args: 通常是 sys.argv[1:],即命令行参数列表(不包括脚本名)
shortopts: 短选项字符串,每个字符代表一个选项
longopts: 长选项列表
2、options, args = getopt.getopt 含义
options: 解析后的 (option, value) 元组列表
print(type(getopt.getopt(argv, "h"))) <class 'tuple'>
[('--db-type', 'mysql'), ('--db-host', '192.168.1.205')]
options, args = getopt.getopt(argv, "h", ["help", "db-type=", "db-host=", "db-oracle-sid=", "db-port="])
3、argv 应该是从命令行接收到的参数列表,通常是 sys.argv[1:]
python script.py --db-type=oracle --db-host=192.168.1.1-
sys.argv[1:]会是['--db-type=oracle', '--db-host=192.168.1.1'] -
把
sys.argv[1:]传给getopt.getopt() -
源码中定义了对args的处理,主要是取到选项和选项的值 def getopt(args, shortopts, longopts = []):
使用示例
import getopt
import sys
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "ho:vf:dm:", [
"help", "output=", "file=", "debug", "mode=", "name=", "version"
])
except getopt.GetoptError as err:
print(str(err))
usage()
sys.exit(2)
output = None
verbose = False
input_file = None
debug = False
mode = "default"
name = None
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("-o", "--output"):
output = a
print(output)
elif o == "-v":
verbose = True
print(verbose)
elif o in ("-f", "--file"):
input_file = a
print(input_file)
elif o in ("-d", "--debug"):
debug = True
print(debug)
elif o in ("-m", "--mode"):
mode = a
print(mode)
elif o == "--name":
name = a
print(name)
elif o == "--version":
print("Script Version 1.0")
sys.exit()
else:
assert False, "unhandled option"
def usage():
print("""Usage: script.py [OPTIONS]
Options:
-h, --help Show this help message and exit
-o OUTPUT, --output=OUTPUT
Specify output file
-v Verbose mode
-f FILE, --file=FILE Input file
-d, --debug Enable debug mode
-m MODE, --mode=MODE Specify operation mode
--name=NAME Specify name
--version Show version information
""")
if __name__ == "__main__":
main()
关键点:
短选项格式:单个字母后跟冒号表示该选项需要参数,如 "ho:vf:dm:"
h: 不需要参数o:: 需要参数v: 不需要参数f:: 需要参数d: 不需要参数m:: 需要参数- name这里没有短选项
长选项格式:选项名后跟等号表示需要参数,如 ["help", "output="]
getopt() 返回两个值:(options, args)
options 是一个 (option, value) 对的列表,列表套元组
args 包含那些没有被 opts 处理的命令行参数
错误处理:
使用 try-except 块来捕获 getopt.GetoptError 异常,这样可以处理无效的命令行参数。
三、推荐使用的argparse和click
1、argparse
是Python标准库中最推荐使用的命令行解析模块。它功能强大,使用简单,并能自动生成帮助和使用信息。
def main():
SCRIPT_NAME = os.path.basename(sys.argv[0])
parser = argparse.ArgumentParser(
description='EMR 文档压缩归档与黑名单删除工具',
formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument('--op', choices=['compress', 'delete', 'extract'], required=True,
help='操作类型:compress(压缩归档)、delete(删除黑名单文档)、extract(从归档恢复)')
compress_group = parser.add_argument_group('压缩归档参数')
compress_group.add_argument('--years', metavar='N', type=int, default=6,
help=f"""归档距今超过 N 年的文档,默认 N=6。
示例:
python {SCRIPT_NAME} --op compress
python {SCRIPT_NAME} --op compress --years 7
""")
delete_group = parser.add_argument_group('黑名单删除参数')
delete_group.add_argument('--blacklist', metavar='PATH',
help=f"""可选:指定外部黑名单文件路径(默认使用内置关键字)。
示例:
python {SCRIPT_NAME} --op delete
python {SCRIPT_NAME} --op delete --blacklist /root/blacklist.txt
""")
extract_group = parser.add_argument_group('归档恢复参数')
extract_group.add_argument('--archive', metavar='PATH',
help=f"""指定归档文件路径(.tar.xz)进行恢复。
示例:
python {SCRIPT_NAME} --op extract --archive /mnt/backup/minio/archive_file/2015/20150101.tar.xz
""")
args = parser.parse_args()
setup_logging(args.op)
try:
if args.op == 'compress':
global ARGS
ARGS = args # 供 group_docs_by_date 中使用
compress_all()
elif args.op == 'delete':
blacklist_patterns = load_blacklist(args.blacklist)
delete_blacklisted_documents(blacklist_patterns)
elif args.op == 'extract':
if args.archive and os.path.exists(args.archive):
extract_archive(args.archive)
else:
logging.error("归档路径无效或不存在,请指定有效的 .tar.xz 文件")
except Exception as e:
logging.exception("未处理的程序异常:" + str(e))
finally:
logging.info("操作完成")
if __name__ == '__main__':
main()

补充:
长短选项:直接多加一个 'x', 即可
parser.add_argument('--years', '-y', type=int, default=6, metavar='', help='归档年限(默认前6年)')
提示信息:metavar
- metavar='' 不提示
- metavar 不写,默认提示选项的大写字母
- metavar='xxx',提示xxx
必填选项:不写报错
required=True,
# python test.py --help
usage: test.py [-h] --op {compress,delete} [--start START] [--end END] [--years] [--blacklist PATH]
必填里面的选项:
'--op', choices=['compress', 'delete'],
子命令写法
比如 Git 风格:git commit, git push。
subparsers = parser.add_subparsers(dest='command')
compress_parser = subparsers.add_parser('compress', help='压缩数据')
compress_parser.add_argument('--start', required=True)
compress_parser.add_argument('--end', required=True)
delete_parser = subparsers.add_parser('delete', help='删除黑名单')
delete_parser.add_argument('--blacklist', required=True)
一个参数接受多个值
parser.add_argument('--files', nargs='+', help='文件路径列表')
版本信息
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
parser = argparse.ArgumentParser(
description='EMR compress and delete tool',
epilog='欢迎反馈问题到 dev@example.com'
)
2、click
这是一个第三方库,设计得非常优雅和直观。它使用装饰器来定义命令行接口,非常适合构建复杂的命令行应用。
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name', help='The person to greet.')
def hello(count, name):
for _ in range(count):
click.echo(f"Hello, {name}!")
if __name__ == '__main__':
hello()

浙公网安备 33010602011771号