diff options
Diffstat (limited to 'Data/Libraries/Penlight/docs_topics/04-paths.md')
-rw-r--r-- | Data/Libraries/Penlight/docs_topics/04-paths.md | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/Data/Libraries/Penlight/docs_topics/04-paths.md b/Data/Libraries/Penlight/docs_topics/04-paths.md new file mode 100644 index 0000000..4367fe6 --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/04-paths.md @@ -0,0 +1,170 @@ +## Paths and Directories + +### Working with Paths + +Programs should not depend on quirks of your operating system. They will be +harder to read, and need to be ported for other systems. The worst of course is +hardcoding paths like 'c:\\' in programs, and wondering why Vista complains so +much. But even something like `dir..'\\'..file` is a problem, since Unix can't +understand backslashes in this way. `dir..'/'..file` is _usually_ portable, but +it's best to put this all into a simple function, `path.join`. If you +consistently use `path.join`, then it's much easier to write cross-platform code, +since it handles the directory separator for you. + +`pl.path` provides the same functionality as Python's `os.path` module (11.1). + + > p = 'c:\\bonzo\\DOG.txt' + > = path.normcase (p) ---> only makes sense on Windows + c:\bonzo\dog.txt + > = path.splitext (p) + c:\bonzo\DOG .txt + > = path.extension (p) + .txt + > = path.basename (p) + DOG.txt + > = path.exists(p) + false + > = path.join ('fred','alice.txt') + fred\alice.txt + > = path.exists 'pretty.lua' + true + > = path.getsize 'pretty.lua' + 2125 + > = path.isfile 'pretty.lua' + true + > = path.isdir 'pretty.lua' + false + + +It is very important for all programmers, not just on Unix, to only write to +where they are allowed to write. `path.expanduser` will expand '~' (tilde) into +the home directory. Depending on your OS, this will be a guaranteed place where +you can create files: + + > = path.expanduser '~/mydata.txt' + 'C:\Documents and Settings\SJDonova/mydata.txt' + + > = path.expanduser '~/mydata.txt' + /home/sdonovan/mydata.txt + +Under Windows, `os.tmpname` returns a path which leads to your drive root full of +temporary files. (And increasingly, you do not have access to this root folder.) +This is corrected by `path.tmpname`, which uses the environment variable TMP: + + > os.tmpname() -- not a good place to put temporary files! + '\s25g.' + > path.tmpname() + 'C:\DOCUME~1\SJDonova\LOCALS~1\Temp\s25g.1' + + +A useful extra function is `pl.path.package_path`, which will tell you the path +of a particular Lua module. So on my system, `package_path('pl.path')` returns +'C:\Program Files\Lua\5.1\lualibs\pl\path.lua', and `package_path('ifs')` returns +'C:\Program Files\Lua\5.1\clibs\lfs.dll'. It is implemented in terms of +`package.searchpath`, which is a new function in Lua 5.2 which has been +implemented for Lua 5.1 in Penlight. + +### File Operations + +`pl.file` is a new module that provides more sensible names for common file +operations. For instance, `file.read` and `file.write` are aliases for +`utils.readfile` and `utils.writefile`. + +Smaller files can be efficiently read and written in one operation. `file.read` +is passed a filename and returns the contents as a string, if successful; if not, +then it returns `nil` and the actual error message. There is an optional boolean +parameter if you want the file to be read in binary mode (this makes no +difference on Unix but remains important with Windows.) + +In previous versions of Penlight, `utils.readfile` would read standard input if +the file was not specified, but this can lead to nasty bugs; use `io.read '*a'` +to grab all of standard input. + +Similarly, `file.write` takes a filename and a string which will be written to +that file. + +For example, this little script converts a file into upper case: + + require 'pl' + assert(#arg == 2, 'supply two filenames') + text = assert(file.read(arg[1])) + assert(file.write(arg[2],text:upper())) + +Copying files is suprisingly tricky. `file.copy` and `file.move` attempt to use +the best implementation possible. On Windows, they link to the API functions +`CopyFile` and `MoveFile`, but only if the `alien` package is installed (this is +true for Lua for Windows.) Otherwise, the system copy command is used. This can +be ugly when writing Windows GUI applications, because of the dreaded flashing +black-box problem with launching processes. + +### Directory Operations + +`pl.dir` provides some useful functions for working with directories. `fnmatch` +will match a filename against a shell pattern, and `filter` will return any files +in the supplied list which match the given pattern, which correspond to the +functions in the Python `fnmatch` module. `getdirectories` will return all +directories contained in a directory, and `getfiles` will return all files in a +directory which match a shell pattern. These functions return the files as a +table, unlike `lfs.dir` which returns an iterator.) + +`dir.makepath` can create a full path, creating subdirectories as necessary; +`rmtree` is the Nuclear Option of file deleting functions, since it will +recursively clear out and delete all directories found begining at a path (there +is a similar function with this name in the Python `shutils` module.) + + > = dir.makepath 't\\temp\\bonzo' + > = path.isdir 't\\temp\\bonzo' + true + > = dir.rmtree 't' + +`dir.rmtree` depends on `dir.walk`, which is a powerful tool for scanning a whole +directory tree. Here is the implementation of `dir.rmtree`: + + --- remove a whole directory tree. + -- @param path A directory path + function dir.rmtree(fullpath) + for root,dirs,files in dir.walk(fullpath) do + for i,f in ipairs(files) do + os.remove(path.join(root,f)) + end + lfs.rmdir(root) + end + end + + +`dir.clonetree` clones directory trees. The first argument is a path that must +exist, and the second path is the path to be cloned. (Note that this path cannot +be _inside_ the first path, since this leads to madness.) By default, it will +then just recreate the directory structure. You can in addition provide a +function, which will be applied for all files found. + + -- make a copy of my libs folder + require 'pl' + p1 = [[d:\dev\lua\libs]] + p2 = [[D:\dev\lua\libs\..\tests]] + dir.clonetree(p1,p2,dir.copyfile) + +A more sophisticated version, which only copies files which have been modified: + + -- p1 and p2 as before, or from arg[1] and arg[2] + dir.clonetree(p1,p2,function(f1,f2) + local res + local t1,t2 = path.getmtime(f1),path.getmtime(f2) + -- f2 might not exist, so be careful about t2 + if not t2 or t1 > t2 then + res = dir.copyfile(f1,f2) + end + return res -- indicates successful operation + end) + +`dir.clonetree` uses `path.common_prefix`. With `p1` and `p2` defined above, the +common path is 'd:\dev\lua'. So 'd:\dev\lua\libs\testfunc.lua' is copied to +'d:\dev\lua\test\testfunc.lua', etc. + +If you need to find the common path of list of files, then `tablex.reduce` will +do the job: + + > p3 = [[d:\dev]] + > = tablex.reduce(path.common_prefix,{p1,p2,p3}) + 'd:\dev' + |