开发者

Get containing path of lua file

I am wondering if there is a way of getting the path to the currently executing lua script file?

This is specifically not the current working directory, which could be entirely different. I know luafilesystem will let me get the current working directory, but it doesn't seem to b开发者_开发技巧e able to tell the current executing script file.

Thanks

EDIT: I'm not running from the standard command line interpreter, I am executing the scripts from a C++ binary via luabind.


This is a more elegant way:

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

print(script_path())


If the Lua script is being run by the standard command line interpreter, then try arg[0].


Shortest form which I have found looks like this:

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

Index 1, 2- other - depends on which function in call stack you want to query. 1 is last called function (where you're in). If you're running in global context, then probably 2 is more appropriate (haven't tested by myself)


As lhf says:

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

Here's the same info using the debug.getinfo mechanism

~ 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$ 

This is available from the C API lua_getinfo


The only reliable way to get what you want is to replace dofile with your own version of this function. Even the debug.getinfo method won't work, because it will only return the string passed to dofile. If that was a relative path, it has no idea how it was converted to an absolute path.

The overriding code would look something like this:

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.

The functions extractFilePath, isRelativePath, and currentDir are not Lua functions; you will have to write them yourself. The extractFilePath function pulls a path string out of a filename. isRelativePath takes a path and returns whether the given path is a relative pathname. currentDir simply returns the current directory. Also, you will need to use "\" instead of "/" on Windows machines.

This function stores the path in a global called path. You can change that to whatever you like.


I have written a function getScriptDir which uses the debug information like a few other people have suggested, but this one is going to work everytime (at least in Windows). But the thing is there are quite a few lines of code as it uses another function string.cut which i have created, which separates a string every given pattern, and puts it into a table.

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

Note: if you want to use this function in another OS than Windows, you have to change the io.popen("echo %cd%") in the line 15 to whatever command gives you present working directory in your OS, e.g. io.popen("pwd") for Linux, and the pwd2:sub(2,3) == ":/" in the line 18 to whatever represents the root directory in your OS, e.g. pwd2:sub(1,1) == "/" for Linux.

Note2: if you don't provide the source variable to the function via debug.getinfo(1).source when calling it, then it will return the path to the directory of the file containing this function. Therefore, if you want to get the directory of a file which you called via dofile or loadfile, you will have to give it the source, like this: getScriptDir(debug.getinfo(1).source).


Have a look at the Lua debug library, which is part of the standard Lua distribution. You can use debug.getinfo to find the current file, or the file up N frames on the call stack:

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

Note that this is probably fairly slow, so it is not something you want to do on the fast path if you are worried about such things.


if you want the actual path :

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

else use this for full file path :

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


If you want the real path including the filename, just use the following

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

Tested on Windows.

Explanation:

io.popen - Sends commands to the command line, and returns the output.

"cd" - when you input this in cmd you get the current path as output.

:read'*all' - as io.popen returns a file-like object you can read it with the same kind of commands. This reads the whole output.


If someone requires the UNC path:

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


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

If it returns nil try changing the .\*\\\ with .*/ and arg[0] with debug.getinfo(1).short_src.

But I find this to be the best and shortest way to get the current directory.

You can of course append the file you are looking for with the .. operator. It will look something like this:

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


This version of anthonygore's answer is cross-platform (handles Windows backslash paths) and works when the script is run without a path (returns relative path).

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜