lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Tue, Jan 21, 2020 at 8:20 AM Hongyi Zhao <hongyi.zhao@gmail.com> wrote:
> Is there any robust and convenient methods for me to obtain the lua
> script's (absolute/real) dirname and file name from within the script
> itself?

TL;DR. You can just go to the last snippet of code.

The question could be split in the following:
1) What I mean for "Scirpt file"
2) How get info about where it is
3) How extract the absolute path
4) Split name and directory

The 1) seems obvious but in lua you can execute code from hard disk,
memory, you can get it from network or from a usb or rs232 device. So
let's just restrict to cases where "Script file" make sense:

a) The "Main script file", i.e. the one you pass to the lua standalone
interpreter from the command line
b) Any other file loaded by the main one through `require`
c) A string passed to `load` with a third parameter that is a proper
file reference

These are all the ways you have to actually execute lua code. Note
that an embedding application that calls lua, falls in the case c) as
well as a lua C modules calling lua code.

The info 2) is in `arg[0]` just for the case a). However, for all the
cases you can use

```
path = debug.getinfo(1,"S").source:sub(2)
```

but remember that in c) what is returned is what you passed to `load`
as third parameter (is up to you to specify correctly where you took
the information).

However the info returned can be a RELATIVE path, e.g.:
- in a) could return `./main.lua` if you launched the script with `lua
./main.lua`
- in b) `package.path='./module/?.lua' require 'mymodule' ` could
return './module/mymodule.lua'

Sadly, if you really need the abolute path 3), you have to rely on
os-dependent tricks. For linux you can use popen and realpath:

```
absolute = io.popen("realpath '"..path.."'", 'r'):read('a')
absolute = absolute:gsub('[\n\r]*$','') -- needed to handle popen result
```

This give you the absolute path to the file, so for the step 4) you
need a bit of string parsing to split path and name, e.g.
`absolute:match('^(.*/)([^/]-)$')`.

So putting all togheter:

```
local fullpath = debug.getinfo(1,"S").source:sub(2)
fullpath = io.popen("realpath '"..fullpath.."'", 'r'):read('a')
fullpath = fullpath:gsub('[\n\r]*$','')

local dirname, filename = fullpath:match('^(.*/)([^/]-)$')
dirname = dirname or ''
filename = filename or fullpath
```