27

the function dir returns an array like

.
..
Folder1
Folder2

and every time I have to get rid of the first 2 items, with methods like :

for i=1:numel(folders)
    foldername = folders(i).name;
    if foldername(1) == '.' % do nothing
            continue;
    end
    do_something(foldername)
end

and with nested loops it can result in a lot of repeated code.

So can I avoid these "folders" by an easier way?

Thanks for any help!

Edit:

Lately I have been dealing with this issue more simply, like this :

for i=3:numel(folders)
    do_something(folders(i).name)
end

simply disregarding the first two items.

BUT, pay attention to @Jubobs' answer. Be careful for folder names that start with a nasty character that have a smaller ASCII value than .. Then the second method will fail. Also, if it starts with a ., then the first method will fail :)

So either make sure you have nice folder names and use one of my simple solutions, or use @Jubobs' solution to make sure.

jeff
  • 13,055
  • 29
  • 78
  • 136

10 Answers10

56

A loop-less solution:

d=dir;
d=d(~ismember({d.name},{'.','..'}));
Trisoloriansunscreen
  • 1,543
  • 1
  • 15
  • 27
16

TL; DR

Scroll to the bottom of my answer for a function that lists directory contents except . and ...

Detailed answer

The . and .. entries correspond to the current folder and the parent folder, respectively. In *nix shells, you can use commands like ls -lA to list everything but . and ... Sadly, MATLAB's dir doesn't offer this functionality.

However, all is not lost. The elements of the output struct array returned by the dir function are actually ordered in lexicographical order based on the name field. This means that, if your current MATLAB folder contains files/folders that start by any character of ASCII code point smaller than that of the full stop (46, in decimal), then . and .. willl not correspond to the first two elements of that struct array.

Here is an illustrative example: if your current MATLAB folder has the following structure (!hello and 'world being either files or folders),

.
├── !hello
└── 'world

then you get this

>> f = dir;
>> for k = 1 : length(f), disp(f(k).name), end
!hello
'world
.
..

Why are . and .. not the first two entries, here? Because both the exclamation point and the single quote have smaller code points (33 and 39, in decimal, resp.) than that of the full stop (46, in decimal).

I refer you to this ASCII table for an exhaustive list of the visible characters that have an ASCII code point smaller than that of the full stop; note that not all of them are necessarily legal filename characters, though.

A custom dir function that does not list . and ..

Right after invoking dir, you can always get rid of the two offending entries from the struct array before manipulating it. Moreover, for convenience, if you want to save yourself some mental overhead, you can always write a custom dir function that does what you want:

function listing = dir2(varargin)

if nargin == 0
    name = '.';
elseif nargin == 1
    name = varargin{1};
else
    error('Too many input arguments.')
end

listing = dir(name);

inds = [];
n    = 0;
k    = 1;

while n < 2 && k <= length(listing)
    if any(strcmp(listing(k).name, {'.', '..'}))
        inds(end + 1) = k;
        n = n + 1;
    end
    k = k + 1;
end

listing(inds) = [];

Test

Assuming the same directory structure as before, you get the following:

>> f = dir2;
>> for k = 1 : length(f), disp(f(k).name), end
!hello
'world
jub0bs
  • 60,866
  • 25
  • 183
  • 186
  • Is there a guarantee by documentation that these entries are always the first two? – glglgl Dec 06 '14 at 23:53
  • 2
    @glglgl I don't have a reference for that, but I'll look for one. In my experience, `.` and `..` have always been the first two elements of the struct array. – jub0bs Dec 06 '14 at 23:55
  • Thanks for your answer. Even though it is guaranteed that they are the first 2 elements, I still don't like for loops starting from 3, or ending in numel(x)-2, so I'm looking for a somehow easier way, if possible. – jeff Dec 07 '14 at 02:41
  • 2
    @Jubobs - From what I've dealt with in `dir`, they seem to always be the first two elements. BTW +1. – rayryeng Dec 07 '14 at 06:45
  • 2
    This is definitely true on OS X. In most cases, it would probably be a bad idea to used these other character to prefix a file or folder. However, one place where this could be relevant to Matlab in particular is for packages in objected-oriented programming. The `+` character is used to [prefix package folders](http://www.mathworks.com/help/matlab/matlab_oop/scoping-classes-with-packages.html#brf3g8k). The decimal value for the `+` character is 43 vs. 46 for `.`. – horchler Dec 07 '14 at 18:55
  • 4
    `if any(strcmp(listing(k).name, {'.', '..'}))` looks shorter and neater. But it does the same. – glglgl Dec 09 '14 at 06:52
  • @glglgl You're right; it's much neater. I've edited my answer accordingly. – jub0bs Jan 26 '15 at 16:15
  • downvoted because Trisoloriansunscreen's answer is much better, but this one appears first because it is the accepted answer – craq Oct 22 '19 at 21:52
  • @craq That's your prerogative. No hard feelings. – jub0bs Oct 22 '19 at 21:59
3

a similar solution from the one suggested by Tal is:

listing = dir(directoryname);
listing(1:2)=[];   % here you erase these . and .. items from listing

It has the advantage to use a very common trick in Matlab, but assumes that you know that the first two items of listing are . and .. (which you do in this case). Whereas the solution provided by Tal (which I did not try though) seems to find the . and .. items even if they are not placed at the first two positions within listing.

Hope that helps ;)

LeChat
  • 466
  • 3
  • 18
  • 4
    This isn't reliable, because the first two elements of the struct array returned by `dir` are not guaranteed to correspond to `.` and `..`. See [my answer](http://stackoverflow.com/a/27337682/2541573). – jub0bs Sep 19 '15 at 13:13
2

If you're just using dir to get a list of files and and directories, you can use Matlab's ls function instead. On UNIX systems, this just returns the output of the shell's ls command, which may be faster than calling dir. The . and .. directories won't be displayed (unless your shell is set up to do so). Also, note that the behavior of this function is different between UNIX and Windows systems.

If you still want to use dir, and you test each file name explicitly, as in your example, it's a good idea to use strcmp (or one of its relations) instead of == to compare strings. The following would skip all hidden files and folder on UNIX systems:

listing = dir;
for i = 1:length(listing)
    if ~strcmp(listing(i).name(1),'.')
        % Do something
        ...
    end
end
Community
  • 1
  • 1
horchler
  • 18,384
  • 4
  • 37
  • 73
  • See my edit. `.` and `..` are not always the first two elements of the struct array. – jub0bs Dec 07 '14 at 17:23
  • 1
    @Jubobs: Thank you. That makes sense. I've updated my answer remove potentially code that could cause issues in rare cases (it would probably be fine in 99.99% of cases). – horchler Dec 07 '14 at 18:44
1

You may also wanna exclude any other files besides removing dots

d = dir('/path/to/parent/folder')
d(1:2)=[]; % removing dots 
d = d([d.isdir])    % [d.isdir] returns a logical array of 1s representing folders and 0s for other entries
Ahmed Abobakr
  • 1,618
  • 18
  • 26
1

I used: a = dir(folderPath);

Then used two short code that return struct:

my_isdir = a([a.isdir]) Get a struct which only has folder info

my_notdir = a(~[a.isdir]) Get a struct which only has non-folder info

1

Combining @jubobs and @Tal solutions:

function d = dir2(folderPath)
% DIR2 lists the files in folderPath ignoring the '.' and '..' paths.

if nargin<1; folderPath = '.'; elseif nargin == 1

d = dir(folderPath);
d = d(~ismember({d.name},{'.','..'}));

end
Lionel Trebuchon
  • 400
  • 6
  • 14
  • You don't need an error message there if you don't use `varargin`. Let MATLAB generate errors if people try to call the function with too many arguments. Simply do `function d=dir2(name)`. You can test `if nargin<1, name='.'; end`. – Cris Luengo Nov 01 '18 at 15:03
  • Edited, following Cris' suggestion. – Lionel Trebuchon Nov 02 '18 at 14:54
1

None of the above puts together the elements as I see the question having being asked - obtain a list only of directories, while excluding the parents. Just combining the elements, I would go with:

function d = dirsonly(folderPath)
% dirsonly lists the unhidden directories in folderPath ignoring '.' and '..'    
% creating a simple cell array without the rest of the dir struct information

if nargin<1; folderPath = '.'; elseif nargin == 1

d = dir(folderPath);
d = {d([d.isdir] & [~ismember({d.name},{'.','..'})]).name}.';

end

if hidden folders in general aren't wanted the ismember line could be replaced with:

d = {d([d.isdir] & [~strncmp({d.name},'.',1)]).name}.';

if there were very very large numbers of interfering non-directory files it might be more efficient to separate the steps:

d = d([d.isdir]);
d = {d([~strncmp({d.name},'.',1)]).name}.';     
PaulS
  • 11
  • 1
1

We can use the function startsWith

folders = dir("folderPath");
folders = string({folders.name});
folders = folders(~startsWith(folders,"."))
Ben Yu
  • 11
  • 1
  • This would remove all files whose names starts with a dot. In the UNIX world, all hidden files start with a dot. So that could be a useful thing or not depending on the goal. – Cris Luengo Dec 24 '20 at 18:44
0

Potential Solution - just remove the fields

Files = dir;
FilesNew = Files(3:end);

You can just remove them as they are the first two "files" in the structure

Or if you are actually looking for specific file types:

Files = dir('*.mat');
Kurt
  • 11
  • 2