5

erlang is a strange language to me, this week i've been playing with multiple languages and I've come here for help often, now I'm on erlang and I'm stuck once again :)

Basically All i'm trying to do is the following but in erlang:

Dim objFSO, objFile, objFolder

Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(currentDirectory))

For Each objFile in objFolder.Files
   do something with the file
   do something else
   do more stuff
Next

The closest i've come is:

-export([main/1]).

main([]) -> 
find:files("c:\","*.txt", fun(F) -> {
       File, c:c(File)
}end).

clearly, not working and nothing like how i'd need it to be.. but I have tried many ways and read many example but simply cant figure out a solution maybe the language just isnt ment for this kind of stuff?

And this is needed as a escript (erlang script)

legoscia
  • 39,593
  • 22
  • 116
  • 167
ace007
  • 577
  • 1
  • 10
  • 20
  • 3
    [filelib:fold_files](http://www.erlang.org/doc/man/filelib.html#fold_files-5) might be the function you're looking for. – legoscia Jan 21 '13 at 16:54

2 Answers2

11

It's tough to recommend exactly which method you should use because your "do something" pseudocode is too vague.

There are two main ways one iterates over a list of something in a functional language like Erlang: map and fold.

The big question comes down to this: What do you want to do with the files? Do you want to total something up for the files (ie, total file size or something), or do you want to store some value for each file (ie each file size individually) or do you want to do something to the files and you don't care what the return values for those files are (ie renaming each file)?

I'll give an example of each quickly here using a list of files returned from file:list_dir/1:

{ok, Filenames} = file:list_dir("some_directory"),

Folding

Here we'll total the filesizes of all files in the directory using lists:foldl (as @legoscia mentioned, in this case, filelib:fold_files is probably the better choice)

TotalSize = lists:foldl(fun(Filename,SizeAcc) ->
                FileInfo = file:read_file_info("some_directory/" ++ Filename),
                FileSize = FileInfo#file_info.size,
                SizeAcc + FileSize
            end, 0, Filenames).

Mapping

Here, we'll get a list of filenames along with the filesize for each individual file using lists:map. The resultant list will be of the format = [{"somefile.txt",452}, {"anotherfile.exe",564},...]:

FileSizes = lists:map(fun(Filename) ->
                FileInfo = file:read_file_info("some_directory/" ++ Filename),
                FileSize = FileInfo#file_info.size,
                {Filename,FileSize}
            end,Filenames).

Foreach (a variant of mapping)

The alternative of just renaming files but not caring about recording any data about the files is to demonstrate the use of lists:foreach, which is generally used exclusively for side-effect programming in which you don't care about the return values, and it works like lists:map, but doesn't return anything useful (it just returns the atom ok):

In this case, I'll show renaming each file by adding a ".old" extension to each filename:

lists:foreach(fun(Filename) ->
                  OldFile = "some_directory/" ++ Filename,
                  NewFile = OldFile ++ ".old",
                  file:rename(OldFile, NewFile), 
              end,Filenames).

Recursion

Of course, the raw version of all of these - if map, fold, foreach, or list comprehensions (which I didn't cover, but are basically another variant of map with a filter component) are too restricting for whatever reason - you can do things recursively:

do_something_with_files([]) -> ok;
do_something_with_files([CurrentFile|RestOfFiles]) ->
   do_something(CurrentFile),
   do_something_with_files(RestOfFiles).

There are a lot of ways to do what you need with Erlang, but unlike with procedural languages like VB, you must think a little ahead to what you want to track or do in order to determine how you wish to iterate over your lists, since you're limited by immutable variables in Erlang.

Note: In order to use the #file_info record, you'll need to include the file.hrl file at the top of your module with:

-include_lib("kernel/include/file.hrl").
chops
  • 2,572
  • 1
  • 16
  • 25
2

i wrote a file system indexer recently and here below, i provide you with a module, which can traverse a directory (s) and lets you so what ever you want with the files it finds or the folders it finds. What it does is that it will spawn a new process to handle any inner directory. You will provide two Functional Objects , one that will deal with directories and the other will deal with the files.

%% @doc This module provides the low level APIs for reading, writing,
%% searching, joining and moving within directories.
%% @end
-module(file_scavenger_utilities). %%% ------- EXPORTS ------------------------------------------------------------------------------- -compile(export_all). %%% ------- INCLUDES ----------------------------------------------------------------------------- %%% -------- MACROS ------------------------------------------------------------------------------ -define(IS_FOLDER(X),filelib:is_dir(X)). -define(IS_FILE(X),filelib:is_file(X)). -define(FAILED_TO_LIST_DIR(X), error_logger:error_report(["* File Scavenger Utilities Error * ", {error,"Failed to List Directory"},{directory,X}])). -define(NOT_DIR(X), error_logger:error_report(["* File Scavenger Utilities Error * ", {error,"Not a Directory"},{alleged,X}])). -define(NOT_FILE(X), error_logger:error_report(["* File Scavenger Utilities Error * ", {error,"Not a File"},{alleged,X}])). %%%--------- TYPES -------------------------------------------------------------------------------
%% @type dir() = string(). %% Must be containing forward slashes, not back slashes. Must not end with a slash %% after the exact directory.e.g this is wrong: "C:/Program Files/SomeDirectory/" %% but this is right: "C:/Program Files/SomeDirectory" %% @type file_path() = string(). %% Must be containing forward slashes, not back slashes. %% Should include the file extension as well e.g "C:/Program Files/SomeFile.pdf" %% -----------------------------------------------------------------------------------------------
%% @doc Enters a directory and executes the fun ForEachFileFound/2 for each file it finds %% If it finds a directory, it executes the fun %% ForEachDirFound/2. %% Both funs above take the parent Dir as the first Argument. Then, it will spawn an %% erlang process that will spread the found Directory too in the same way as the parent directory %% was spread. The process of spreading goes on and on until every File (wether its in a nested %% Directory) is registered by its full path. %% @end %% %% @spec new_user(dir(),funtion(),function())-> ok.

spread_directory(Dir,Top_Directory,ForEachFileFound,ForEachDirFound) when is_function(ForEachFileFound),is_function(ForEachDirFound) -> case ?IS_FOLDER(Dir) of false -> ?NOT_DIR(Dir); true -> F = fun(X)-> FileOrDir = filename:absname_join(Dir,X), case ?IS_FOLDER(FileOrDir) of true -> (catch ForEachDirFound(Top_Directory,FileOrDir)), spawn(fun() -> ?MODULE:spread_directory(FileOrDir,Top_Directory,ForEachFileFound,ForEachDirFound) end); false -> case ?IS_FILE(FileOrDir) of false -> {error,not_a_file,FileOrDir}; true -> (catch ForEachFileFound(Top_Directory,FileOrDir)) end end end, case file:list_dir(Dir) of
{error,_} -> ?FAILED_TO_LIST_DIR(Dir); {ok,List} -> lists:foreach(F,List) end end.

To test it, below is usage:

E:\Applications>erl
Eshell V5.9  (abort with ^G)
1> Dir = "E:/Ruth".
"E:/Ruth"
2> TopDir = "E:/Ruth".
"E:/Ruth"
3> Folder = fun(Parent,F) -> io:format("\n\t~p contains Folder: ~p~n",[Parent,F]) end.
#Fun<erl_eval.12.111823515>
4> File = fun(Parent,F) -> io:format("\n\t~p contains File: ~p~n",[Parent,F]) end.
#Fun<erl_eval.12.111823515>
5> file_scavenger_utilities:spread_directory(Dir,TopDir,File,Folder).
    "E:/Ruth" contains File: "e:/Ruth/Thumbs.db"        

    "E:/Ruth" contains File: "e:/Ruth/Robert Passport.pdf"

    "E:/Ruth" contains File: "e:/Ruth/new mixtape.mp3"

    "E:/Ruth" contains File: "e:/Ruth/Manning - Java 3d Programming.pdf"

    "E:/Ruth" contains File: "e:/Ruth/jcrea350.zip"

    "E:/Ruth" contains Folder: "e:/Ruth/java-e books"

    "E:/Ruth" contains File: "e:/Ruth/Java Programming Unleashed.pdf"        

    "E:/Ruth" contains File: "e:/Ruth/Java Programming Language Basics.pdf"

    "E:/Ruth" contains File: "e:/Ruth/java-e books/vb blackbook.pdf.pdf"

Muzaaya Joshua
  • 7,736
  • 3
  • 47
  • 86