A questions about @Files "+d"

#1
Issue: I wanted to count the number of subdirectories of a given directory.

Problem: Best described by two commands and their results:

Code:
[Z:\]dir Sample /K /M
11/09/2011   3:37         <DIR>    .
11/09/2011   3:37         <DIR>    ..

[Z:\]Echo  %@Files[Sample]
 2
As can be seen, the dot and double dot entries (the only entries in the "Sample" directory) are being counted. The For" command has a "/H(ide dots)" parameter for use when directories are explicitly included by using the "/A:" parameter with "+D" included as part of its value. However, I'm not finding an "equivalent" option for the "@Files" function. Is there a way to achieve this result other than a really ugly method of doing something like:
Code:
[Z:\]UnsetArray /Q TreeList

[Z:\]SetArray TreeList[2048]

[Z:\]Echo %@ExecArray[TreeList, Tree C:\WinDDK] >NUL:

[Z:\]Echo %@Eval[%_ExecArray-2]
1929

[Z:\]UnsetArray /Q TreeList
Or even:
Code:
[Z:\]Set Count=0

[Z:\]For /R C:\WinDDK /D %D in (*) do (Set Count=%@Inc[%Count])

[Z:\]Echo %Count
1929
For the "Sample" directory, both of the above code sections return 0, as they should.

By the way, as an aside I had a real problem with the above text within [CODE] and [/CODE] "sections" (or even [HTML] and [HTML] sections) being seriously "reformatted" in a very destructive way. I've figured out a way to work around the problem for now, but I consider it to be both ugly and stupid (if anybody's interested in it I have no problem with "sharing" it; but, again, I think that it is both ugly and stupid.) I tend to think that it is primarily the "<" and/or the ">" character(s), but I don't know that for sure. It seems to me that someone offered a solution to this problem in the past, but my recollection is both that it wasn't all that easy and that made it too complicated for me to remember given my often-mentioned bad memory. I couldn't find those postings, but again, I wasn't quite sure what to "search" for. If that was the "best" solution, can somebody "point to" where it is and I'll copy it off of this website to somewhere where I can find it in the future? Other than that, is there an "official" way to "work around" this problem?
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
3,552
46
Albuquerque, NM
prospero.unm.edu
#3
As can be seen, the dot and double dot entries (the only entries in the "Sample" directory) are being counted. The For" command has a "/H(ide dots)" parameter for use when directories are explicitly included by using the "/A:" parameter with "+D" included as part of its value. However, I'm not finding an "equivalent" option for the "@Files" function.
It seems to me that the simplest workaround would be to check whether you're looking at a root directory and, if not, just subtract two from the value returned by %@FILES[*,-D]. Is there another situation where you wouldn't see the . and .. entries? (Ancient Novell file servers...?)
 
#6
On Wed, 09 Nov 2011 11:02:44 -0500, rconn <> wrote:

|%@files[[!.][!.]*,+d]

That seems to exclude directories with single letter names and ones with names
like ".source".

Code:
v:\> dir /a:d

 Volume in drive V is DATA           Serial number is c007:d3e4
 Directory of  V:\*

2011-11-09  11:18         <DIR>    .
2011-11-09  11:18         <DIR>    ..
2011-11-09  11:18         <DIR>    .source
2010-11-23  20:29         <DIR>    4Utils
2011-11-09  11:12         <DIR>    a
2011-09-17  20:43         <DIR>    foo
2011-09-14  18:33         <DIR>    InternetRegistries
2011-10-03  22:44         <DIR>    iptest
2011-03-02  14:30         <DIR>    sysutils UNZIPPED
2011-07-22  11:39         <DIR>    tcmd12help
2011-03-02  14:26         <DIR>    zippe[er]s
2010-09-12  09:43         <DIR>    ziptest
                 0 bytes in 0 files and 12 dirs
     6,750,965,760 bytes free

v:\> echo %@files[[!.][!.]*,+d]
8
I count 10.

I doubt anyone, ever, wants "." and ".." included in @FILES[]'s count.
 
#7
On Wed, 09 Nov 2011 11:13:02 -0500, Charles Dye <> wrote:

|---Quote (Originally by rconn)---
|%@files[[!.][!.]*,+d]
|---End Quote---
|Hmm. The help page for @FILES does say that "exclusion ranges are not supported."

They're not ranges (no '/').
 
#9
On Wed, 09 Nov 2011 10:00:24 -0500, Charles Dye <> wrote:

|It seems to me that the simplest workaround would be to check whether you're looking at a root directory and, if not, just subtract two from the value returned by %@FILES[*,-D]. Is there another situation where you wouldn't see the . and .. entries? (Ancient Novell file servers...?)

A little tricky since SUBST's drives are treated like their target.

And remote dirs are handled a bit funny. Apparently DIR doesn't show/count "."
and ".." while @FILES[] does.

Code:
v:\> dir \\lucky\e$

 Directory of  \\lucky\e$\*

2010-02-24  13:03         <DIR>    4ntlogs
2011-11-05  20:23         <DIR>    anonymous
2010-02-24  15:38         <DIR>    Logs
2010-02-24  15:38         <DIR>    MAIL
2010-02-25  23:05         <DIR>    ShrDump
2011-11-09  10:56         <DIR>    Temp
2010-02-24  16:57         <DIR>    Users
2011-10-18  11:06         <DIR>    Workplace
                 0 bytes in 0 files and 8 dirs
    23,547,125,760 bytes free

v:\> echo %@files[\\lucky\e$,+d]
10

v:\>
I didn't try a mapped network drive.
 
#11
From: vefatica
| rconn wrote:
|
|| %@files[[!.][!.]*,+d]
|
| That seems to exclude directories with single letter names and ones
| with names like ".source".

Unfortunately (as his example, not quoted, shows) Vince is right... Any directory with name starting with at least one period ("hidden" directories in POSIX file systems), and any directory with a single-character name are excluded from the count.

Another issue is that in root directories there is no . or .. directory, yet both of the conditional expressions "isdir ." and "isdir .." are TRUE in every directory, including root directories, hence a seemingly simple test for root directory that does not providfails.

| I doubt anyone, ever, wants "." and ".." included in @FILES[]'s count.

Agreed. In a separate post I provided a function that does what the OP wants.
--
Steve
 
#12
From: Charles Dye
| (But I think an exclusion range is what's really wanted here.)

and here it is:

function directories=`%@eval[ %@files[*,d] - %@files[[.],d] - %@files[[.][.],d] ]`

which just reduces the "simple" directory count by the number of . and the number of .. directories; note how the use of . as a wildcard was suppressed by using it as single-character list of acceptable characters.

On my winXP system this function works correctly in both root directories and subdirectories of local and mapped network drives as well as when accessing the mapped network drive by its UNC.
--
HTH, Steve
 
#15
It seems to me that the simplest workaround would be to check whether you're looking at a root directory and, if not, just subtract two from the value returned by %@FILES[*,-D]. Is there another situation where you wouldn't see the . and .. entries? (Ancient Novell file servers...?)
Charles, that really isn't a "solution", but for a reason that I didn't note in my original posting because I didn't (probably incorrectly) feel that it was all that relevant. And that is the fact that I am using the "@Files" function with the "/S" option and that is failing; specifically "Echo %@Files[/S C:\WinDDK,+d]" yeilds "5789", whereas the "correct" answer is "1929", and I have (just again!) verified that fact using two completely separate and totally unrelated ways (which I dreamed up "in desperation", so to speak, and neither of which is particularly "optimal" in my opinion; although I will admit that either of them will get the job done "in a pinch". And I no absolutely no way to "convert" that "5789" to "1929", and I've spent some time trying. ("(5789-2)/3" works in this case, but it did not seem to work completely accurately (it wasn't off by much, but it was off) for other directory trees that I tried it on. (Although it is completely possible given my previously mentioned disabilities that I made a stupid mistake somewhere along the line.)
 
#16
From: Charles Dye
function directories=`%@eval[ %@files[*,d] - %@files[[.],d] - %@files[[.][.],d] ]`
--
HTH, Steve
Steve, that does not work at all (TCC: Divide by zero ...") with the "/S" option (which I had neglected to mention in my original post because I incorrectly didn't think that it was particularly relevant). Try it yourself.
 
#18
On Wed, 09 Nov 2011 16:42:11 -0500, mathewsdw <> wrote:

|---Quote (Originally by Steve Fabian)---
|From: Charles Dye
|function directories=`%@eval[ %@files[*,d] - %@files[[.],d] - %@files[[.][.],d] ]`

How elegant (?) do you want to get. Perhaps an approach based on this

Code:
v:\> echo %@word[" ^t",6,%@execstr[dir /h /a:d /s /u c:\ | tail /n2]]
4,106
That probably needs tidying up and I have no idea if it gives (or could give) a
correct answer.

The question should not be hard to answer.
 
#19
On Wed, 09 Nov 2011 16:54:39 -0500, David Marcus <> wrote:

|The following seems to work, although I don't know why:
|
|
|HTML:
|---------
|echo %@files[/s [.],d]
|---------

That counts the dirs named "." (one in each subdir) ... sneaky.

The [] is not even necessary.

Code:
v:\> echo %@files[/s c:\.,d]
4106

v:\> echo %@word[" ^t",6,%@execstr[dir /h /a:d /s /u c:\ | tail /n2]]
4,106
 
#20
From: mathewsdw
| Originally Posted by Steve Fabian
| function directories=`%@eval[ %@files[*,d] - %@files[[.],d] -
| %@files[[.][.],d] ]`

OOPS! You managed to get your response deleted by overquoting - do NOT quote signatures!

The search through a whole hierarchy is qualitatively different than doing it for a single directory. You found a "divide by zero" error in the /S version - please show what you used!

When you go through the tree, two significant issues come up:
1/ do you want to count junctions / symbolic links?
2/ if you want to count them, do you want to also descend into them?

Below it is assumed that the answer is NO to both questions, OTOH it is assumed that you want to count hidden directories.

function tree_count=`%@execstr[ ( pushd %1 %+ set __n=0 %+ global /i /q /h /n set __n=%@inc[%__n] %+ popd %+ echo %__n ) ]`

This function assumes that its only parameter is the root of the directory tree to be checked, and returns the count of directories it found. BEWARE! NOT TESTED! It may be necessary to initialize the variable __N to a value different than zero, possibly -1.
--
HTH, Steve
 
#21
That counts the dirs named "." (one in each subdir) ... sneaky.
But, I don't understand why it doesn't also count the directory you are in:

HTML:
C:\Junk>dir

 Volume in drive C is TI105444W0C    Serial number is 709c:de9d
 Directory of  C:\Junk\*

11/09/2011   5:38p        <DIR>    .
11/09/2011   5:38p        <DIR>    ..
                 0 bytes in 0 files and 2 dirs
   242,054,033,408 bytes free

C:\Junk>echo  %@files[[.],d]
 1

C:\Junk>echo  %@files[/s [.],d]
 0
 
#22
The following seems to work, although I don't know why:

HTML:
echo %@files[/s [.],d]
Dave, I also don't know why, but it doesn't work (at least in all situations) when compared to two, again completely unrelated, "solutions" that I have found. (Specifically yours says "156", both of my alternatives say "153".) The main remaining "problems" with my alternatives are that they can not be turned into "functions", which absolutely would be the most desirable "solution".
 
#24
On Wed, 09 Nov 2011 16:54:39 -0500, David Marcus <> wrote:

|The following seems to work, although I don't know why:
|
|
|HTML:
|---------
|echo %@files[/s [.],d]
|---------

That counts the dirs named "." (one in each subdir) ... sneaky.

The [] is not even necessary.

Code:
v:\> echo %@files[/s c:\.,d]
4106

v:\> echo %@word[" ^t",6,%@execstr[dir /h /a:d /s /u c:\ | tail /n2]]
4,106
Vince, I again really don't understand at all. "%@Files[/S [.],+d]" produces "153" (off by three when compared to either of my "alternatives" which I am quite confident in), whereas "%@Files[/S .,+d]" produces 469!!!!!
 
#25
Why does "%@Files[/s *,d]" produce a different result than the others below?

HTML:
C:\Junk>dir

 Volume in drive C is TI105444W0C    Serial number is 709c:de9d
 Directory of  C:\Junk\*

11/09/2011   6:17p        <DIR>    .
11/09/2011   6:17p        <DIR>    ..
                 0 bytes in 0 files and 2 dirs
   242,054,496,256 bytes free

C:\Junk>echo %@Files[*,d]
2

C:\Junk>echo %@Files[*,+d]
2

C:\Junk>echo %@Files[/s *,d]
0

C:\Junk>echo %@Files[/s *,+d]
2
 
#26
What other methods are you using? What does "ffind /s /a:d *" give?
Dave, solution 1, which I think is the "best" solution:
Code:
Set Count=0
For /R Z:\ /D %D in (*) Do (Set Count=%@Inc[%Count])
Echo %Count
And solution 2:
Code:
UnsetArray /Q TreeList
SetArray TreeList[2048]
Echo %@ExecArray[TreeList, Tree C:\WinDDK] >NUL:
Echo %@Eval[%_ExecArray-2]
again produces the same answer, and, in particular, I can not think of any reason at all which would make the results of "Solution 1" incorrect.

FFind produces "157" in this one particular situation, where, again, the "correct" answer is, as far as I can tell, "153".

Update: I just took the time to "investigate" the difference between my "For" loop and the results returned by "FFind". It turns out that "FFind" counts the "System Volume Information" and "$RECYCLE.BIN" directories, whereas the "For" loop does not. (And neither does the "Tree" command for that matter.) However, since I don't want to count hidden and system directories, that's perfectly OK with me. And adding "/A:" (with no attributes specified) to the "For" loop the answer is off by 1 (i.e. "156") because that "For" loop is not counting a "$RECYCLE.BIN" sub-directory. (I don't know why it is not counting that "$RECYCLE.BIN" sub-directory, but I don't really care and there is a long and totally unimportant reason as to why there is a "$RECYCLE.BIN" sub-directory in the first place, particularly since I don't want to "count" that directory anyway.)
 

samintz

Scott Mintz
May 20, 2008
1,270
11
Solon, OH, USA
#27
So far the discussion has been all about @files and not necessarily answering the original question, which was counting subdirectories.

This command works for me:
Code:
dir /h /u2 /s
And if you specifically want the count so you can use it in a variable, then this works:
Code:
function subdirs=`%@word[" ",6,%@execstr[4,dir /h /u2 /s %$]]`

echo %@subdirs[]
echo %@subdirs[..]
echo %@subdirs[c:\windows]
-Scott
 

rconn

Administrator
Staff member
May 14, 2008
10,406
95
#28
> This command works for me:
>
> Code:
> ---------
> dir /h /u2 /s
> ---------
> And if you specifically want the count so you can use it in a variable,
then

> this works:
>
> Code:
> ---------
> function subdirs=`%@word[" ",6,%@execstr[4,dir /h /u2 /s %$]]`
>
> echo %@subdirs[]
> echo %@subdirs[..]
> echo %@subdirs[c:\windows]
> ---------
If you're using v13, you can use the %_dir_dirs internal variable, which
would be simpler.
 
#29
On Wed, 09 Nov 2011 18:09:16 -0500, mathewsdw <> wrote:

|Vince, I again really don't understand at all. "%@Files[/S [.],+d]" produces "153" (off by three when compared to either of my "alternatives" which I am quite confident in), whereas "%@Files[/S .,+d]" produces 469!!!!!

I'm pretty confused myself.

Code:
v:\> echo %@files[/s d:\[.],d]
1450

v:\> echo %@files[/s d:\.,d]
1450

v:\> echo %@files[/s d:\.,+d]
4320

v:\> echo %@files[/s d:\[.],+d]
1435
The GLOBAL routine (see below) says 1443.

At this point, I have no idea how many directories are on my D drive.

Of these (below) the one with the faulty syntax (no '+') gets it right (it
doesn't count the top level, though DIR shows "." inside v:\, v:\being a SUBST).

Code:
v:\> echo %@files[/s .,d]
19

v:\> echo %@files[/s .,+d]
59
Has anyone mentioned GLOBAL yet?

Code:
v:\> set count=0

v:\> global /q /h set /a count+=1 > NUL

v:\> echo %count
20
 

samintz

Scott Mintz
May 20, 2008
1,270
11
Solon, OH, USA
#30
The internal variable is only set after the corresponding DIR command has
been run as near as I can tell.

So a corresponding function could be:
Code:
function subdirs=`%@execstr[(dir /shu2k %$ > NUL) & echo %_dir_dirs]`
-Scott




If you're using v13, you can use the %_dir_dirs internal variable, which
would be simpler.