【问题标题】:Get containing path of lua file获取包含lua文件的路径
【发布时间】:2011-09-16 20:56:30
【问题描述】:

我想知道是否有办法获取当前正在执行的 lua 脚本文件的路径?

这不是当前的工作目录,可能完全不同。我知道luafilesystem会让我得到当前的工作目录,但它似乎无法告诉当前正在执行的脚本文件。

谢谢

编辑: 我不是从标准命令行解释器运行的,而是通过luabind 从 C++ 二进制文件执行脚本。

【问题讨论】:

    标签: filesystems lua


    【解决方案1】:

    这是一种更优雅的方式:

    function script_path()
       local str = debug.getinfo(2, "S").source:sub(2)
       return str:match("(.*/)")
    end
    
    print(script_path())
    

    【讨论】:

    • 如果脚本在没有路径的情况下运行,例如lua myscript(其中lua假设这意味着./myscript),该函数返回nil。我建议使用return str:match("(.*/)") or "."
    • 要在 Windows 上进行这项工作,请将 str:match("(.*/)") 替换为 str:match("(.*[/\\])")
    【解决方案2】:

    如果 Lua 脚本正在由标准命令行解释器运行,请尝试 arg[0]。

    【讨论】:

    • 如果我在原文件中做了dofile(subfile),这种方法是否可以获取子文件的路径?
    • @radman:不。此外,dofile 使用它获得的任何路径,因此相对路径是相对于进程的当前工作目录的。如果你想跟踪给dofile的文件,你可以重新定义它。
    • 并且用户也可以使用 loadfile 然后将加载的文件作为函数调用运行,您可能还需要重新定义 loadfile
    • 不幸的是,这在 Lua 5.3 中不起作用。主块的... 仅返回与arg[1] 等价的值。
    • @HishamHM,谁说过...? arg[0] 确实适用于 Lua 5.3。
    【解决方案3】:

    正如 lhf 所说:

    ~ e$ echo "print(arg[0])" > test.lua
    ~ e$ lua test.lua
    test.lua
    ~ e$ cd /
    / e$ lua ~/test.lua
    /Users/e/test.lua
    / e$ 
    

    这是使用 debug.getinfo 机制的相同信息

    ~ e$ echo "function foo () print(debug.getinfo(1).source) end; foo()" > test.lua
    ~ e$ lua test.lua
    @test.lua
    ~ e$ cd /
    / e$ lua ~/test.lua
    @/Users/e/test.lua
    / e$ 
    

    这可从 C API lua_getinfo 获得

    【讨论】:

      【解决方案4】:

      我发现的最短形式如下:

      debug.getinfo(1).source:match("@?(.*/)")
      

      索引 1、2- 其他 - 取决于您要查询的调用堆栈中的哪个函数。 1 是最后一次调用的函数(您所在的位置)。如果您在全局环境中运行,那么可能 2 更合适(我自己没有测试过)

      【讨论】:

      • arg[0]:match("@?(.*/)") 适合我从控制台运行。
      【解决方案5】:

      获得所需内容的唯一可靠方法是将dofile 替换为您自己版本的此函数。即使debug.getinfo 方法也不起作用,因为它只会返回传递给dofile 的字符串。如果那是相对路径,它不知道它是如何转换为绝对路径的。

      覆盖代码如下所示:

      local function CreateDoFile()
          local orgDoFile = dofile;
      
          return function(filename)
              if(filename) then --can be called with nil.
                  local pathToFile = extractFilePath(filename);
                  if(isRelativePath(pathToFile)) then
                      pathToFile = currentDir() .. "/" .. pathToFile;
                  end
      
                  --Store the path in a global, overwriting the previous value.
                  path = pathToFile; 
              end
              return orgDoFile(filename); --proper tail call.
          end
      end
      
      dofile = CreateDoFile(); //Override the old.
      

      函数extractFilePath、isRelativePath和currentDir不是Lua函数;你必须自己写。 extractFilePath 函数从文件名中提取路径字符串。 isRelativePath 接受路径并返回给定路径是否为相对路径名。 currentDir 只是返回当前目录。此外,您需要在 Windows 机器上使用“\”而不是“/”。

      此函数将路径存储在名为path 的全局变量中。您可以将其更改为您喜欢的任何内容。

      【讨论】:

      • fyi,您也可以完美地将/ 用于 Windows 路径。
      • 加载文件也应该被覆盖
      【解决方案6】:

      我编写了一个函数getScriptDir,它使用了其他一些人建议的调试信息,但是这个函数每次都可以工作(至少在 Windows 中)。但问题是有很多代码行,因为它使用了我创建的另一个函数string.cut,它将每个给定模式分隔一个字符串,并将其放入一个表中。

      function string.cut(s,pattern)
        if pattern == nil then pattern = " " end
        local cutstring = {}
        local i1 = 0
        repeat
          i2 = nil
          local i2 = string.find(s,pattern,i1+1)
          if i2 == nil then i2 = string.len(s)+1 end
          table.insert(cutstring,string.sub(s,i1+1,i2-1))
          i1 = i2
        until i2 == string.len(s)+1
        return cutstring
      end
      
      function getScriptDir(source)
        if source == nil then
          source = debug.getinfo(1).source
        end
        local pwd1 = (io.popen("echo %cd%"):read("*l")):gsub("\\","/")
        local pwd2 = source:sub(2):gsub("\\","/")
        local pwd = ""
        if pwd2:sub(2,3) == ":/" then
          pwd = pwd2:sub(1,pwd2:find("[^/]*%.lua")-1)
        else
          local path1 = string.cut(pwd1:sub(4),"/")
          local path2 = string.cut(pwd2,"/")
          for i = 1,#path2-1 do
            if path2[i] == ".." then
              table.remove(path1)
            else
              table.insert(path1,path2[i])
            end
          end
          pwd = pwd1:sub(1,3)
          for i = 1,#path1 do
            pwd = pwd..path1[i].."/"
          end
        end
        return pwd
      end
      

      注意:如果您想在 Windows 以外的其他操作系统中使用此功能,您必须将 第 15 行 中的 io.popen("echo %cd%") 更改为您提供的任何命令操作系统中的工作目录,例如io.popen("pwd") 用于 Linux,第 18 行 中的 pwd2:sub(2,3) == ":/" 表示操作系统中的根目录,例如pwd2:sub(1,1) == "/" 用于 Linux。

      注意2:如果你在调用函数时没有通过debug.getinfo(1).source将source变量提供给函数,那么它将返回文件所在目录的路径这个函数。因此,如果您想获取通过dofile 或loadfile 调用的文件的目录,则必须为其提供源,如下所示:getScriptDir(debug.getinfo(1).source)。

      【讨论】:

        【解决方案7】:

        查看 Lua 调试库,它是标准 Lua 发行版的一部分。您可以使用 debug.getinfo 查找当前文件,或调用堆栈上 N 帧的文件:

        http://www.lua.org/manual/5.1/manual.html#5.9

        请注意,这可能相当慢,因此如果您担心此类事情,则不希望在快速路径上执行此操作。

        【讨论】:

        • 感谢您的想法,但我认为这对我来说太肮脏了;)
        【解决方案8】:

        如果你想要实际路径:

        path.dirname(path.abspath(debug.getinfo(1).short_src))
        

        否则将其用于完整文件路径:

        path.abspath(debug.getinfo(1).short_src)
        

        【讨论】:

          【解决方案9】:

          如果你想要包含文件名的真实路径,只需使用以下

          pathWithFilename=io.popen("cd"):read'*all'
          print(pathWithFilename)
          

          在 Windows 上测试。

          说明:

          io.popen - 将命令发送到命令行,并返回输出。

          "cd" - 当您在cmd 中输入此内容时,您将获得当前路径作为输出。

          :read'*all' - 因为 io.popen 返回一个类似文件的对象,您可以使用相同类型的命令读取它。这会读取整个输出。


          如果有人需要 UNC 路径:

          function GetUNCPath(path,filename)
          local DriveLetter=io.popen("cd "..path.." && echo %CD:~0,2%"):read'*l'
          local NetPath=io.popen("net use "..DriveLetter):read'*all'
          local NetRoot=NetPath:match("[^\n]*[\n]%a*%s*([%a*%p*]*)")
          local PathTMP=io.popen("cd "..path.." && cd"):read'*l'
          PathTMP=PathTMP:sub(3,-1)
          UNCPath=NetRoot..PathTMP.."\\"..filename
          return UNCPath
          end
          

          【讨论】:

            【解决方案10】:
            arg[0]:match('.*\\')
            

            如果它返回 nil,请尝试将 .\*\\\ 更改为 .*/ 并将 arg[0] 更改为 debug.getinfo(1).short_src。

            但我发现这是获取当前目录的最佳和最短的方法。

            您当然可以使用.. 运算符附加您要查找的文件。它看起来像这样:

            arg[0]:match('.*\\')..'file.lua'
            

            【讨论】:

              猜你喜欢
              • 2021-04-03
              • 2015-11-01
              • 2011-12-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-02-20
              • 1970-01-01
              • 2010-10-21
              相关资源
              最近更新 更多