Check if directory exists in lua? - lua

How do I check if a directory exists in lua, preferably without using the LuaFileSystem module if possible?
Trying to do something like this python line:
os.path.isdir(path)

This is a way that works on both Unix and Windows, without any external dependencies:
--- Check if a file or directory exists in this path
function exists(file)
local ok, err, code = os.rename(file, file)
if not ok then
if code == 13 then
-- Permission denied, but it exists
return true
end
end
return ok, err
end
--- Check if a directory exists in this path
function isdir(path)
-- "/" works on both Unix and Windows
return exists(path.."/")
end

The problem is that the stock Lua distribution (nearly) only includes features that are specified in standard C. Standard C makes no presumptions about there actually being a file system of any specific sort out there (or even an operating system, for that matter), so the os and io modules don't provide access information not available from the standard C library.
If you were attempting to code in pure standard C, you would have the same issue.
There is a chance that you can learn whether the folder exists implicitly from an attempt to use it. If you expect it to exist and be writable to you, then create a temporary file there and if the that succeeds, the folder exists. If it fails, you might not be able to distinguish a non-existent folder from insufficient permissions, of course.
By far the lightest-weight answer to getting a specific answer would be a thin binding to just those OS-specific function calls that provide the information you need. If you can accept the lua alien module, then you can like do the binding in otherwise pure Lua.
Simpler, but slightly heavier, is to accept Lua File System. It provides a portable module that supports most things one might want to learn about files and the file system.

If you're specifically interested in avoiding the LFS library, the Lua Posix library has an interface to stat().
require 'posix'
function isdir(fn)
return (posix.stat(fn, "type") == 'directory')
end

Well, the 5.1 reference manual doesn't have anything in the os table, but if you use Nixstaller, you get os.fileexists for precisely what you've explained.
If you can afford to fiddle around a bit, or if you know what OS you'll be running on, you might get away with the standard os library's os.execute with some system call that will identify if the file exists.
Even better than os.execute might be os.rename:
os.rename(oldname, newname)
Renames file named oldname to newname.
If this function fails, it returns
nil, plus a string describing the
error.
You could try setting oldname and newname the same -- you might not have write permissions, though, so it might fail because you can't write, even though you can read. In that event, you'd have to parse the returned error string and deduce whether you could write, or you'd have to just try executing your function that needs an existing file, and wrap it in a pcall.

You can also use the 'paths' package. Here's the link to the package
Then in Lua do:
require 'paths'
if paths.dirp('your_desired_directory') then
print 'it exists'
else
print 'it does not exist'
end

This is tested for the windows platform. It is quite easy actually:
local function directory_exists( sPath )
if type( sPath ) ~= "string" then return false end
local response = os.execute( "cd " .. sPath )
if response == 0 then
return true
end
return false
end
Obviously, this may not work on other OS's. But for windows users, this can be a solution :)

here is a simple way to check if a folder exists WITHOUT ANY EXTERNAL LIBRARY DEPENDENCIES :)
function directory_exists(path)
local f = io.popen("cd " .. path)
local ff = f:read("*all")
if (ff:find("ItemNotFoundException")) then
return false
else
return true
end
end
print(directory_exists("C:\\Users"))
print(directory_exists("C:\\ThisFolder\\IsNotHere"))
If you copy and paste the above into Lua you should see
false
true
good luck :)

I use these (but i actually check for the error):
require("lfs")
-- no function checks for errors.
-- you should check for them
function isFile(name)
if type(name)~="string" then return false end
if not isDir(name) then
return os.rename(name,name) and true or false
-- note that the short evaluation is to
-- return false instead of a possible nil
end
return false
end
function isFileOrDir(name)
if type(name)~="string" then return false end
return os.rename(name, name) and true or false
end
function isDir(name)
if type(name)~="string" then return false end
local cd = lfs.currentdir()
local is = lfs.chdir(name) and true or false
lfs.chdir(cd)
return is
end
os.rename(name1, name2) will rename name1 to name2. Use the same name and nothing should change (except there is a badass error). If everything worked out good it returns true, else it returns nil and the errormessage. You said you dont want to use lfs. If you dont you cant differentiate between files and directorys without trying to open the file (which is a bit slow but ok).
So without LuaFileSystem
-- no require("lfs")
function exists(name)
if type(name)~="string" then return false end
return os.rename(name,name) and true or false
end
function isFile(name)
if type(name)~="string" then return false end
if not exists(name) then return false end
local f = io.open(name)
if f then
f:close()
return true
end
return false
end
function isDir(name)
return (exists(name) and not isFile(name))
end
It looks shorter, but takes longer...
Also open a file is a it risky, because of that you should use lfs.
If you don't care about performance (and errorhandling -.-) you can just use it.
Have fun coding!

My preferred way of doing this in linux is
if os.execute '[ -e "/home" ]' then
io.write "it exists"
if os.execute '[ -d "/home" ]' then
io.write " and is a directory"
end
io.write "\n"
end
or, to put this into a function:
function is_dir(path)
return os.execute(('[ -d "%s" ]'):format(path))
end -- note that this implementation will return some more values

This feels so simple that I feel like there's a reason nobody else has done this.
But it works on my machine (Ubuntu focal), for files and directories.
It does however say that existent-yet-forbidden files don't exist.
For quick scripts and/or few files:
function exists(path)
return (io.open(path,"r") ~= nil)
end
Good practice:
function exists(path)
local file = io.open(path,"r")
if (file ~= nil) then
io.close(file)
return true
else
return false
end
end

For Linux users:
function dir_exists( path )
if type( path ) ~= 'string' then
error('input error')
return false
end
local response = os.execute( 'cd ' .. path )
if response == nil then
return false
end
return response
end

local function directory_exist(dir_path)
local f = io.popen('[ -d "' .. dir_path .. '" ] && echo -n y')
local result = f:read(1)
f:close()
return result == "y"
end
try this

nixio.fs
a package widely used in openwrt,
has lots of useful functions, and easy to use
docs:
http://openwrt.github.io/luci/api/modules/nixio.fs.html#nixio.fs.stat
fs.stat, lstat
> fs = require'nixio.fs'
> st = fs.stat('/tmp')
> = st.type=='dir'
true
> = fs.lstat'/var'.type=='lnk'
true
> = fs.stat'/bin/sh'.type=='reg'
true
tests:
> = pj(fs.stat'/var')
{
"dev": 17,
"type": "dir",
"modedec": 755,
"rdev": 0,
"nlink": 28,
"atime": 1617348683,
"blocks": 0,
"modestr": "rwxr-xr-x",
"ino": 1136,
"mtime": 1666474113,
"gid": 0,
"blksize": 4096,
"ctime": 1666474113,
"uid": 0,
"size": 1960
}
> = pj(fs.lstat'/var')
{
"dev": 19,
"type": "lnk",
"modedec": 777,
"rdev": 0,
"nlink": 1,
"atime": 1613402557,
"blocks": 0,
"modestr": "rwxrwxrwx",
"ino": 1003,
"mtime": 1613402557,
"gid": 0,
"blksize": 1024,
"ctime": 1613402557,
"uid": 0,
"size": 3
}
about stat(), lstat() syscall
shell test operator
[ -e path ]
[ -f path ]
[ -d path ]
[ -L path ]
low level both rely on this syscall.
$ strace test -e /var |& grep stat | tail -1
stat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
$ strace test -f /var |& grep stat | tail -1
stat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
$ strace test -d /var |& grep stat | tail -1
stat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
$ strace test -L /var |& grep stat | tail -1
lstat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0

Related

how to restart a lua program using the script in the lua script

It's me again
I'm trying to make a Terminal program in Lua since it's my best language I know, I'm making the calculator program in it and I'm trying to make it so if the user types "exit" the program will restart and will go back to the terminal, but I don't know how to reset the program through the code. if anyone can help that be deeply appreciated.
This is the code:
io.write("Terminal is starting up --- done!")
io.write("Making sure everything works --- Done!")
cmd = io.read()
io.write(">")
if cmd == "" then
io.write(">\n")
end
if cmd == "cal"then
io.write("Calculator Terminal Program v1.0")
io.write("what operation?/n")
op = io.read()
if op == "exit"then
io.write("Exiting")
end
end
To answer your question directly, I don't think it's possible to "restart the program". However, by taking advantage of loops, you can achieve the same result.
For instance, this code probably does what you want:
print('Terminal is starting up --- done!')
print('Making sure everything works --- Done!')
repeat
io.write('>')
cmd = io.read()
if cmd == 'cal' then
print('Calculator Terminal Program v1.0')
repeat
io.write('Operation: ')
op = io.read()
until op == 'exit'
print('Exiting')
elseif cmd == 'command' then
--another command
else
print('Unknown command.')
end
until cmd == 'exit'
Other tips:
You should take advantage of elseif instead of writing multiple separate if statements to improve readability.
Consider using the print function when you want a new line after writing some text for a better Terminal experience. You could also use io.write('\n').
You probably want os.exit(), which terminates the entire program.
i think this might work through creative use of load() and coroutines
this gonna stop restarting itself when 3 total error has occured
if innerProgram == nil then --innerProgram will set to true when it load itself
local filename = nil
local errorLimit = 3 --Change this to any value to enable this code to restart itself when error occur until this amount of time set zero or below to exit instantly when error occur
local errors = 0
local filename = function()
local str = debug.getinfo(2, "S").source:sub(2)
return str:match("^.*/(.*)") or str
end
filename = filename()
local src_h = io.open(filename, "r") --open in read mode
local src = src_h:read("*a")
src_h:close()
local G = _G
local quit = false --set true when you want to exit instead restart
local code = nil
local request = false
local restart = false --set true when you want restart
local program
local yield = coroutine.yield --Incase when coroutine get removed in your calculator code for no reason
local running = coroutine.running
local exit = os.exit
function G.restart()
restart = true --Always refer to restart variable above
request = true
yield() --Always refer to yield above
end
function G.os.exit(exitcode) --Replace os.exit with this
quit = true --Always refer to quit variable above
reuqest = true
code = exitcode or nil
yield() --Always refer to yield above
end
function G.coroutine.yield()
if running() == program and request == false then --Emulating coroutine.yield when it not run inside coroutine
error("attempt to yield from outside a coroutine")
end
end
G.innerProgram = true --So the inner program not keep loading itself forever
function copy(obj, seen)
if type(obj) ~= 'table' then return obj end --got from https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value for us to clone _G variable without reference to original _G thus we can do total restart without using same _G
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
return res
end
print("Loading "..filename)
program = coroutine.create(load(src, filename, "bt", copy(G)))
while errors < errorLimit do
restart = false
local status, err = coroutine.resume(program)
if restart == true then
print("Restarting...")
program = coroutine.create(load(src, filename, "bt", copy(G)))
--Put errors = errors + 1 if you want errors counter to reset every time the program request restart
end
if status == false and restart ~= true then
print(filename.." errored with "..err.."\nRestarting...")
program = coroutine.create(load(src, filename, "bt", copy(G)))
errors = errors + 1
elseif restart ~= true then
print(filename.." done executing.")
exit()
end
end
return
else
innerProgram = nil --Nil-ing the variable
end
Features
Auto exit when 3 total errors has occur (configure errorLimit variable)
_G is not shared (same _G as start of the program but not linked with the actual _G)
Emulating yielding outside of coroutine
Replaced os.exit so it yield then the self-loader run the os.exit
How to use
put the code i give above to very first line of your code
Feature Number 1 and 3 test
it error with the a content the value will be different in each error restart
if a == nil then --Only set a when a equal nil so if _G was shared the error value will be same
a = math.random() --Set global a to a random value
end
error(a) --Error with number a
os.exit()

In trepl or luajit, how can I find the source code of a library I'm using?

Let's say I'm working with a lua library I installed using luarocks, and I want to see the definition of a function from that library. In ipython in could use
??function_name
to see the definition in the terminal, in matlab I could use
which function_name
then use my editor to look at the path returned by which. How could I do something similar to find the function definition for a lua library?
In 'plain' Lua/JIT, you can say debug.getinfo( func ) and will get a table containing (among others) the fields short_src, source and linedefined.
For Lua functions, short_src will be the filename or stdin if it was defined in the REPL. (source has a slightly different format, filenames are prefixed with an #, a = prefix is used for C functions or stuff defined interactively, and for loaded functions, it will be the actual string that was loaded.)
You can pack that up in a function like
function sourceof( f )
local info = debug.getinfo( f, "S" )
return info.short_src, info.linedefined
end
or maybe even start an editor and point it there, e.g. (for vim)
function viewsource( f )
-- get info & check it's actually from a file
local info = debug.getinfo( f, "S" )
local src, line = info.source, info.linedefined
if src == "=[C]" then return nil, "Is a C function." end
local path = src:match "^#(.*)$"
if path then
-- start vim (or an other editor if you adapt the format string)
return os.execute( ("vim -fR %q +%d"):format( path, line ) )
end
return nil, "Was defined at run time."
end
And just for fun, here's yet another version that returns the code if it can find it somewhere. (This will also work for functions that have been generated at run time, e.g. by calling load, and where no source file exists. You could also work in the other direction by dumping the loaded snippet into a temp file and opening that…)
-- helper to extract the source block defining the function
local function funclines( str, line1, lineN, filename )
-- if linedefined / lastlinedefined are 0, this is the main chunk's function
if line1 == 0 and lineN == 0 then
filename = filename and filename.." (main chunk)"
or "(chunk defined at runtime)"
return "-- "..filename.."\n"..str
end
-- add line info to file name or use placeholder
filename = filename and filename..":"..line1 or "(defined at runtime)"
-- get the source block
local phase, skip, grab = 1, line1-1, lineN-(line1-1)
local ostart, oend -- these will be the start/end offsets
if skip == 0 then phase, ostart = 2, 0 end -- starts at first line
for pos in str:gmatch "\n()" do
if phase == 1 then -- find offset of linedefined
skip = skip - 1 ; if skip == 0 then ostart, phase = pos, 2 end
else -- phase == 2, find offset of lastlinedefined+1
grab = grab - 1 ; if grab == 0 then oend = pos-2 ; break end
end
end
return "-- "..filename.."\n"..str:sub( ostart, oend )
end
function dumpsource( f )
-- get info & line numbers
local info = debug.getinfo( f, "S" )
local src, line, lastline = info.source, info.linedefined, info.lastlinedefined
-- can't do anything for a C function
if src == "=[C]" then return nil, "Is a C function." end
if src == "=stdin" then return nil, "Was defined interactively." end
-- for files, fetch the definition
local path = src:match "^#(.*)$"
if path then
local f = io.open( path )
local code = f:read '*a'
f:close( )
return funclines( code, line, lastline, path )
end
-- otherwise `load`ed, so `source`/`src` _is_ the source
return funclines( src, line, lastline )
end
A closing remark: If you paste code into a Lua/JIT REPL, locals disappear between definitions, because every line (or minimal complete group of lines) is its own chunk. The common fix (that you probably know) is to wrap everything into a block as do*paste*end, but an alternative is to load[[*paste*]]() (possibly with more =s like [===[ and ]===].) If you paste this way, the above dumpsource (or any other function using debug.getinfo) will then be able to get the source of the function(s). This also means that if you defined a nice function but it's gone from the history and the scroll buffer, you can recover it in this way (if you defined it by loading and not directly feeding the interpreter). Saving the source in a file will then also be possible without copy-pasting and not require editing out the >> prompts.

how do you properly use a pipe with lua to get the output of a program?

I am using Lua with the luaposix library to get the the output of some commands, and sometimes send some too. I am using this code, or variants of this to do my work, but I sometimes get stuck at posix.wait(cpid) or sometimes the command doesn't seem to finish.
-- returns true on connected, else false
function wifi.wpa_supplicant_status()
-- iw dev wlan0-1 link
local r,w = posix.pipe()
local cpid = posix.fork()
if cpid == 0 then --child writes to pipe
--close unused read end
local devnull = posix.open("/dev/null", posix.O_RDWR)
posix.close(r)
posix.dup2(devnull, 0)
posix.dup2(w, 1)
posix.dup2(devnull, 2)
local dir = wifi.wpa_supplicant_dir()
local iface = posix.basename(dir)
iface = string.gsub(iface, "wpa_supplicant%-",'')
posix.exec('/usr/sbin/iw', {'dev', iface, 'link'})
posix._exit(-1)
elseif cpid > 0 then
--parent reads from pipe, close write end
posix.close(w)
local buf = ''
while true do
local tmp = posix.read(r, 100)
if tmp ~= nil and #tmp > 0 then
buf = buf .. tmp
else
break
end
end
-- TODO, check exit value, to see if entry exists or not
while posix.wait(cpid) ~= cpid do print("waiting in wpa_supplicant_status") end
print("status is "..buf)
if string.find(buf, "Connected to", 1, true) then
return true
else
return false
end
end
end
This is what I understand what I have to do(to just get output):
create a single pipe
fork
if child,
close read end of pipe
dup2(write_end, stdout)
exec() to desired process
if parent
close write end of pipe
do blocking reads to read end of pipe, till you get a 0 byte read, which means the child process has terminated, closing the pipe
Am I missing something?
You should have a look at the answers in this question How do you construct a read-write pipe with lua?
to get an idea of the difficulties to implement this. One thing that strikes me is that nobody seems to be able to get it to work with only one child process.
However, In the example you give, it seems you only want to get the output of the command, so I would suggest simply doing this:
function os.capture(cmd)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
return s
end
function wifi.wpa_supplicant_status()
local dir = wifi.wpa_supplicant_dir()
local iface = posix.basename(dir):gsub("wpa_supplicant%-",'')
local cmd = ('/usr/sbin/iw dev %s link'):format(iface)
local buf = os.capture(cmd)
return buf:find("Connected to", 1, true) ~= nil
end
This is not even tested, but you should get the idea.

how to check if a file exists in lua - when you don't have the full file name?

I have a situation where I don't know the complete name of a file but I need to search for it to see if it exists. the part of the file name that I don't know is a sequence number at the end of the name.
For example, the file name looks like this:
myfile.1234567890.12.xff
the ".12" part is what I don't know. However, I just need to know if any files starting with "myfile.1234567890" and ending with ".xff" exist.
How would i accomplish this in lua?
thanks.
Version for Windows.
No external libraries.
local function recursive_search(path, OS_filemask, filename_pattern, search_for_dirs, only_top_level)
path = path:gsub('/', '\\'):gsub('\\*$', '\\', 1)
OS_filemask = OS_filemask or '*.*'
filename_pattern = filename_pattern or '.*'
local arr = {}
local pipe = io.popen((search_for_dirs and 'dir /b/ad "' or 'dir /b/a-d "')..path..OS_filemask..'" 2> nul')
for f in pipe:lines() do
if f:lower():match('^'..filename_pattern..'$') then
table.insert(arr, path..f)
end
end
pipe:close()
if not only_top_level then
for _, path in ipairs(recursive_search(path, nil, nil, true, true)) do
for _, f in ipairs(recursive_search(path, OS_filemask, filename_pattern, search_for_dirs)) do
table.insert(arr, f)
end
end
end
return arr
end
-- Find all number-named JPEG picures in C:\Games
-- OS filemask can't filter it properly, use Lua pattern to restrict search conditions
for _, f in ipairs(recursive_search('C:\\Games', '*.jp*g', '%d+%.jpe?g')) do
print(f)
end
-- Find all folders in C:\WINDOWS with 32 in its name
-- OS filemask is enough here
for _, f in ipairs(recursive_search('C:\\WINDOWS', '*32*', nil, true)) do
print(f)
end
-- myfile.1234567890.12.xff
for _, f in ipairs(recursive_search('C:\\', 'myfile.1234567890.*.xff', 'myfile%.1234567890%.%d+%.xff')) do
print(f)
end
You can find an implementation of glob function in Lua at this link. But you could just iterate through the list of all files in the folder and check them against your pattern. You could use LuaFileSystem lfs.dir() to get the list.

Lua os.execute return value

Is it possible to read the following from the local variable in Lua?
local t = os.execute("echo 'test'")
print(t)
I just want to achieve this: whenever os.execute returns any value, I would like to use it in Lua - for example echo 'test' will output test in the bash command line - is that possible to get the returned value (test in this case) to the Lua local variable?
You can use io.popen() instead. This returns a file handle you can use to read the output of the command. Something like the following may work:
local handle = io.popen(command)
local result = handle:read("*a")
handle:close()
Note that this will include the trailing newline (if any) that the command emits.
function GetFiles(mask)
local files = {}
local tmpfile = '/tmp/stmp.txt'
os.execute('ls -1 '..mask..' > '..tmpfile)
local f = io.open(tmpfile)
if not f then return files end
local k = 1
for line in f:lines() do
files[k] = line
k = k + 1
end
f:close()
return files
end
Lua's os.capture returns all standard output, thus it will be returned into that variable.
Example:
local result = os.capture("echo hallo")
print(result)
Printing:
hallo
Sorry,
but this is impossible.
If the echo programm exit with success it will return 0. This returncode is what the os.execute() function get and returned too.
if 0 == os.execute("echo 'test'") then
local t = "test"
end
This is a way to get what you want, i hope it helps you.
Another Tip for getting the return code of a function is the Lua reference.
Lua-Reference/Tutorial

Resources