如何在Lua中获取目录列表

37

我需要一个LUA目录列表

假设我有一个目录路径为"C:\Program Files"

我需要列出该特定路径下的所有文件夹,并且知道如何在该列表中搜索任何特定文件夹。

例如:

需要列出路径"C:\Program Files"下的所有文件夹

以下是上述路径中的文件夹名称:

  1. test123

  2. test4567

  3. folder 123

  4. folder 456

  5. folder 456 789

    需要将其列入列表,然后在folder 456 789文件夹中搜索特定字符串,如folder 456。

已尝试以下代码,但缺少某些内容:

local function Loc_Lines( str )
--
local ret= {}   -- 0 lines

while str do
    local _,_,line,tail= string.find( str, "(.-)\n(.+)" )
    table.insert( ret, line or str )
    str= tail
  Print (str)
end

return ret
end


local function Loc_ShellCommand( cmd )
--
local str= nil

    --
    local f= io.popen( cmd )    -- no command still returns a handle :(
     if f then

        str= f:read'*a'
    Print(str)
        f:close()
    end
    
    if str=="" then   -- take no output as a failure (we can't tell..)
    Print("hi")
        str= nil
    end
 
-- Remove terminating linefeed, if any (eases up one-line analysis)
--
if str then
    if string.sub( str, -1 ) == '\n' then
        str= string.sub( str, 1, -2 )
    end
end

return str
 end


 local function Loc_DirCmd( cmd )

 Print(cmd)

  local str= Loc_ShellCommand( cmd )



 return Loc_Lines(str)
 end


local function Loc_DirList( dirname )

 local ret= {}
  
    local lookup= {}

   local tbl= Loc_DirCmd( "dir /AD /B "..dirname )   -- only dirs

    -- Add slash to every dir line
    --
    for i,v in ipairs(tbl) do
        table.insert( ret, v..'\\' )
        lookup[v]= true
    end       

    
    -- Return with forward slashes
    --
    if true then
        for i=1,table.getn(ret) do
            ret[i]= string.gsub( ret[i], '\\', '/' )
     Print (ret[i])
        end
    end
  

   return ret
 end


 Loc_DirList("C:\\Program Files\\")

13年过去了... 那么... 你需要哪些操作系统? - user894319twitter
8个回答

63

我讨厌安装库(尤其是那些想让我使用安装程序包来安装它们的库)。如果您正在寻找一个干净的解决方案来列出Lua绝对路径上的目录列表,请不要再找了。

在sylvanaar提供的答案基础上,我创建了一个函数,该函数返回给定目录(需要绝对路径)中所有文件的数组。这是我首选的实现方法,因为它可以在我的所有机器上运行。

-- Lua implementation of PHP scandir function
function scandir(directory)
    local i, t, popen = 0, {}, io.popen
    local pfile = popen('ls -a "'..directory..'"')
    for filename in pfile:lines() do
        i = i + 1
        t[i] = filename
    end
    pfile:close()
    return t
end

如果您使用的是Windows系统,则需要安装Bash客户端,以便“ls”命令可以正常工作。或者,您可以使用Sylvanaar提供的dir命令。

'dir "'..directory..'" /b /ad'

3
请注意:您的代码会返回所有文件;给定的dir命令只会返回目录。要获取所有文件,请使用dir <路径> /b(省略/ad,该选项指定仅返回目录)。 - Mark
1
你可以使用包含lfs.h和lfs.c的lua库进行重新编译。这就是我安装lfs时所做的。 - RandyGaul
17
不要解析 ls - micheal65536
7
当目录名包含单引号(和其他可能的恶意字符)时,这种情况会非常糟糕。请记住,POSIX文件名只是字节字符串。 - Heinrich Hartmann
2
不要解析 ls 的输出。改用 find -maxdepth 1 -print0ls 的问题在于实现之间存在许多不一致性,而且很少有允许使用文件名不能包含的分隔符(它们可以包含换行符!),最常见的是空字节,但斜杠也可能有效。 - Solomon Ucko
显示剩余2条评论

34

走捷径,安装lfs。然后使用以下结构找到所需内容:

require'lfs'
for file in lfs.dir[[C:\Program Files]] do
    if lfs.attributes(file,"mode") == "file" then print("found file, "..file)
    elseif lfs.attributes(file,"mode")== "directory" then print("found dir, "..file," containing:")
        for l in lfs.dir("C:\\Program Files\\"..file) do
             print("",l)
        end
    end
end

请注意反斜杠等同于 [[\]] 等同于 "\\",在Windows系统中,/也可以使用,但不能用于命令提示符本身(如果我有错误,请纠正我)。


1
为了让它对我起作用,我必须使用一个具有完全限定路径名的文件。也就是说,lfs.attributes("C:\Program Files\" .. file, "mode") 此外,您应该考虑在Linux系统上至少过滤掉“.”和“..”。 - Jay Elston

21
 for dir in io.popen([[dir "C:\Program Files\" /b /ad]]):lines() do print(dir) end

*适用于Windows操作系统

输出结果:

Adobe
Bitcasa
Bonjour
Business Objects
Common Files
DVD Maker
IIS
Internet Explorer
iPod
iTunes
Java
Microsoft Device Emulator
Microsoft Help Viewer
Microsoft IntelliPoint
Microsoft IntelliType Pro
Microsoft Office
Microsoft SDKs
Microsoft Security Client
Microsoft SQL Server
Microsoft SQL Server Compact Edition
Microsoft Sync Framework
Microsoft Synchronization Services
Microsoft Visual Studio 10.0
Microsoft Visual Studio 9.0
Microsoft.NET
MSBuild
...

每次循环时,您都会获得一个新的文件夹名称。我选择将其打印作为示例。


3
注意:要获取文件列表,只需使用/b而不是/b /ad - RainingChain

18

我也不喜欢安装库,并且正在使用一台内存比PC小的嵌入式设备。我发现使用“ls”命令会导致内存溢出。因此,我创建了一个使用“find”解决问题的函数。

这样就可以保持内存使用稳定并循环所有的30k个文件。

function dirLookup(dir)
   local p = io.popen('find "'..dir..'" -type f')  --Open directory look for files, save data in p. By giving '-type f' as parameter, it returns all files.     
   for file in p:lines() do                         --Loop through all files
       print(file)       
   end
end

1
这似乎是最佳答案,没有使用任何库,它在Windows上能用吗? - Daniel Jordi
@DanielJordi 我不这么认为;在Windows中,命令find是grep的对应项,而不是文件查找器。 - Roman Shapovalov
2
该函数将递归遍历整个树,而不仅仅是列出目录。 - Roman Shapovalov
2
为了正确处理包含换行符的文件名,请使用 find ... -print0 并在空字节上分割。您可能还想添加 -maxdepth 1 以避免递归到子目录中。 - Solomon Ucko

3
不要解析ls,它很危险! 在Linux上使用带有零终止字符串的find代替:

最初的回答:

function scandir(directory)
    local i, t = 0, {}
    local pfile = assert(io.popen(("find '%s' -maxdepth 1 -print0"):format(directory), 'r'))
    local list = pfile:read('*a')
    pfile:close()
    for filename in s:gmatch('[^\0]+')
        i = i + 1
        t[i] = filename
    end
    return t
end

警告:然而,作为一个被接受的答案,这种方法可能会被利用,如果目录名中包含'。唯一安全的解决方案是使用lfs或其他特殊库。

最初的回答:


2
你还需要安装并使用 'paths' 模块。然后你可以按照以下步骤轻松完成:
require 'paths'

currentPath = paths.cwd() -- Current working directory
folderNames = {}
for folderName in paths.files(currentPath) do
    if folderName:find('$') then
        table.insert(folderNames, paths.concat(currentPath, folderName))
    end
end

print (folderNames)

-- 这将打印出所有文件夹的名称

你还可以通过将 fileName:find('$') 替换为 fileName:find('txt' .. '$') 来查找具有特定扩展名的文件名。

如果你正在运行Unix系统,可以使用以下代码获取一个按数字排序的文件列表:

thePath = '/home/Your_Directory'
local handle = assert(io.popen('ls -1v ' .. thePath)) 
local allFileNames = string.split(assert(handle:read('*a')), '\n')

print (allFileNames[1]) -- This will print the first file name

第二段代码还排除了像'.'和'..'这样的文件。所以它可以正常运行!

我发现 paths.files(currentPath) 返回的结果是未排序的。如何对它们进行排序?是否有类似于 for file in sort(paths.files(currentPath)) do 的代码? - C. Wang
@C.Wang 请再次检查我的答案。我已经更新了适合你任务的代码。 - Amir

2
据我所知,使用原始的Lua无法获取目录列表。您需要编写一些粘合代码,或者使用LuaFileSystem。后者可能是最容易的方法。快速扫描文档显示lfs.dir()将为您提供迭代器,您可以使用它来获取您要查找的目录。此时,您可以进行字符串比较以获取所需的特定目录,但请保留HTML标签。

请检查一下,似乎在获取列表时我漏掉了什么。 - che
你建议的东西让我感到疲惫,我可能漏掉了什么。 - che
公平的观点,我没有提到您可以使用io.popen()来运行shell。我认为您正在寻找一种通用的方法来完成此操作,而不是针对Windows的特定方式。我没有Windows设备来尝试这个功能。也许您可以更新问题并告诉我们您得到的结果以及您期望得到的结果。 - Glenn McAllister
@che - 从我所看到的情况来看,您似乎没有尝试我建议的方法,而是选择使用特定于平台的选项。正如我已经指出的那样,我没有安装Lua的Windows电脑可以为您跟踪问题,特别是因为您没有提供您得到的输出和期望的输出。 - Glenn McAllister
这就是我要找的答案,可以使用 require "lfs" 来完成。 hFile = io.popen('dir /a:d /b "C:\Program Files\"') line = hFile:read('*l') while (line ~= nil) do Print(line) line = hFile:read('*l') end - che

1

val表示恢复Monica的说法 的解决方案有一些修正:

function scandir(directory)
    local pfile = assert(io.popen(("find '%s' -mindepth 1 -maxdepth 1 -type d -printf '%%f\\0'"):format(directory), 'r'))
    local list = pfile:read('*a')
    pfile:close()

    local folders = {}

    for filename in string.gmatch(list, '[^%z]+') do
        table.insert(folders, filename)
    end

    return folders
end

现在它按文件夹过滤,排除文件夹本身并仅打印名称。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接