如何使用Swift來實現(xiàn)一個命令行工具的方法
本文即簡單介紹了如何在Swift中開發(fā)命令行工具,以及與Shell命令的交互。水文一篇,不喜勿噴。
主要是使用該工具來解析微信的性能監(jiān)控組件Matrix的OOM Log。
基本模塊
這里,僅簡單介紹了常見的基本模塊。
Process
Process類可以用來打開另外一個子進程,并監(jiān)控其運行情況。
- launchPath:指定了執(zhí)行路徑。如可以設(shè)置為 /usr/bin/env ,這個命令可以用于打印本機上所有的環(huán)境變量;也可以用于執(zhí)行shell命令,如果你接了參數(shù)的話。本文的Demo就用它來執(zhí)行輸入的命令。
- arguments:參數(shù),以數(shù)組形式傳遞即可。
- launch:調(diào)用launch函數(shù)即可啟動process,用于執(zhí)行命令。
- waitUntilExit:一般執(zhí)行Shell命令,需要等待命令返回。
- terminationStatus:當(dāng)前process的結(jié)束狀態(tài),正常為0.
- standardOutput:standardOutput對應(yīng)于終端的標(biāo)準(zhǔn)輸出。standardError則是錯誤輸出。
Pipe
Pipe這個類就是操作系統(tǒng)的管道,在這里用來接受子進程的輸出。這里,可以用于將process的輸出傳遞至管道指定的地方,如一個output變量,或者文件也可以。
- fileHandleForReading:pipe從哪里讀取內(nèi)容?
- fileHandleForWriting:pipe將內(nèi)容寫到哪里?
CommandLine
用于獲取腳本參數(shù)而已。
print(CommandLine.argc) // 2 print(CommandLine.arguments) // ["./test.swift", "hello"]
封裝Shell命令
僅執(zhí)行Shell命令
這里提供了兩種調(diào)用Shell命令的封裝函數(shù),個人更傾向于第二種,直接將Shell命令及參數(shù)封裝成一個字符串傳入即可。
@discardableResult func runShell(_ command: String) -> Int32 { let task = Process() task.launchPath = "/bin/bash" task.arguments = ["-c", command] task.launch() task.waitUntilExit() return task.terminationStatus } @discardableResult func runShellWithArgs(_ args: String...) -> Int32 { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args task.launch() task.waitUntilExit() return task.terminationStatus }
使用如下:
runShell("pwd") runShell("ls -l") runShellWithArgs("pwd") runShellWithArgs("ls", "-l")
需要Shell命令的輸出內(nèi)容
這里就需要使用到Pipe了。
@discardableResult func runShellAndOutput(_ command: String) -> (Int32, String?) { let task = Process() task.launchPath = "/bin/bash" task.arguments = ["-c", command] let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8) task.waitUntilExit() return (task.terminationStatus, output) } @discardableResult func runShellWithArgsAndOutput(_ args: String...) -> (Int32, String?) { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8) task.waitUntilExit() return (task.terminationStatus, output) }
使用如下:
let (ret1, output1) = runShellAndOutput("ls -l") if let output11 = output1 { print(output11) } let (ret2, output2) = runShellWithArgsAndOutput("ls", "-l") if let output22 = output2 { print(output2) }
如何解析Matrix的OOM Log
Matrix的OOM Log格式如下,其實就是一個大JSON:
{ "head": { "protocol_ver": 1, "phone": "iPhone10,1", "os_ver": "13.4", "launch_time": 1589361495000, "report_time": 1589362109100, "app_uuid": "" }, "items": [ { "tag": "iOS_MemStat", "info": "", "scene": "", "name": "Malloc 12.54 MiB", "size": 146313216, "count": 1, "stacks": [ { "caller": "f07199ac8a903127b17f0a906ffb0237@84128", "size": 146313216, "count": 1, "frames": [ { "uuid": "a0a7d67af0f3399a8f006f92716d8e6f", "offset": 67308 }, { "uuid": "a0a7d67af0f3399a8f006f92716d8e6f", "offset": 69836 }, { "uuid": "f07199ac8a903127b17f0a906ffb0237", "offset": 84128 }, { "uuid": "b80198f7beb93e79b25c7a27d68bb489", "offset": 14934312 }, { "uuid": "1a46239df2fc34b695bc9f38869f0c85", "offset": 1126304 }, { "uuid": "1a46239df2fc34b695bc9f38869f0c85", "offset": 123584 }, { "uuid": "1a46239df2fc34b695bc9f38869f0c85", "offset": 1135100 }] } ] } ] }
解析的思路其實非常簡單,將JSON轉(zhuǎn)為Model,然后根據(jù)所需,提取對應(yīng)的信息即可。
uuid是mach-o的唯一標(biāo)識,offset則是符號相對于mach-o基地址的偏移量。拿到dSYM文件,使用 atos 命令即可進行符號化。
guard let rawLogModel = MatrixOOMLogParser.parse() else { exit(-1) } print("______ Start to process Matrix OOM Log ...") let group = DispatchGroup() var metaLog = "" for item in bodyInfo.items { guard let stacks = item.stacks else { continue } group.enter() DispatchQueue.global().async { var log = "______ item ______ name: \(item.name), size: \(item.size), count: \(item.count) \n" metaLog += log for stack in stacks { let outputs = stack.frames.map({ (frame: MatrixOOMLogModelFrame) -> String in // let uuid = frame.uuid let offset = frame.offset let instructionAddress = loadAddress + offset let (_, output) = runShellAndOutput("xcrun atos -o \(dwarf) -arch arm64 -l 0x1 \(instructionAddress.hexValue)") return output ?? "" }) log += outputs.joined() print(log) } group.leave() } } group.wait() print("\n\(metaLog)\n") print("______ Finished processing Matrix OOM Log ...")
MatrixOOMLogParser.parse() 就是將JSON轉(zhuǎn)為Model,這里用的就是Swift里邊的Codable。
這里有一個需要注意的點,Mac CLI沒有Bundle的概念,只有一個bin文件。所以對于原始的JSON文件,只能通過外部bundle的方式來添加。通過 New->Target 單獨建立一個bundle。需要在 Xcode -> Build Phases -> Copy Files 中添加該bundle名,然后即可通過 Bundle(url: mockDataBundleURL) 來加載該bundle并獲取其中的log文件了。
因為atos的執(zhí)行時間較長,所以大量的符號化操作會非常耗時。一般來說,這段代碼執(zhí)行六七分鐘左右,可以將一個Matrix的OOM Log完全符號化。而符號化之后的記錄如何分析,就是另外一個話題了。
參考資料
How do I run an terminal command in a swift script? (e.g. xcodebuild)
到此這篇關(guān)于如何使用Swift來實現(xiàn)一個命令行工具的文章就介紹到這了,更多相關(guān)Swift 命令行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Objective-c代碼如何移植為Swift代碼 Objective-c代碼轉(zhuǎn)移到Swift過程介紹
這篇文章主要介紹了Objective-c代碼如何移植為Swift代碼,Objective-c代碼轉(zhuǎn)移到Swift過程介紹,需要的朋友可以參考下2014-07-07swift實現(xiàn)顏色漸變以及轉(zhuǎn)換動畫
這篇文章主要為大家詳細介紹了swift實現(xiàn)顏色漸變以及轉(zhuǎn)換動畫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01Swift中實現(xiàn)點擊、雙擊、捏、旋轉(zhuǎn)、拖動、劃動、長按手勢的類和方法介紹
這篇文章主要介紹了Swift中實現(xiàn)點擊、雙擊、捏、旋轉(zhuǎn)、拖動、劃動、長按手勢的類和方法介紹,本文分別給出了各種手勢的實現(xiàn)代碼,需要的朋友可以參考下2015-01-01