命令行参数
加密程序
考虑这样一个加密程序,其中一个功能,是对一段字符串进行base64加密,另一个功能,是对一段base64字符串解密:
import base64
def encrypt_to_base64(input_string):
byte_data = input_string.encode("utf-8")
base64_encoded = base64.b64encode(byte_data)
return base64_encoded.decode("utf-8")
def decrypt_from_base64(base64_string):
byte_data = base64_string.encode("utf-8")
decoded_data = base64.b64decode(byte_data)
return decoded_data.decode("utf-8")
encrypt_to_base64("sagegrass")
decrypt_from_base64("c2FnZWdyYXNz")
对于这样一个功能,如果我希望通过命令行调用,然后快速得到结果,可能是像这样的:
# 设置-e选项,进行加密
python base64_encrypt.py -e "sagegrass"
# 设置-d选项,进行解密
python base64_encrypt.py -d "c2FnZWdyYXNz"
那么,该怎么做到呢?
sys.argv
命令行参数以列表的形式,保存于sys模块的argv属性中,具体来说
import sys
print(sys.argv)
# 对于python base64_encrypt.py -e "123456",结果为:['base64_encrypt.py', '-e', 'sagegrass']
# 对于python base64_encrypt.py -d "MTIzNDU2",结果为:['base64_encrypt.py', '-d', 'c2FnZWdyYXNz']
处理命令行参数
因此,根据sys.argv的返回结果,我们可以假设,如果用户的使用方法正确,那么,我们可以:
import base64
import sys
def encrypt_to_base64(input_string):
byte_data = input_string.encode("utf-8")
base64_encoded = base64.b64encode(byte_data)
return base64_encoded.decode("utf-8")
def decrypt_from_base64(base64_string):
byte_data = base64_string.encode("utf-8")
decoded_data = base64.b64decode(byte_data)
return decoded_data.decode("utf-8")
if len(sys.argv) >= 3:
if sys.argv[1] == "-e":
print(encrypt_to_base64(sys.argv[2]))
elif sys.argv[1] == "-d":
print(decrypt_from_base64(sys.argv[2]))
此时,我们程序已经可以正确处理-e
以及-d
的参数了。
复杂命令行参数
计算器案例
考虑这样一个计算器程序,允许接受两个参数,并且选择一个操作,进行加,减,乘,除的操作。
但是,也可以允许只接受一个参数,并且选择进行平方,或者开根号的操作。
同时,也应该允许用户可以先提供数字,或者后提供数字。
# 实现一个加法操作
python calculator.py 520 0.1314 --operation add
python calculator.py -o add 520 0.1314
# 实现一个平方操作
python calculator.py -o square 5
因为操作较为复杂,用户可能搞不清楚,所以同样应该提供一份帮助说明。
python calculator.py -h
那么,在这种情况下,如果还是使用sys.argv
,并且在整个列表中,去解析命令行参数,会非常麻烦。因此,需要更好的工具,去处理这个问题。
argparse
argparse是一个更好的工具,可以处理更加复杂的命令行参数。
创建ArgumentParser对象
parser = argparse.ArgumentParser(description="base64加解密")
添加位置参数
parser.add_argument("text", type=str, help="用于加解密的一段文本")
添加操作符
parser.add_argument("-e", "--encrypt", action="store_true", help="对文本进行base64加密")
parser.add_argument("-d", "--decrypt", action="store_true", help="对文本进行base64解密")
解析参数
args = parser.parse_args()
if args.encrypt:
print(encrypt_to_base64(args.text))
elif args.decrypt:
print(decrypt_from_base64(args.text))
argparse处理base64加解密
import base64
import argparse
def encrypt_to_base64(input_string):
byte_data = input_string.encode("utf-8")
base64_encoded = base64.b64encode(byte_data)
return base64_encoded.decode("utf-8")
def decrypt_from_base64(base64_string):
byte_data = base64_string.encode("utf-8")
decoded_data = base64.b64decode(byte_data)
return decoded_data.decode("utf-8")
parser = argparse.ArgumentParser(description="base64加解密")
parser.add_argument("text", type=str, help="待加解密的文本")
parser.add_argument("-e", "--encrypt", action="store_true", help="对文本进行base64加密")
parser.add_argument("-d", "--decrypt", action="store_true", help="对文本进行base64解密")
args = parser.parse_args()
if args.encrypt:
print(encrypt_to_base64(args.text))
elif args.decrypt:
print(decrypt_from_base64(args.text))
argparse处理计算器
import argparse
import math
parser = argparse.ArgumentParser(description="一个命令行的计算器")
parser.add_argument("num1", type=float, help="第一个数字")
parser.add_argument("num2", type=float, nargs='?', default=None, help="第二个数字(平方和开根号操作无需提供)")
parser.add_argument("-o", "--operation", choices=["add", "sub", "mul", "div", "square", "sqrt"], default="add", help="选择操作:add(加),sub(减),mul(乘),div(除),square(平方),sqrt(开根号)")
args = parser.parse_args()
if args.operation in ["add", "sub", "mul", "div"]:
if args.num2 is None:
parser.error(f"操作'{args.operation}'需要提供两个数字。")
elif args.operation in ["square", "sqrt"]:
if args.num2 is not None:
parser.error(f"操作'{args.operation}'只需要提供一个数字。")
if args.operation == "add":
result = args.num1 + args.num2
elif args.operation == "sub":
result = args.num1 - args.num2
elif args.operation == "mul":
result = args.num1 * args.num2
elif args.operation == "div":
if args.num2 == 0:
result = "除数不能为零!"
else:
result = args.num1 / args.num2
elif args.operation == "square":
result = args.num1 ** 2
elif args.operation == "sqrt":
if args.num1 < 0:
result = "不能对负数开平方根!"
else:
result = math.sqrt(args.num1)
else:
result = None
if result is not None:
print(f"结果: {result}")
在这种情况下,使用-h
查看帮助的时候,就会发现,已经自动生成了一份简洁的帮助信息。
安装为全局命令
全局命令
我们可能经常观察到这样一种情况,在使用他人的第三方库的时候,可以使用命令,例如,lunar-find 春节 2025
而对比我们的命令行程序,则必须要按照python xxx
的形式使用,而且必须找到对应的py文件,这并不方便,那么该可以让我们也使用全局命令呢?
my-lunar-find
因为原本的lunar-find
输出格式不够灵活,因此,我们对其进行简单的重写,以满足可以输出任意格式的时间。
关于原本的lunar-find
工具,如果你并不了解,可以看看这篇文章,lunarcalendar的使用
import argparse
from lunarcalendar.festival import festivals
from lunarcalendar.solarterm import solarterms
from itertools import chain
from datetime import date
def main():
parser = argparse.ArgumentParser(description="自行创建的lunar-find,修改了原本的日期格式")
parser.add_argument("fs_or_st", type=str, help="需要查找的节日或节气")
parser.add_argument("year", type=int, help="年份")
parser.add_argument("-f", "--format", default="%Y年%m月%d日", help="按照需要的格式重新格式化,%Y为年,%m为月,%d为日")
args = parser.parse_args()
for day in chain(festivals, solarterms):
if day.get_lang("zh") == args.fs_or_st:
formatted_time = day(args.year).strftime(args.format)
print(formatted_time)
if __name__ == "__main__":
main()
此时,可以通过提供--format
,来按照需要格式化时间。
python my_lunar_find.py 春节 2025 # 输出:2025年01月29日
python my_lunar_find.py 春节 2025 --format %Y/%m/%d # 输出:2025/01/29
python my_lunar_find.py 春节 2025 --format %Y-%m-%d # 输出:2025-01-29
安装
编写一份setup.py
from setuptools import setup
setup(
name="my_lunar_find",
version="1.0",
py_modules=["my_lunar_find"],
entry_points={
"console_scripts": [
"my-lunar-find = my_lunar_find:main",
],
},
)
安装:pip install .
注意:其中的.
代表当前路径,因此需要在包含setup.py的目录下运行。
此时,是安装了一个我们自己的模块,my-lunar-find==1.0
调用命令:my-lunar-find 春节 2025