使用Python程式碼實現Linux中的ls遍歷目錄命令的例項程式碼
一、寫在前面
前幾天在微信上看到這樣一篇文章,連結為:https://www.jb51.net/it/692145.html,在這篇文章中,有這樣一段話,吸引了我的注意:
在 Linux 中 ls 是一個使用頻率非常高的命令了,可選的引數也有很多, 算是一條不得不掌握的命令。Python 作為一門簡單易學的語言,被很多人認為是不需要認真學的,或者只是隨便調個庫就行了,那可就真是小瞧 Python 了。那這次我就要試著用 Python 來實現一下 Linux 中的 ls 命令, 小小地證明下 Python 的不簡單!
二、ls簡介
Linux ls 命令用於顯示指定工作目錄下的內容。語法如下:
ls [-alkrt] [name]
這裡只列舉了幾個常用的引數,ls 命令的可選引數還是很多的,可以使用 man ls 來進行檢視具體資訊。這裡列出的幾個引數對應含義如下:
1)-a:顯示所有檔案及目錄;
2)-l:除檔名稱外,亦將檔案大小、建立時間等資訊列出;
3)-k:將檔案大小以 KB 形式表示;
4)-r:將檔案以相反次序排列;
5)-t:將檔案以修改時間次序排列。
三、具體思路
主要使用的模組是 argparse 和 os,其中 argparse 模組能設定和接收命令列引數,也就使得 Python 對命令列的操作變得簡單,而 os 模組則用於檔案操作,對 argparse 模組不熟悉的可以在這裡檢視官方文件。
既然要用 Python 實現 ls.py, 也就要在命令列中進行操作,比如 python ls.py -a 這樣的命令,而對 Python 比較熟悉的人可能會想到使用 sys 模組來接收輸入的命令,但使用 argparse 能讓命令列操作變得更加簡單!首先要匯入模組並建立一個 ArgumentParser 物件,可以理解為一個解析器,然後就可以通過使用 add_argument() 方法為這個解析器新增引數了。示例如下:
# test.py import argparse parser = argparse.ArgumentParser(description='Find the maximum number.') parser.add_argument("integers",type=int,nargs="+",help="The input integers.") parser.add_argument("-min",nargs="?",required=False,dest="find_num",default=max,const=min,help="Find the minimum number(Default: find the maximum number).") args = parser.parse_args() print(args) print(args.find_num(args.Nums))
這段程式碼的功能是輸入一到多個整數,預設求其中的最大值,若有 -min 引數則是求其中的最小值。可以看到在建立解析器和新增命令列引數的時候都設定了 description 描述資訊,這個資訊會在我們使用 --help 命令的時候顯示出來,例如:
在上面的程式碼中,需要注意的是其中使用 add_argument() 添加了一個位置引數 "integers" 和一個可選引數 "-min",位置引數在命令列中必須存在,不可遺漏,也就不能設定 required 引數了,而可選引數就不是必須要有的了,因而還可以使用 default 引數設定預設值。nargs 引數用於設定命令列引數的數量,"+" 表示一個或多個,"?" 表示零個或一個,這裡由於輸入的數字可能有多個,所以要設定為 "+"。最終執行示例如下:
> python test.py 1 3 5
Namespace(find_num=<built-in function max>,integers=[1,3,5])
5> python test.py 1 3 5 -min
Namespace(find_num=<built-in function min>,5])
1
關於 argparse 的介紹就到此為止了,下面簡單介紹下 os 模組, os 模組提供了便捷的使用作業系統相關功能的方式,實現 ls.py 所用到的該模組下的方法包括:
1)os.path.isdir(path):若 path 是一個存在的目錄,返回 True。
2)os.listdir(path):返回一個列表,其中包括 path 對應的目錄下的內容,不包含“.”和“..”,即使它們存在。
3)os.stat(path):獲取檔案或檔案描述符的狀態,返回一個 stat_result 物件,其中包含了各種狀態資訊。
四、主要程式碼
ls.py 中的主函式如下,主要功能為建立解析器,設定可選引數和位置引數,然後接收命令列引數資訊,並根據輸入的引數呼叫相應的方法,這裡設定了一個 "-V" 引數用於顯示版本資訊,可以使用 "-V" 或者 "-Version" 進行檢視。
def main(): """ 主函式,設定和接收命令列引數,並根據引數呼叫相應方法 :return: """ # 建立解析器 parse = argparse.ArgumentParser(description="Python_ls") # 可選引數 parse.add_argument("-a","-all",help="Show all files",action="store_true",required=False) parse.add_argument("-l","-long",help="View in long format",required=False) parse.add_argument("-k",help="Expressed in bytes",required=False) parse.add_argument("-r","-reverse",help="In reverse order",required=False) parse.add_argument("-t",help="Sort by modified time",required=False) parse.add_argument("-V","-Version",help="Get the version",required=False) # 位置引數 parse.add_argument("path",type=str,help="The path",nargs="?") # 命令列引數資訊 data = vars(parse.parse_args()) assert type(data) == dict if data["V"]: print("Python_ls version: 1.0") return else: check_arg(data)
然後是一個獲取指定路徑下的內容資訊的函式,要做的就是判斷路徑是否存在,若存在就返回一個檔案列表,若不存在則顯示錯誤資訊,並退出程式。
def get_all(path): """ 獲取指定路徑下的全部內容 :param path: 路徑 :return: """ if os.path.isdir(path): files = [".",".."] + os.listdir(path) return files else: print("No such file or directory") exit()
五、執行結果
下面是 ls.py 執行後的部分結果截圖。
首先是 python ls.py -a,這裡並沒有輸入路徑,就會使用預設路徑即當前目錄,如下圖:
然後是 python ls.py -a -t .,使用該命令會顯示當前目錄下的所有內容,並按照建立的時間進行排序,如下圖:
最後是 python ls.py -a -l -k -r .,也是顯示當前目錄下的所有內容並按照建立名稱排序,不過這次檔案大小會以 KB 為單位來顯示,如下圖:
到這裡為止,ls.py 就算是基本實現了,當然還是有很多可以去實現的功能的,比如更多的引數等等,如果你感興趣的話可以自己嘗試一下==
完整python程式碼
""" Version: Python3.7 Author: OniOn Site: http://www.cnblogs.com/TM0831/ Time: 2019/9/6 21:41 """ import os import time import argparse import terminaltables def get(path): """ 獲取指定路徑下的內容 :param path: 路徑 :return: """ if os.path.isdir(path): # 判斷是否是真實路徑 files = os.listdir(path) return files else: print("No such file or directory") exit() def get_all(path): """ 獲取指定路徑下的全部內容 :param path: 路徑 :return: """ if os.path.isdir(path): files = [".",".."] + os.listdir(path) return files else: print("No such file or directory") exit() def check_arg(data): """ 檢查引數資訊 :param data: 命令列引數(dict) :return: """ assert type(data) == dict if not data["path"]: data["path"] = "." # a引數 if data["a"]: files = get_all(data["path"]) else: files = get(data["path"]) # r引數 if data["r"]: files = files[::-1] # t引數 if data["t"]: files = sorted(files,key=lambda x: os.stat(x).st_mtime) for i in range(len(files)): files[i] = [files[i],time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(os.stat(files[i]).st_mtime))] # l引數 if data["l"]: result = [] for i in range(len(files)): file = files[i][0] if data["t"] else files[i] # 獲取檔案資訊 file_info = os.stat(file) # k引數 if data["k"]: # 格式化時間,檔案大小用KB表示 result.append([file,time.localtime(file_info.st_ctime)),"%.3f KB" % (file_info.st_size / 1024)]) else: # 格式化時間,檔案大小用B表示 result.append([file,"{} Byte".format(file_info.st_size)]) if data["t"]: for i in result: i.append(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(os.stat(i[0]).st_mtime))) show_file(result,True,True) if data["t"] else show_file(result,False) return show_file(files,False,True) if data["t"] else show_file(files,False) def show_file(files,has_l,has_t): """ 格式化輸出檔案資訊 :param files: 檔案列表 :param has_l: 是否有l引數 :param has_t: 是否有t引數 :return: """ # 根據引數資訊設定表頭 if not has_l: if not has_t: table_data = [["ID","FILE_NAME"]] for i in range(len(files)): table_data.append([i + 1,files[i]]) else: table_data = [["ID","FILE_NAME","FILE_MTIME"]] for i in range(len(files)): table_data.append([i + 1] + files[i]) else: if not has_t: table_data = [["ID","FILE_CTIME","FILE_SIZE"]] else: table_data = [["ID","FILE_SIZE","FILE_MTIME"]] for i in range(len(files)): table_data.append([i + 1] + files[i]) # 建立AsciiTable物件 table = terminaltables.AsciiTable(table_data) # 設定標題 table.title = "file table" for i in range(len(table.column_widths)): if i != 1: # 居中顯示 table.justify_columns[i] = "center" print(table.table) def main(): """ 主函式,設定和接收命令列引數,並根據引數呼叫相應方法 :return: """ # 建立解析器 parse = argparse.ArgumentParser(description="Python_ls") # 可選引數 parse.add_argument("-a",required=False) parse.add_argument("-l",required=False) parse.add_argument("-k",required=False) parse.add_argument("-r",required=False) parse.add_argument("-t",required=False) parse.add_argument("-V",required=False) # 位置引數 parse.add_argument("path",nargs="?") # 命令列引數資訊 data = vars(parse.parse_args()) assert type(data) == dict if data["V"]: print("Python_ls version: 1.0") return else: check_arg(data) if __name__ == '__main__': main()
完整程式碼已上傳到GitHub!