Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

foolproof subdirectory enumeration

27 views
Skip to first unread message

Liviu

unread,
Jul 24, 2011, 9:14:04 PM7/24/11
to
Point of the exercise is to recurse into all subdirectories of a given
root directory, and execute a call'able batch (or label) in each. Basic
as this may sound, yet I haven't found a definitive answer elsewhere, so
here is my attempt at working this out, copied below. Critique and
alternatives most welcome.

Difficulties to consider:
- hidden directories must be listed;
- directories with Unicode names (outside the current codepage) must be
processed;
- special batch characters (%!&^ etc) must be supported in directory
names.

Liviu

P.S. Save the snippet below as, say echoTree.cmd, which simply echoes
the relative pathnames of all subdirectories. Run with a target
directory name as the argument, or none for the '.' current directory.
Redirect '> nul' to suppress the regular listing of subdirectory names,
and only see the error stream output.

@rem ===================================
@echo off
setlocal disabledelayedexpansion

if not "~1"=="" pushd "%~1" || goto :done
call :dirs
if not "~1"=="" popd

:done
endlocal
goto :eof

:dirs
setlocal disabledelayedexpansion
set "cwd=%cd%"
@rem drop trailing '\' when %cd% is a drive root
if "%cwd:~-1%"=="\" set "cwd=%cwd:~0,-1%"
@rem 'for /r %%d in (.)' recurses into all subdirectories, works with
@rem - hidden directories, unlike 'for /d %%d in (*)'
@rem - unicode names, unlike 'for /f "delims=" %%d in ('dir /ad /b ..')'
for /r %%d in (.) do (
@rem drop trailing '\.' appended by 'for /r' to each %%~d
set "d=%%~fd"
setlocal enabledelayedexpansion
@rem convert to relative path rooted off %cwd%
set "d=.!d:%cwd%=!"
@rem double quoted % percents since 'call' will halve (or lose) them
call :dir "!d:%%=%%%%!"
endlocal
)
endlocal
goto :eof

:dir
setlocal disabledelayedexpansion
@rem quote entire 'set' argument for & ampersands to be passed literally
set "d=%~1"
@rem halve quoted ^ carets since 'call' did double them
set "d=%d:^^=^%"
@rem run 'echoTree ^>nul' to only see the error output
if not exist "%d%" echo *** error: "%d%" not found 1>&2
setlocal enabledelayedexpansion
echo !d!
endlocal
endlocal
goto :eof
@rem ===================================


foxidrive

unread,
Jul 24, 2011, 9:33:20 PM7/24/11
to
On 25/07/2011 11:14, Liviu wrote:
> Point of the exercise is to recurse into all subdirectories of a given
> root directory, and execute a call'able batch (or label) in each. Basic
> as this may sound, yet I haven't found a definitive answer elsewhere, so
> here is my attempt at working this out, copied below. Critique and
> alternatives most welcome.
>
> Difficulties to consider:
> - hidden directories must be listed;
> - directories with Unicode names (outside the current codepage) must be
> processed;
> - special batch characters (%!&^ etc) must be supported in directory
> names.
>
> Liviu

I think this should work.


@echo off
for /f "delims=" %%a in ('dir \ /a:d /b /s') do (
pushd "%%a"
cd
popd
)


--
Regards,
Mic

Liviu

unread,
Jul 24, 2011, 9:48:31 PM7/24/11
to
Thanks, but I can only wish it were that easy ;-)

"foxidrive" <foxi...@gotcha.woohoo.invalid> wrote in message
news:JJ3Xp.39728$z67....@newsfe10.iad...


> On 25/07/2011 11:14, Liviu wrote:
>> Point of the exercise is to recurse into all subdirectories of a
>> given root directory, and execute a call'able batch (or label) in

>> each. [...] Difficulties to consider:


>> - hidden directories must be listed;
>> - directories with Unicode names (outside the current codepage)
>> must be processed;
>> - special batch characters (%!&^ etc) must be supported in
>> directory names.
>

> I think this should work.
>
> @echo off
> for /f "delims=" %%a in ('dir \ /a:d /b /s') do (
> pushd "%%a"
> cd
> popd
> )

Directories with ! exclamation marks in the name will fail, and so
will those containing Unicode characters outside the current codepage
of the cmd prompt.

You can replace

> pushd "%%a"

with

> pushd "%%a" || echo *** error: "%%a" not found 1>&2

and run the batch >nul to verify.

Liviu


foxidrive

unread,
Jul 24, 2011, 10:03:35 PM7/24/11
to
On 25/07/2011 11:48, Liviu wrote:
> Thanks, but I can only wish it were that easy ;-)
>
> "foxidrive"<foxi...@gotcha.woohoo.invalid> wrote in message
> news:JJ3Xp.39728$z67....@newsfe10.iad...
>> On 25/07/2011 11:14, Liviu wrote:
>>> Point of the exercise is to recurse into all subdirectories of a
>>> given root directory, and execute a call'able batch (or label) in
>>> each. [...] Difficulties to consider:
>>> - hidden directories must be listed;
>>> - directories with Unicode names (outside the current codepage)
>>> must be processed;
>>> - special batch characters (%!&^ etc) must be supported in
>>> directory names.
>>
>> I think this should work.
>>
>> @echo off
>> for /f "delims=" %%a in ('dir \ /a:d /b /s') do (
>> pushd "%%a"
>> cd
>> popd
>> )
>
> Directories with ! exclamation marks in the name will fail,

No, they will be fine. Unless you start your command processor with delayedexpansion as default.

Output from the batch file:

===[paste]===
D:\ADD\test\a % b
D:\ADD\test\c ! d
D:\ADD\test\e ^ f
===[paste]===

> and so
> will those containing Unicode characters outside the current codepage
> of the cmd prompt.

I don't do unicode - sorry.

--
Regards,
Mic

Liviu

unread,
Jul 24, 2011, 10:31:43 PM7/24/11
to
"foxidrive" <foxi...@gotcha.woohoo.invalid> wrote...

> On 25/07/2011 11:48, Liviu wrote:
>>
>> Directories with ! exclamation marks in the name will fail,
>
> No, they will be fine. Unless you start your command processor
> with delayedexpansion as default.

You are right, I had delayedexpansion turned on. !s work otherwise.

>> and so will those containing Unicode characters outside the current
>> codepage of the cmd prompt.
>
> I don't do unicode - sorry.

No problem, and I actually sympathize. Yet among the files I have to
deal with (but not control the naming thereof) there is a growing number
with unicode characters. The cmd console itself has some decent support
for unicode, but like everything else batch related it's neither obvious
nor consistent. FWIW the batch I posted originally handles all
combinations of unicode and special characters correctly, at least for
the test cases I've thrown at it so far.

Liviu

foxidrive

unread,
Jul 24, 2011, 11:10:35 PM7/24/11
to
On 25/07/2011 12:31, Liviu wrote:
>>> and so will those containing Unicode characters outside the current
>>> codepage of the cmd prompt.

I didn't notice your REM regarding this, sorry.


>> I don't do unicode - sorry.
>
> No problem, and I actually sympathize. Yet among the files I have to
> deal with (but not control the naming thereof) there is a growing number
> with unicode characters. The cmd console itself has some decent support
> for unicode, but like everything else batch related it's neither obvious
> nor consistent. FWIW the batch I posted originally handles all
> combinations of unicode and special characters correctly, at least for
> the test cases I've thrown at it so far.

I can't even create a unicode foldername in XP.


Can you use the shortname for the unicode folders? You might run into the shortname bug but it'd be interesting if it overcomes that unicode issue anyway.


--
Regards,
Mic

Liviu

unread,
Jul 25, 2011, 12:27:49 PM7/25/11
to
Resending the post because I realized that the (tiny) attachment
referenced at #4 below caused it to be rejected by text-only servers
such as aioe, teranews. The file in question can be downloaded from
http://dl.dropbox.com/u/36296631/unirange.rar instead.
________

"foxidrive" <foxi...@gotcha.woohoo.invalid> wrote...


>
> I can't even create a unicode foldername in XP.

Some ways to do that, in no particular order...

1. Enable a few additional languages and switch between them to
enter national characters. For example, western, greek and cyrillic
should be enough to guarantee that there is no single codepage
containing all of the respective characters.

2. Enable and use Alt+ to enter individual unicode characters by
codepoint (http://www.fileformat.info/tip/microsoft/enter_unicode.htm).

3. Copy/paste folder names from existing Unicode text e.g.
http://www.ltg.ed.ac.uk/~richard/unicode-sample.html
http://www.alanwood.net/unicode/unicode_samples.html

4. Un-RAR the attached.

> Can you use the shortname for the unicode folders? You might
> run into the shortname bug but it'd be interesting if it overcomes
> that unicode issue anyway.

SFNs work the same for unicode (except when they don't ;-)).
But in my actual use case, I need the original unicode names, and
the SFNs don't really help.

Liviu


foxidrive

unread,
Jul 25, 2011, 5:55:24 PM7/25/11
to
On 26/07/2011 02:27, Liviu wrote:
> Resending the post because I realized that the (tiny) attachment
> referenced at #4 below caused it to be rejected by text-only servers
> such as aioe, teranews. The file in question can be downloaded from
> http://dl.dropbox.com/u/36296631/unirange.rar instead.
> ________
>
> "foxidrive"<foxi...@gotcha.woohoo.invalid> wrote...
>>
>> I can't even create a unicode foldername in XP.
>
> Some ways to do that, in no particular order...
>
> 1. Enable a few additional languages and switch between them to
> enter national characters. For example, western, greek and cyrillic
> should be enough to guarantee that there is no single codepage
> containing all of the respective characters.
>
> 2. Enable and use Alt+ to enter individual unicode characters by
> codepoint (http://www.fileformat.info/tip/microsoft/enter_unicode.htm).
>
> 3. Copy/paste folder names from existing Unicode text e.g.
> http://www.ltg.ed.ac.uk/~richard/unicode-sample.html
> http://www.alanwood.net/unicode/unicode_samples.html
>
> 4. Un-RAR the attached.
>

Thanks for the Rar file, easy peasy.
This seems to work for the samples listed.


@echo off


@rem 'for /r %%d in (.)' recurses into all subdirectories, works with
@rem - hidden directories, unlike 'for /d %%d in (*)'
@rem - unicode names, unlike 'for /f "delims=" %%d in ('dir /ad /b ..')'
for /r %%d in (.) do (

for /f "delims=" %%e in ("%%d") do echo %%~nxe
)
)
pause


--
Regards,
Mic

billious

unread,
Jul 25, 2011, 8:10:36 PM7/25/11
to

"foxidrive" <foxi...@gotcha.woohoo.invalid> wrote in message
news:oDlXp.16904$wc1....@newsfe04.iad...
[snip]

>
> Thanks for the Rar file, easy peasy.
> This seems to work for the samples listed.
>
>
> @echo off
> @rem 'for /r %%d in (.)' recurses into all subdirectories, works with
> @rem - hidden directories, unlike 'for /d %%d in (*)'
> @rem - unicode names, unlike 'for /f "delims=" %%d in ('dir /ad /b ..')'
> for /r %%d in (.) do (
> for /f "delims=" %%e in ("%%d") do echo %%~nxe
> )
> )
> pause
>
>
>
>
> --
> Regards,
> Mic

I used the 'dir' approach, which seemed to work:

@echo off
for /f "delims=" %%d in ( ' dir /b /ad a* ' ) do (
for %%n in ("%%~fd") do echo\%%n
pushd %%~fd
call :show
popd
)
goto :eof
:show
echo\now at "%cd%"
goto :eof

( the 'a*' was simply to reduce the number of targets )

Seemed to work with !^%& and unicode.

And to create a unicode directory name, I used "character map" to paste
hieroglyphics to the clipboard.


Liviu

unread,
Jul 25, 2011, 9:12:35 PM7/25/11
to
"foxidrive" <foxi...@gotcha.woohoo.invalid> wrote...

>
> This seems to work for the samples listed.
>
> @echo off
> for /r %%d in (.) do (
> for /f "delims=" %%e in ("%%d") do echo %%~nxe
> )

Yes, and I think the inner 'for' can be replaced with simply
'echo %%~nxd' without any loss.

The extra gymnastics in my original batch was only needed
in order for it to work with a call'd label vs. an inlined echo.

Liviu

Liviu

unread,
Jul 25, 2011, 9:12:31 PM7/25/11
to
"billious" <billio...@hotmail.com> wrote...

>
> I used the 'dir' approach, which seemed to work:
>
> @echo off
> for /f "delims=" %%d in ( ' dir /b /ad a* ' ) do (
> for %%n in ("%%~fd") do echo\%%n
> pushd %%~fd
> call :show
> popd
> )
> goto :eof
> :show
> echo\now at "%cd%"
> goto :eof
>
> ( the 'a*' was simply to reduce the number of targets )
>
> Seemed to work with !^%& and unicode.

What version of Windows are you running?

The above doesn't work here (xp sp3) with the directories in my sample
.rar file. Saving it to a batch and running it '>nul' shows a few errors

|| The system cannot find the path specified.

coming from the 'pushd' line. If you replace that line with

> pushd %%~fd || echo *** error: "%%~fd" not found 1>&2

you'll get the directory names on which it fails. The exact ones may
depend on the current codepage of your cmd prompt, but I tried it with
437, 850 and 1252, and none of them works here without error.

> And to create a unicode directory name, I used "character map" to
> paste hieroglyphics to the clipboard.

If you happened to only paste characters which exist by chance in the
current cmd codepage, then the 'for /f in ('dir')' would work, indeed.

Liviu


billious

unread,
Jul 27, 2011, 2:07:18 AM7/27/11
to

"Liviu" <lab...@gmail.c0m> wrote in message
news:4e2e14bd$0$29797$c3e8da3$cc4f...@news.astraweb.com...

Quite correct - inadequate testing. I just established a directory
containing 'numero'.

I'd suggest that the cause is that 'dir...' is designed to display and its
output is massaged - undisplayable replaced by "?". FOR /D on the other hand
is not intended to display and hence deals with raw data.

I seem to recall that there is some problem with FOR/D, but the details
escape me - which is why I wanted to avoid it. Ah well - led up the garden
path again...


Liviu

unread,
Aug 6, 2011, 12:46:07 AM8/6/11
to
"Liviu" wrote...

> Point of the exercise is to recurse into all subdirectories of a given
> root directory, and execute a call'able batch (or label) in each.
>
> [...] Difficulties to consider:
> [...] - special batch characters (%!&^ etc) must be supported
> in directory names.

For the record, the batch file as originally posted fails if the target
directory has an "=" equal sign in its name.

> Critique and alternatives most welcome.

Self-critique done, alternatives still welcome ;-)

Liviu


0 new messages