1. 程式人生 > 程式設計 >使用Python程式碼實現Linux中的ls遍歷目錄命令的例項程式碼

使用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!