簡體   English   中英

在Databricks(DBFS)中遞歸列出目錄和子目錄的文件

[英]list the files of a directory and subdirectory recursively in Databricks(DBFS)

使用python/dbutils,如何在Databricks文件系統(DBFS)中遞歸顯示當前目錄和子目錄的文件。

關於 dbutils.fs.ls(和 %fs 魔法命令)的令人驚訝的事情是它似乎不支持任何遞歸開關。 但是,由於 ls 函數返回一個 FileInfo 對象列表,因此遞歸迭代它們以獲取整個內容非常簡單,例如:

def get_dir_content(ls_path):
  dir_paths = dbutils.fs.ls(ls_path)
  subdir_paths = [get_dir_content(p.path) for p in dir_paths if p.isDir() and p.path != ls_path]
  flat_subdir_paths = [p for subdir in subdir_paths for p in subdir]
  return list(map(lambda p: p.path, dir_paths)) + flat_subdir_paths
    

paths = get_dir_content('/databricks-datasets/COVID/CORD-19/2020-03-13')
[print(p) for p in paths]

可以使用生成器和yield運算符完成另一種實現。 您必須至少使用Python 3.3+來獲取yield from運算符的yield from並查看這篇很棒的文章以更好地理解yield運算符:

def get_dir_content(ls_path):
    for dir_path in dbutils.fs.ls(ls_path):
        if dir_path.isFile():
            yield dir_path.path
        elif dir_path.isDir() and ls_path != dir_path.path:
            yield from get_dir_content(dir_path.path)
    
list(get_dir_content('/databricks-datasets/COVID/CORD-19/2020-03-13'))

這里列出了其他答案,但值得注意的是,databricks 將數據集存儲為文件夾。

例如,您可能有一個名為my_dataset_here的“目錄”,其中包含如下文件:

my_dataset_here/part-00193-111-c845-4ce6-8714-123-c000.snappy.parquet
my_dataset_here/part-00193-123-c845-4ce6-8714-123-c000.snappy.parquet
my_dataset_here/part-00193-222-c845-4ce6-8714-123-c000.snappy.parquet
my_dataset_here/part-00193-444-c845-4ce6-8714-123-c000.snappy.parquet
...

在一組典型的表中將有數千個這樣的文件

嘗試枚舉此類文件夾中的每個文件可能需要很長時間……比如幾分鍾,因為對dbutils.fs.ls的單次調用必須返回每個結果的數組。

因此,一種幼稚的方法,例如:

stack = ["/databricks-datasets/COVID/CORD-19/2020-03-13"]
while len(stack) > 0:
  current_folder = stack.pop(0)
  for file in dbutils.fs.ls(current_folder):
    if file.isDir():
      stack.append(file.path)
      print(file.path)
    else:
      print(file.path)

確實會列出每個文件,但也需要很長時間才能完成。 在我的測試環境中,枚舉 50 多個表需要8 分鍾

但是,如果使用新的“delta”格式,則會在 delta 表文件夾中創建一個名為“_delta_log”的標准命名文件夾。

因此,在嘗試枚舉文件夾的全部內容之前,我們可以修改我們的代碼以檢查每個文件夾以查看它是否是一個數據集:

stack = ["/databricks-datasets/COVID/CORD-19/2020-03-13"]
while len(stack) > 0:
  current_folder = stack.pop(0)
  for file in dbutils.fs.ls(current_folder):
    if file.isDir():
      # Check if this is a delta table and do not recurse if so!
      try:
        delta_check_path = f"{file.path}/_delta_log"
        dbutils.fs.ls(delta_check_path)  # raises an exception if missing
        print(f"dataset: {file.path}")
      except:            
        stack.append(file.path)
        print(f"folder: {file.path}")
    else:
        print(f"file: {file.path}")

此代碼在38 秒內在相同的測試環境中運行。

在瑣碎的情況下,天真的解決方案是可以接受的,但在現實世界的情況下,它很快就會變得完全不可接受。

請注意,此代碼僅適用於增量表; 如果您使用的是 parquet/csv/任何格式,那么您就不走運了。

你也可以試試這個遞歸函數:

def lsR(path):
  return([fname for flist in [([fi.path] if fi.isFile() else lsR(fi.path)) for fi in dbutils.fs.ls(path)] for fname in flist])
                
lsR('/your/folder')
def list_all_directories_recursively_from(path:str, directories_found = []) -> list:
  files = dbutils.fs.ls(path)
  if len(files) == 0:
    return
  for file in files:
    if file.isDir():
      directories_found.append(file.path)
      list_directories(file.path, directories_found)
  return directories_found


I've use this one to list all directories from: "dbfs:/mont/data-driven/1-common/cf-/table_name/data" 

and it returns:

['dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=7/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=7/day=1/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=7/day=1/hour=8/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=25/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=25/hour=8/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=26/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=26/hour=7/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=26/hour=8/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=29/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=29/hour=8/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=30/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=8/day=30/hour=8/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=9/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=9/day=1/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=9/day=1/hour=6/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=9/day=7/',
 'dbfs:/mont/data-driven-/1-common/cf-/table_name/data/year=2022/month=9/day=7/hour=8/']

the deepest directory is on an hourly level

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM