获取 Lua 文件的包含路径

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

这个问题特别不是当前工作目录,可能完全不同。我知道 luafilesystem 可以让我获取当前工作目录,但它似乎不能告诉我当前正在执行的脚本文件。

谢谢

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

原文链接 https://stackoverflow.com/questions/6380820

点赞
stackoverflow用户107090
stackoverflow用户107090

如果Lua脚本是由标准命令行解释器运行的,则尝试使用 arg[0]

2011-06-17 02:46:13
stackoverflow用户33252
stackoverflow用户33252

如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]中获得(http://www.lua.org/manual/5.1/manual.html#lua_getinfo)

2011-06-17 02:47:45
stackoverflow用户776473
stackoverflow用户776473

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

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

请注意,这可能相当慢,因此如果您担心这些事情,这不是您想在快速路径上做的事情。

2011-06-17 02:58:47
stackoverflow用户734069
stackoverflow用户734069

唯一可靠的方法是将 dofile 替换为这个函数的自定义版本才能得到想要的结果。即使使用 debug.getinfo 方法也不行,因为它只会返回传递给 dofile 的字符串。如果它是相对路径,那么它不知道它是如何被转换为绝对路径的。

重载代码应该如下:

local function CreateDoFile()
    local orgDoFile = dofile;

    return function(filename)
        if(filename) then --可以使用 nil 调用。
            local pathToFile = extractFilePath(filename);
            if(isRelativePath(pathToFile)) then
                pathToFile = currentDir() .. "/" .. pathToFile;
            end

            --将路径存储在全局变量中,覆盖以前的值。
            path = pathToFile;
        end
        return orgDoFile(filename); --正确的尾调用。
    end
end

dofile = CreateDoFile(); //覆盖旧的。

函数 extractFilePath, isRelativePath, 和 currentDir 都不是 Lua 函数;你需要自己编写它们。 extractFilePath 函数从文件名中提取一个路径字符串。isRelativePath 接受一个路径并返回给定路径是否为相对路径名。 currentDir 简单地返回当前目录。此外,在 Windows 机器上需要使用 "\" 而不是 "/"。

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

2011-06-17 04:51:13
stackoverflow用户2278963
stackoverflow用户2278963

这是一种更优雅的方法:

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

print(script_path())
2014-05-08 07:24:27
stackoverflow用户2338477
stackoverflow用户2338477

我找到的最短形式看起来像这样:

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

索引1,2- 其他- 取决于您要查询哪个调用堆栈中的函数。 1是最后调用的函数(您所在的函数)。如果您在全局上下文中运行,则可能更适合使用2(我还没有自己测试过)。

2016-01-28 21:11:30
stackoverflow用户5057507
stackoverflow用户5057507
arg[0]:match('.*\\')

如果返回值为 nil,则尝试使用 .*/ 替换 .\*\\\,并将 arg[0] 替换为 debug.getinfo(1).short_src

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

当然,您可以使用 .. 运算符将要查找的文件附加到当前目录。它将如下所示:

arg[0]:match('.*\\')..'file.lua'
2016-08-28 11:32:28
stackoverflow用户3290872
stackoverflow用户3290872

如果你想要实际路径:

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

否则,使用此选项获取完整文件路径:

path.abspath(debug.getinfo(1).short_src)
2017-09-24 12:49:15
stackoverflow用户3507506
stackoverflow用户3507506

如果希望获得包括文件名在内的真实路径,只需要使用以下代码

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
2017-10-20 12:14:43
stackoverflow用户5075715
stackoverflow用户5075715

我写了一个函数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%")更改为给您的操作系统提供当前工作目录的命令,例如Linux的io.popen("pwd"),并将第18行pwd2:sub(2,3) == ":/"更改为您的操作系统中表示根目录的任何内容,例如Linux的pwd2:sub(1,1) == "/"

注意2:如果您在调用函数时没有通过debug.getinfo(1).source提供source变量给函数,那么它将返回包含该函数的文件的目录路径。因此,如果您想获取通过dofileloadfile调用的文件的目录路径,则必须将源代码作为参数传递给它,例如:getScriptDir(debug.getinfo(1).source)

2017-12-05 07:20:53
stackoverflow用户79125
stackoverflow用户79125

这个版本的 anthonygore 的回答 是跨平台的(能处理 Windows 反斜杠路径),并且在没有路径时运行脚本(返回相对路径)。

local function script_path()
    local str = debug.getinfo(2, "S").source:sub(2)
    return str:match("(.*[/\\])") or "./"
end
2022-12-02 20:53:47