Get containing path of lua file - filesystems

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 be 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

Related

Finding all image files in folder and change their names with number

Let's assume we have following files.
00043.jpg
00086.jpg
00123.jpg
...
04523.jpg
What I want is, change their names to as following.
00001.jpg
00002.jpg
00003.jpg
so on
How can I do this on linux console?
Or I can use python scripts.
Supply the valid folder path and Run this code.
import os
# Function to rename multiple files
def main():
i = 0
path = 'E:\\files\\'
for filename in os.listdir("E:\\files"):
dst = str(i).zfill(4) + ".jpg"
src = path + filename
dst = path + dst
# rename() function will
# rename all the files
os.rename(src, dst)
i += 1
# Driver Code
if __name__ == '__main__':
# Calling main() function
main()
If you have PHP in your console,
<?php
$images = glob("*.jpg");
$start=0;
function squencer()
{
global $start;
++$start;
return str_pad($start, 5, "0", STR_PAD_LEFT);
}
foreach($images as $image)
{
$newname = squencer().".jpg";
rename($image, $newname);
}
Then run: php -f rename.php
The only standard command for renaming files is mv and it has very limited capability. What you want to do inherently requires a script because the complexity of sequential naming is beyond the capability of globbing or even regular expressions. You have not specified whether there are other files in the directory or whether you care about the original order. Unlike Windows, Linux directory contents are not in any predictable order. I assume that you do want to maintain the original order. Your example indicates 0-padding. This is an important detail. It means that a lexical sort matches what we intuitively think, e.g. that 43 > 42. Lexically, 43 > 42 but 42 > 043. I'm going to show you how to do this using Python, assuming that the directory contains other files and that you want to maintain the original order. The first line is a shebang, telling the OS to use python3. This is particularly for Ubuntu but most systems will need this or a variation of it because they use python 2 by default. Also, although Python doesn't care what kind of line endings are in the script, the OS itself reads the shebang and it requires that these be Unix.
#!/usr/bin/env python3
import os
jpgfiles = [f for f in os.listdir() if os.path.splitext(f)[1].casefold() == jpg']
jpgfiles.sort()
for i,f in enumerate(jpgfiles) :
os.rename(f, '%04.d' % i + '.jpg')
Python's inherent power enables a simple script like this to perform rather complex operations. But you don't want to have to write a script for every kind of file renaming you might want to do. When I couldn't find an existing program for the variety of renaming that I do, I wrote a Python program that can do what you want and much more. It is rene.py. You can get it at Sourceforge https://rene-file-renamer.sourceforge.io. It is free and open source (GNU GPL v3.0). The command to do what you want is rene *.jpg :.jpg I///5.

How to run same code on multiple files, or all files in directory

so I am very new to coding and recently wrote a little program that involved R and sox. It looked like this
file <- "test.mp3"
testSox = paste("sox ",file," -n spectrogram -o ",file,".png stats",sep='')
sox = system(testSox, intern = TRUE)
print(sox)
Now, instead of assigning the one file manually within the code, I would just like to have this code read through all the mp3s in a folder automatically. Is this possible? Any help would be greatly appreciated. Thanks!
EDIT: Actually, I should add that I tried list.files, but when it comes to running the system() command, I get
"Error in system(command, as.integer(flag), f, stdout, stderr) :
character string expected as first argument"
Here's the list.files code I tried:
> temp = list.files(path = ".", pattern=".mp3")
>
> file <- temp
>
> firstSox = paste("sox ",file," -n spectrogram -o ",file,".png stats",sep='')
> sox = system(firstSox, intern = TRUE)
Error in system(command, as.integer(flag), f, stdout, stderr) :
character string expected as first argument
> print(sox)
I'm guessing this is not the correct route to go? Because I basically need to replace 'file' in the firstSox line with each mp3 that's in the temp array. So instead of running:
file <- "test.mp3"
...I would just like to have it re-assign each time for every file in the folder..., so it runs through as test.mp3, then 1.mp3, then 2.mp3, then 3.mp, etc.
I've scoured the net, and just feel like I've hit a brick wall. As stated in the comments, I've read up on loops, but for some reason I can't wrap my head around how to incorporate it into what I have written. I feel like I just need someone to show me at least the way, or maybe even write me an example so I can wrap my head around it. Would greatly appreciate help and any tips on what I'm doing wrong and could correct. Thanks.
Try the below code. I am using dir() instead of list.files, just because I find it easier. Remember there are many ways to do the same thing in R.
files <- dir(path = ".",pattern = ".mp3") #Get all the mp3 files
for(f in files) { #Loop over the mp3 files one at a time
firstSox = paste("sox ",f," -n spectrogram -o ",f,".png stats",sep='')
sox = system(firstSox, intern = TRUE)
print(sox)
}
Your firstSox variable will be a vector of commands to run (paste will generate a vector, one string for each element of file). So now you just need to run each command through system
One way to do this and capture the output is to use the lapply or sapply function:
sox <- lapply( firstSox, function(x) system(x, intern=TRUE) )
In this code lapply will run the function for each element of firstSox one at a time, the function just takes the current element (in x) and passes that to system. Then lapply gathers all the outputs together and combines them into a list that it puts into sox.
If the results of each run give the same shape of results (single number or vector of same length) then you can use sapply instead and it will simplify the return into a vector or matrix.

Lua 5.2.1 - Edit and save variable in file

I have a file which is part of a game I'm making, and I am trying to manipulate it with code.
Here is the file:
tech =
{
weaponstech = 1.5,
armortech = 1.8,
shieldstech = 2
}
I am trying to open the file like this
local file = io.open("tech")
and then try to change the value of the variable 'shieldstech' to 2.2.
I need this to happen automatically every time I run a function.
I usually use single variable files such as this:
v = 1
but that just gives me a clutter of files which is unmanageable.
so now I store variables the way I wrote my tech file.
This is how I used to edit these single-variable files:
local file = io.open("file", "w")
file:write("v = "..var)
file.close()
but it is just too much work to rewrite the whole file in a single line or code, so I want to just change and save the variable, something like this:
local file = io.open("tech", "w")
shieldstech = 2.2
file:close()
but it won't work like that, and I know why. I'm not telling the program to edit the file, I'm telling it to edit the variable in that instance of the program. All I'm doing to the file is opening it and then closing it.
Any of you guys know a way to do this?
Thx,
Brendan
My suggestion would be to use something designed for that task already. Here is an example: https://github.com/2ion/ini.lua That will allow you to read in the data, make as many or as few changes to it as you want, and then write it back out.
EDIT: This has a dependency on this: https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua
Might want to try inih instead (although it's written C, so integration will require a bit more knowledge): http://luarocks.org/repositories/rocks/#lua-inih
This will rewrite the whole file each time, which is not very performing, but it will work. Consider using a sqlite database.
local file = io.open("tech", "w")
file:write("tech = {")
for p,v in pairs(tech) do file:write(p .. " = " .. v .. "," ) end
file:write("}")
file:close()

How to determine if a path is inside a directory? (POSIX)

In C, using POSIX calls, how can I determine if a path is inside a target directory?
For example, a web server has its root directory in /srv, this is getcwd() for the daemon.
When parsing a request for /index.html, it returns the contents of /srv/index.html.
How can I filter out requests for paths outside of /srv?
/../etc/passwd,
/valid/../../etc/passwd,
etc.
Splitting the path at / and rejecting any array containing .. will break valid accesses /srv/valid/../index.html.
Is there a canonical way to do this with system calls? Or do I need to manually walk the path and count directory depth?
There's always realpath:
The realpath() function shall derive, from the pathname pointed to by *file_name*, an absolute pathname that resolves to the same directory entry, whose resolution does not involve '.' , '..' , or symbolic links.
Then compare what realpath gives you with your desired root directory and see if they match up.
You could also clean up the filename by hand by expanding the double-dots before you prepend the "/srv". Split the incoming path on slashes and walk through it piece by piece. If you get a "." then remove it and move on; if you get a "..", then remove it and the previous component (taking care not go past the first entry in your list); if you get anything else, just move on to the next component. Then paste what's left back together with slashes between the components and prepend your "/srv/". So if someone gives you "/valid/../../etc/passwd", you'll end up with "/srv/etc/passwd" and "/where/is/../pancakes/house" will end up as "/srv/where/pancakes/house".
That way you can't get outside "/srv" (except through symbolic links of course) and an incoming "/../.." will be the same as "/" (just like in a normal file system). But you'd still want to use realpath if you're worried about symbolic under "/srv".
Working with the path name component by component would also allow you to break the connection between the layout you present to the outside world and the actual file system layout; there's no need for "/this/that/other/thing" to map to an actual "/srv/this/that/other/thing" file anywhere, the path could just be a key in some sort of database or some sort of namespace path to a function call.
To determine if a file F is within a directory D, first stat D to determine its device number and inode number (members st_dev and st_ino of struct stat).
Then stat F to determine if it is a directory. If not, call basename to determine the name of the directory containing it. Set G to the name of this directory. If F was already a directory, set G=F.
Now, F is within D if and only if G is within D. Next we have a loop.
while (1) {
if (samefile(d_statinfo.d_dev, d_statinfo.d_ino, G)) {
return 1; // F was within D
} else if (0 == strcmp("/", G) {
return 0; // F was not within D.
}
G = dirname(G);
}
The samefile function is simple:
int samefile(dev_t ddev, ino_t dino, const char *path) {
struct stat st;
if (0 == stat(path, &st)) {
return ddev == st.st_dev && dino == st.st_no;
} else {
throw ...; // or return error value (but also change the caller to detect it)
}
}
This will work on POSIX filesystems. But many filesystems are not POSIX. Problems to look out for include:
Filesystems where the device/inode are not unique. Some FUSE filesystems are examples of this; they sometimes make up inode numbers when the underlying filesystems don't have them. They shouldn't re-use inode numbers, but some FUSE filesystems have bugs.
Broken NFS implementations. On some systems all NFS filesystems have the same device number. If they pass through the inode number as it exists on the server, this could cause a problem (though I've never seen it happen in practice).
Linux bind mount points. If /a is a bind mount of /b, then /a/1 correctly appears to be inside /a, but with the implementation above, /b/1 also appears to be inside /a. I think that's probably the correct answer. However, if this is not the result you prefer, this is easily fixed by changing the return 1 case to call strcmp() to compare the path names too. However, for this to work you will need to start by calling realpath on both F and D. The realpath call can be quite expensive (since it may need to hit the disk a number of times).
The special path //foo/bar. POSIX allows path names beginning with // to be special in a way which is somewhat not well defined. Actually I forget the precise level of guarantee about semantics that POSIX provides. I think that POSIX allows //foo/bar and //baz/ugh to refer to the same file. The device/inode check should still do the right thing for you but you may find it does not (i.e. you may find that //foo/bar and //baz/ugh can refer to the same file but have different device/inode numbers).
This answer assumes that we start with an absolute path for both F and D. If this is not guaranteed you may need to do some conversion using realpath() and getcwd(). This will be a problem if the name of the current directory is longer than PATH_MAX (which can certainly happen).
You should simply process .. yourself and remove the previous path component when it's found, so that there are no occurrences of .. in the final string you use for opening files.

Can I tell if a string corresponds to either a file or a directory in .NET? [duplicate]

This question already has answers here:
.NET How to check if path is a file and not a directory?
(8 answers)
Closed 9 years ago.
If I have a string, I know I can use If System.IO.File.Exists(mystring) or System.IO.Directory.Exists(mystring) to see which it is. Is there a method I can use to make a single call to determine which type it is? Since, at least in Windows, you can't have both a file and a directory with the same name, it seems like there should be a function that accepts a string and returns either "Folder", "File", or "Nothing".
I'm currently doing this, but it doesn't seem like the best way to do it:
If Directory.Exists(mystring) Then
' It's a folder
ElseIf File.Exists(mystring) Then
' It's a file
Else
' It's neither - doesn't exist
End If
Use the System.IO.File.GetAttributes() method. The returned FileAttributes enum has a flag that indicates if it's a directory.
string path = #"C:\Program Files";
if( (int)(File.GetAttributes( path ) & FileAttributes.Directory) != 0 )
{
// .. it's a directory...
}
This works best if you know the path to exist. If the path is invalid, you will get an exception. If you don't know that the path exists, your approach of first calling Directory.Exists() followed by File.Exists() is probably better.
You can, of course, write your own method to wrap this logic up together, so you don't have to repeat it in more than one place.
Another solution might be:
if ( !string.IsNullOrEmpty( Path.GetFileName(path) ) )
{
//it's a file
}
else if ( !string.IsNullOrEmpty( Path.GetDirectory(path) )
{
//it's a directory
}
This is obviously not foolproof nor will it in any way establish whether the given file or directory exists on the disk. It will only determine if the path has something that looks like a filename in it. It obviously will not handle oddball situations like a directory called "foo.com". However, if you are looking for something that is pretty close that will lessen the odds of an exception, this might help.

Resources