使用Python編寫(xiě)類UNIX系統(tǒng)的命令行工具的教程
引言
您是否能編寫(xiě)命令行工具?也許您可以,但您能編寫(xiě)出真正好用的命令行工具嗎?本文討論使用 Python 來(lái)創(chuàng)建一個(gè)強(qiáng)健的命令行工具,并帶有內(nèi)置的幫助菜單、錯(cuò)誤處理和選項(xiàng)處理。由于一些奇怪的原因,很多人并不了解 Python? 的標(biāo)準(zhǔn)庫(kù)具有制作功能極其強(qiáng)大的 *NIX 命令行工具所需的全部工具。
可以這樣說(shuō),Python 是制作 *NIX 命令行工具的最佳語(yǔ)言,因?yàn)樗勒铡癰atteries-included”的哲學(xué)方式工作,并且強(qiáng)調(diào)提供可讀性高的代碼。但僅作為提醒,當(dāng)您發(fā)現(xiàn)使用 Python 創(chuàng)建命令行工具是一件多么簡(jiǎn)單的事情時(shí),這些想法很危險(xiǎn),您的生活可能被攪得一團(tuán)糟。據(jù)我所知,至今還沒(méi)有發(fā)表過(guò)詳細(xì)說(shuō)明使用 Python 創(chuàng)建命令行工具的文章,因此我希望您喜歡這篇文章。
設(shè)置
Python 標(biāo)準(zhǔn)庫(kù)中的 optparse 模塊可完成創(chuàng)建命令行工具的大部分瑣碎工作。optparse 包含在 Python 2.3 中,因此該模塊將包括在許多 *NIX 操作系統(tǒng)中。如果由于某種原因,您使用的操作系統(tǒng)不包含所需要的模塊,那么值得慶幸的是,Python 的最新版本已經(jīng)過(guò)測(cè)試并編譯到幾乎任何 *NIX 操作系統(tǒng)中。Python 支持的系統(tǒng)包括 IBM? AIX?、HP-UX、Solaris、Free BSD、Red Hat Linux?、Ubuntu、OS X、IRIX,甚至包括幾種 Nokia 手機(jī)。
創(chuàng)建 Hello World 命令行工具
編寫(xiě)優(yōu)秀的命令行工具的第一步是定義要解決的問(wèn)題。這對(duì)您工具的成功至關(guān)重要。這對(duì)于以盡可能簡(jiǎn)單的方法解決問(wèn)題也同樣重要。這里明確地采用了 KISS(Keep It Simple Stupid,保持簡(jiǎn)單)準(zhǔn)則。只有在實(shí)現(xiàn)并測(cè)試了計(jì)劃內(nèi)功能之后才添加選項(xiàng)和增加其他功能。
我們首先從創(chuàng)建 Hello World 命令行工具開(kāi)始。按照上面的建議,我們使用盡可能簡(jiǎn)單的術(shù)語(yǔ)來(lái)定義問(wèn)題。
問(wèn)題定義:我希望創(chuàng)建一個(gè)命令行工具,默認(rèn)打印 Hello World,并提供用于打印不通人的姓名的選項(xiàng)。
基于上述說(shuō)明,可以提供一個(gè)包含少量代碼的解決方案。
Hello World 命令行接口 (CLI)
#!/usr/bin/env python import optparse def main(): p = optparse.OptionParser() p.add_option('--person', '-p', default="world") options, arguments = p.parse_args() print 'Hello %s' % options.person if __name__ == '__main__': main()
如果運(yùn)行此代碼,預(yù)期的輸出如下:
Hello world
但是,我們通過(guò)少量代碼所能做到的遠(yuǎn)不止于此。我們可以獲得自動(dòng)生成的幫助菜單:
python hello_cli.py --help Usage: hello_cli.py [options] Options: -h, --help show this help message and exit -p PERSON, --person=PERSON
從幫助菜單中可以了解到,我們可以使用兩種方法來(lái)更改 Hello World 的輸出:
python hello_cli.py -p guido Hello guido
我們還實(shí)現(xiàn)了自動(dòng)生成的錯(cuò)誤處理:
python hello_cli.py --name matz Usage: hello_cli.py [options] hello_cli.py: error: no such option: --name
如果您還沒(méi)有使用過(guò) Python 的 optparse 模塊,那么您剛才可能會(huì)大吃一驚,并思忖使用 Python 可以編寫(xiě)的所有這些不可思議的工具。如果您剛開(kāi)始接觸 Python,那么您可能會(huì)驚訝于 Python 讓一切變得如此簡(jiǎn)單?!癤KCD”網(wǎng)站發(fā)表了關(guān)于“Python 是如此簡(jiǎn)單”主題的非常有趣的漫畫(huà),已包括在參考資料中。
創(chuàng)建有用的命令行工具
既然我們已經(jīng)打好了基礎(chǔ),我們就可以繼續(xù)創(chuàng)建解決特定問(wèn)題的工具。對(duì)于本例,我們將使用 Python 的名為 Scapy 的網(wǎng)絡(luò)庫(kù)和交互式工具。Scapy 可以在大多數(shù) *NIX 系統(tǒng)上正常工作,可以在第 2 層和第 3 層上發(fā)送數(shù)據(jù)包,并允許您創(chuàng)建只有幾行 Python 代碼的非常復(fù)雜的工具。如果您希望按部就班從頭開(kāi)始,請(qǐng)確保您正確地安裝了必要的軟件。
我們先定義要解決的新問(wèn)題。
問(wèn)題:我希望創(chuàng)建一個(gè)使用 IP 地址或子網(wǎng)作為參數(shù)的命令行工具,并向標(biāo)準(zhǔn)輸出返回 MAC 地址或 MAC 地址列表以及它們各自的 IP 地址。
既然我們已經(jīng)清楚地定義了問(wèn)題,讓我嘗試將問(wèn)題分解為盡可能簡(jiǎn)單的部分,然后逐一解決這些部分。對(duì)于這一問(wèn)題,我看到了兩個(gè)獨(dú)立的部分。第一部分是編寫(xiě)接收 IP 地址或子網(wǎng)范圍的函數(shù),并返回 MAC 地址或 MAC 地址列表。我們可以在解決此問(wèn)題之后再考慮將其集成到命令行工具中。
解決方案第 1 部分:創(chuàng)建通過(guò) IP 地址確定 MAC 地址的 Python 函數(shù)
arping from scapy import srp,Ether,ARP,conf conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="10.0.1.1"), timeout=2) for snd, rcv in ans: print rcv.sprintf(r"%Ether.src% %ARP.psrc%")
該命令的輸出是:
sudo python arping.py 00:00:00:00:00:01 10.0.1.1
請(qǐng)注意,使用 scapy 執(zhí)行操作要求提升的權(quán)限,因此我們必須使用 sudo。考慮到本文的目的,我還將實(shí)際輸出更改為包括偽 MAC 地址。我們已經(jīng)證實(shí)了我們可以通過(guò) IP 地址找到 MAC 地址。我們需要整理此代碼以接受 IP 地址或子網(wǎng)并返回 MAC 地址和 IP 地址對(duì)。
arping 函數(shù)
#!/usr/bin/env python from scapy import srp,Ether,ARP,conf def arping(iprange="10.0.1.0/24"): conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=iprange), timeout=2) collection = [] for snd, rcv in ans: result = rcv.sprintf(r"%ARP.psrc% %Ether.src%").split() collection.append(result) return collection #Print results values = arping() for ip,mac in values: print ip,mac
正如您看到的,我們編寫(xiě)了一個(gè)函數(shù),該函數(shù)接受 IP 地址或網(wǎng)絡(luò)并返回嵌套的 IP/MAC 地址列表。我們現(xiàn)已為第二部分做好準(zhǔn)備,為我們的工具創(chuàng)建一個(gè)命令行接口。
解決方案第 2 部分:從我們的 arping 函數(shù)創(chuàng)建命令行工具
在本例中,我們綜合本文前面部分的想法,創(chuàng)建一個(gè)能解決我們初始問(wèn)題的完整命令行工具。
arping CLI
#!/usr/bin/env python import optparse from scapy import srp,Ether,ARP,conf def arping(iprange="10.0.1.0/24"): """Arping function takes IP Address or Network, returns nested mac/ip list""" conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=iprange), timeout=2) collection = [] for snd, rcv in ans: result = rcv.sprintf(r"%ARP.psrc% %Ether.src%").split() collection.append(result) return collection def main(): """Runs program and handles command line options""" p = optparse.OptionParser(description=' Finds MAC Address of IP address(es)', prog='pyarping', version='pyarping 0.1', usage='%prog [10.0.1.1 or 10.0.1.0/24]') options, arguments = p.parse_args() if len(arguments) == 1: values = arping(iprange=arguments) for ip, mac in values: print ip, mac else: p.print_help() if __name__ == '__main__': main()
對(duì)以上腳本進(jìn)行幾點(diǎn)說(shuō)明將有助于我們了解 optparse 的工作方式。
首先,必須創(chuàng)建 optparse.OptionParser() 的一個(gè)實(shí)例,并且接受如下所示的可選參數(shù):
這些參數(shù)的含義基本上可以不言自明,但我希望確認(rèn)的一點(diǎn)是,您應(yīng)該了解 optparse 雖然功能強(qiáng)大,但并不是無(wú)所不能。它具有明確定義的接口,可用于快速創(chuàng)建命令行工具。
其次,在如下行中:
該行的作用是將選項(xiàng)和參數(shù)劃分為不同的位。在上述代碼中,我們預(yù)期恰有一個(gè)參數(shù),因此我指定必須只有一個(gè)參數(shù)值,并將該值傳遞給 arping 函數(shù)。
if len(arguments) == 1: values = arping(iprange=arguments)
為了進(jìn)一步說(shuō)明,讓我們運(yùn)行下面的命令以了解其工作方式:
sudo python arping.py 10.0.1.1 10.0.1.1 00:00:00:00:00:01
在上述示例中,參數(shù)為 10.0.1.1,由于正如我在條件語(yǔ)句中指定的那樣只有一個(gè)參數(shù),因此該參數(shù)被傳遞給 arping 函數(shù)。如果存在選項(xiàng),它們將在 options, arguments = p.parse_args() 方法中傳遞給 options。讓我們看一下,當(dāng)我們分解命令行工具的預(yù)期用例并賦予該用例兩個(gè)參數(shù)時(shí)將會(huì)發(fā)生什么情況:
sudo python arping.py 10.0.1.1 10.0.1.3 Usage: pyarping [10.0.1.1 or 10.0.1.0/24] Finds MAC Address or IP address(es) Options: --version show program's version number and exit -h, --help show this help message and exit
根據(jù)我為參數(shù)構(gòu)建的條件語(yǔ)句的結(jié)構(gòu),如果參數(shù)的數(shù)目不為 1,它將自動(dòng)打開(kāi)幫助菜單:
if len(arguments) == 1: values = arping(iprange=arguments) for ip, mac in values: print ip, mac else: p.print_help()
這是一種用于控制工具的工作方式的重要方法,因?yàn)槟梢允褂脜?shù)的個(gè)數(shù)或特定選項(xiàng)的名稱作為控制命令行工具的流程的機(jī)制。因?yàn)槲覀冊(cè)谧畛醯?Hello World 示例中涉及了選項(xiàng)的創(chuàng)建,接下來(lái)通過(guò)略微更改主函數(shù)向我們的命令行工具添加幾個(gè)選項(xiàng):
arping CLI main 函數(shù)
def main(): """Runs program and handles command line options""" p = optparse.OptionParser(description='Finds MAC Address of IP address(es)', prog='pyarping', version='pyarping 0.1', usage='%prog [10.0.1.1 or 10.0.1.0/24]') p.add_option('-m', '--mac', action ='store_true', help='returns only mac address') p.add_option('-v', '--verbose', action ='store_true', help='returns verbose output') options, arguments = p.parse_args() if len(arguments) == 1: values = arping(iprange=arguments) if options.mac: for ip, mac in values: print mac elif options.verbose: for ip, mac in values: print "IP: %s MAC: %s " % (ip, mac) else: for ip, mac in values: print ip, mac else: p.print_help()
所做的主要更改是創(chuàng)建了基于是否指定了某個(gè)選項(xiàng)的條件語(yǔ)句。請(qǐng)注意,與 Hello World 命令行工具不同,我們僅使用選項(xiàng)作為我們工具的 true/false 信號(hào)。對(duì)于 –MAC 選項(xiàng)的情況,如果指定了該選項(xiàng),我們的條件語(yǔ)句 elif 將只打印 MAC 地址。
下面是新選項(xiàng)的輸出:
arping 輸出
sudo python arping2.py Password: Usage: pyarping [10.0.1.1 or 10.0.1.0/24] Finds MAC Address of IP address(es) Options: --version show program's version number and exit -h, --help show this help message and exit -m, --mac returns only mac address -v, --verbose returns verbose output [ngift@M-6][H:11184][J:0]> sudo python arping2.py 10.0.1.1 10.0.1.1 00:00:00:00:00:01 [ngift@M-6][H:11185][J:0]> sudo python arping2.py -m 10.0.1.1 00:00:00:00:00:01 [ngift@M-6][H:11186][J:0]> sudo python arping2.py -v 10.0.1.1 IP: 10.0.1.1 MAC: 00:00:00:00:00:01
深入學(xué)習(xí)創(chuàng)建命令行工具
下面是幾個(gè)用于深入學(xué)習(xí)的新想法。在我正與別人合著的有關(guān) Python *NIX 系統(tǒng)管理的書(shū)中對(duì)這些想法進(jìn)行了深入的探討,該書(shū)將在 2008 年中期出版。
在命令行工具中使用 subprocess 模塊
subprocess 模塊包括在 Python 2.4 或更高版本中,是用于處理系統(tǒng)調(diào)用和流程的統(tǒng)一接口。您可以輕松替換上面的 arping 函數(shù),以使用適用于您的特定 *NIX 操作系統(tǒng)的 arping 工具。以下是體現(xiàn)上述想法的粗略示例:
子流程 arping
import subprocess import re def arping(ipaddress="10.0.1.1"): """Arping function takes IP Address or Network, returns nested mac/ip list""" #Assuming use of arping on Red Hat Linux p = subprocess.Popen("/usr/sbin/arping -c 2 %s" % ipaddress, shell=True, stdout=subprocess.PIPE) out = p.stdout.read() result = out.split() pattern = re.compile(":") for item in result: if re.search(pattern, item): print item arping()
以下是該函數(shù)單獨(dú)運(yùn)行時(shí)的輸出: [root@localhost]~# python pyarp.py [00:16:CB:C3:B4:10]
請(qǐng)注意使用 subprocess 來(lái)獲取 arping 命令的輸出,以及使用已編譯的正則表達(dá)式匹配 MAC 地址。注意,如果您使用的是 Python 2.3,則可以使用 popen 模塊替換 subprocess,后者在 Python 2.4 或更高版本中提供。
在命令行工具中使用對(duì)象關(guān)系映射器,如配合 SQLite 使用的 SQLAlchemy 或 Storm
命令行工具的另一個(gè)可能選項(xiàng)是使用 ORM(對(duì)象關(guān)系映射器)來(lái)存儲(chǔ)由命令行工具生成的數(shù)據(jù)記錄。有相當(dāng)多的 ORM 可用于 Python,但 SQLAlchemy 和 Storm 恰好是最常用的兩個(gè)。我通過(guò)擲硬幣的方式?jīng)Q定使用 Storm 作為示例:
Storm ORM arping
#!/usr/bin/env python import optparse from storm.locals import * from scapy import srp,Ether,ARP,conf class NetworkRecord(object): __storm_table__ = "networkrecord" id = Int(primary=True) ip = RawStr() mac = RawStr() hostname = RawStr() def arping(iprange="10.0.1.0/24"): """Arping function takes IP Address or Network, returns nested mac/ip list""" conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=iprange), timeout=2) collection = [] for snd, rcv in ans: result = rcv.sprintf(r"%ARP.psrc% %Ether.src%").split() collection.append(result) return collection def main(): """Runs program and handles command line options""" p = optparse.OptionParser() p = optparse.OptionParser(description='Finds MACAddr of IP address(es)', prog='pyarping', version='pyarping 0.1', usage= '%prog [10.0.1.1 or 10.0.1.0/24]') options, arguments = p.parse_args() if len(arguments) == 1: database = create_database("sqlite:") store = Store(database) store.execute("CREATE TABLE networkrecord " "(id INTEGER PRIMARY KEY, ip VARCHAR,\ mac VARCHAR, hostname VARCHAR)") values = arping(iprange=arguments) machine = NetworkRecord() store.add(machine) #Creates Records for ip, mac in values: machine.mac = mac machine.ip = ip #Flushes to database store.flush() #Prints Record print "Record Number: %r" % machine.id print "MAC Address: %r" % machine.mac print "IP Address: %r" % machine.ip else: p.print_help() if __name__ == '__main__': main()
本例中需要關(guān)注的主要內(nèi)容是創(chuàng)建名為 NetworkRecord 的類,該類映射到“內(nèi)存中”的 SQLite 數(shù)據(jù)庫(kù)。在 main 函數(shù)中,我將 arping 函數(shù)的輸出更改為映射到我們的記錄對(duì)象,將它們更新到數(shù)據(jù)庫(kù),然后再將其取回以打印結(jié)果。這明顯不是一個(gè)可用于生產(chǎn)的工具,但可作為在我們的工具中使用 ORM 的相關(guān)步驟的說(shuō)明性示例。
在 CLI 中集成 config 文件
Python INI config 語(yǔ)法
[AIX] MAC: 00:00:00:00:02 IP: 10.0.1.2 Hostname: aix.example.com [HPUX] MAC: 00:00:00:00:03 IP: 10.0.1.3 Hostname: hpux.example.com [SOLARIS] MAC: 00:00:00:00:04 IP: 10.0.1.4 Hostname: solaris.example.com [REDHAT] MAC: 00:00:00:00:05 IP: 10.0.1.5 Hostname: redhat.example.com [UBUNTU] MAC: 00:00:00:00:06 IP: 10.0.1.6 Hostname: ubuntu.example.com [OSX] MAC: 00:00:00:00:07 IP: 10.0.1.7 Hostname: osx.example.com
接下來(lái),我們需要使用 ConfigParser 模塊來(lái)解析上述內(nèi)容:
ConfigParser 函數(shù)
#!/usr/bin/env python import ConfigParser def readConfig(file="config.ini"): Config = ConfigParser.ConfigParser() Config.read(file) sections = Config.sections() for machine in sections: #uncomment line below to see how this config file is parsed #print Config.items(machine) macAddr = Config.items(machine)[0][1] print machine, macAddr readConfig()
該函數(shù)的輸出如下:
OSX 00:00:00:00:07 SOLARIS 00:00:00:00:04 AIX 00:00:00:00:02 REDHAT 00:00:00:00:05 UBUNTU 00:00:00:00:06 HPUX 00:00:00:00:03
我將剩下的問(wèn)題作為練習(xí)留給讀者來(lái)解決。我接下來(lái)要做的是將該 config 文件集成到我的腳本中,這樣我就可以將我的 config 文件中記錄的機(jī)器庫(kù)存與出現(xiàn)在 ARP 緩存中的 MAC 地址的實(shí)際庫(kù)存進(jìn)行比較。IP 地址或主機(jī)名只在跟蹤到計(jì)算機(jī)時(shí)才能發(fā)揮其作用,但是我們實(shí)現(xiàn)的工具對(duì)于跟蹤網(wǎng)絡(luò)上存在的計(jì)算機(jī)的硬件地址并確定它以前是否出現(xiàn)在網(wǎng)絡(luò)上可能非常有用。
結(jié)束語(yǔ)
我們首先通過(guò)編寫(xiě)幾行代碼創(chuàng)建了一個(gè)非常簡(jiǎn)單但功能強(qiáng)大的 Hello World 命令行工具。然后使用 Python 網(wǎng)絡(luò)庫(kù)創(chuàng)建了一個(gè)復(fù)雜的網(wǎng)絡(luò)工具。最后,我們繼續(xù)討論一些更高級(jí)的研究領(lǐng)域以饗讀者。在高級(jí)研究部分,我們討論了 subprocess 模塊、對(duì)象關(guān)系映射器的集成,最后討論了配置文件。
雖然并不為眾人所知,但任何具有 IT 背景的讀者都可以使用 Python 輕松地創(chuàng)建命令行工具。我希望本文能夠激勵(lì)您親自動(dòng)手創(chuàng)建全新的命令行工具。
- python調(diào)用ffmpeg命令行工具便捷操作視頻示例實(shí)現(xiàn)過(guò)程
- Python編程編寫(xiě)完善的命令行工具
- python命令行工具Click快速掌握
- Python中的命令行參數(shù)解析工具之docopt詳解
- Python命令行參數(shù)解析工具 docopt 安裝和應(yīng)用過(guò)程詳解
- 詳解Python命令行解析工具Argparse
- Python中最好用的命令行參數(shù)解析工具(argparse)
- python實(shí)現(xiàn)測(cè)試工具(一)——命令行發(fā)送get請(qǐng)求
- Python fire模塊(最簡(jiǎn)化命令行生成工具)的使用教程詳解
- python開(kāi)發(fā)簡(jiǎn)單的命令行工具簡(jiǎn)介
相關(guān)文章
使用python修改文件并立即寫(xiě)回到原始位置操作(inplace讀寫(xiě))
這篇文章主要介紹了使用python修改文件并立即寫(xiě)回到原始位置操作(inplace讀寫(xiě)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06使用selenium模擬動(dòng)態(tài)登錄百度頁(yè)面的實(shí)現(xiàn)
本文主要介紹了使用selenium模擬動(dòng)態(tài)登錄百度頁(yè)面,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05解決python圖像處理圖像賦值后變?yōu)榘咨膯?wèn)題
這篇文章主要介紹了解決python圖像處理圖像賦值后變?yōu)榘咨膯?wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06Python 爬蟲(chóng)之Beautiful Soup模塊使用指南
這篇文章主要介紹了Python 爬蟲(chóng)之Beautiful Soup模塊使用指南,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Python MNIST手寫(xiě)體識(shí)別詳解與試練
MNIST(官方網(wǎng)站)是非常有名的手寫(xiě)體數(shù)字識(shí)別數(shù)據(jù)集,在Tensorflow的官方網(wǎng)站里,第一個(gè)就拿它來(lái)做實(shí)戰(zhàn)講解,咱們也以此作為開(kāi)始的項(xiàng)目2021-11-11Python的10道簡(jiǎn)單測(cè)試題(含答案)
這篇文章主要介紹了Python的10道簡(jiǎn)單測(cè)試題(含答案),學(xué)習(xí)了一段時(shí)間python的小伙伴來(lái)做幾道測(cè)試題檢驗(yàn)一下自己的學(xué)習(xí)成果吧2023-04-04關(guān)于Python中幾種隊(duì)列Queue用法區(qū)別
這篇文章主要介紹了關(guān)于Python中幾種隊(duì)列Queue用法區(qū)別,queue隊(duì)列中的put()或者get()方法中都提供了timeout參數(shù),利用這個(gè)參數(shù)可以有效解決上述消除不能消費(fèi)和線程一直阻塞問(wèn)題,需要的朋友可以參考下2023-05-05Python Selenium網(wǎng)頁(yè)自動(dòng)化利器使用詳解
這篇文章主要為大家介紹了使用Python Selenium實(shí)現(xiàn)網(wǎng)頁(yè)自動(dòng)化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12python基礎(chǔ)之變量和數(shù)據(jù)類型
這篇文章主要介紹了python的變量和數(shù)據(jù)類型,實(shí)例分析了Python中返回一個(gè)返回值與多個(gè)返回值的方法,需要的朋友可以參考下2021-10-10