List all files and dirs without recursion with junctions

4

3

Is there a native|portable tool that can give me a unicode or a system local-compatible list of all files and directories under a path recursively, without recursing into junction points or links, in Windows?

For example, the built-in dir takedown icacls commands run into an infinite loop with the Application Data directory (1).

EDIT I would like to be able to get a text file or at least an easy clipboard transfer as an output.

n611x007

Posted 2013-01-05T11:00:07.927

Reputation: 5 291

Did you try the tree command in Windows? You can pass the result of this command to a text file – Prasanna – 2016-10-10T12:08:16.627

I would like to avoid using a scripting language (eg. python's os.walk), because I'd prefer a solution more easily portable (eg. a smaller single-file tool) – n611x007 – 2013-01-05T11:00:53.703

Oh, and the tool should handle paths with spaces. :) – n611x007 – 2013-01-05T11:13:57.730

Note the text file doesn't have to be 'plain', it can be xml, csv, tsv, or anything text. – n611x007 – 2013-01-05T11:16:01.837

2I believe you could do this with robocopy, using the /L flag to prevent it from actually doing any copying. – Harry Johnston – 2013-01-07T02:24:10.993

@HarryJohnston indeed, with robocopy /XJ /L /E <dir> <dummy-target>, the /XJ flag skips the junctions and the /L causes to list only! /E is for recursion. Problem: too verbose. But it can be made slightly better with a win32 build of gnu's grep: robocopy /XJ /L /E <dir> <dummy-target> | grep -i "new dir\|new file". Still needs "applying" the containing directory for each file. – n611x007 – 2013-03-22T12:56:32.710

Answers

6

This was somehow a hard question. Even StackOverflow has only fancy solutions.
But here is a simple one for listing all files recursively without junction folder loops.

Use PowerShell and test each file if it contains the attribute "ReparsePoint"

function Recurse($path) {

  $fc = new-object -com scripting.filesystemobject
  $folder = $fc.getfolder($path)

  foreach ($i in $folder.files) { $i | select Path }

  foreach ($i in $folder.subfolders) {
    $i | select Path        
    if ( (get-item $i.path).Attributes.ToString().Contains("ReparsePoint") -eq $false) {        
        Recurse($i.path)
    }
  }
}

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$outputlist = Recurse($scriptPath) | Out-File -Filepath .\outputlist.txt 
  1. Paste the code into a text file and save it as PowerShell (.ps1) script
  2. Place the script inside your desired folder to list all files+folder recursively
  3. Execute the script with PowerShell. A new file called outputlist.txt at the same location will appear

Quick comparison between the Powershell script and a common batch command

powershell  - good, no indefinite loops

enter image description here

batch "DIR /B /S" - bad, indefinite loops through junctions points

enter image description here

nixda

Posted 2013-01-05T11:00:07.927

Reputation: 23 233

To test it, I've just created a junction with sysinternals tool junction.exe in a folder to itself, and unfortunately for me Directory List & Print did get into recursion. – n611x007 – 2013-01-05T12:37:58.663

@naxa A late response. But better late, then never. – nixda – 2013-08-18T17:15:51.777

I love the fact that you sorted it out, and it just works! Thanks nixda! As my original problem was gone (and I don't tend to keep a powershell at home), it took me too some time but I've finally tested it and it works just like I wanted!! Also, I'm absolutely fond of better late than never myself. :) – n611x007 – 2013-09-26T11:05:57.120

1For powershell beginners like me, powershell -ExecutionPolicy Unrestricted -File <yourfilename-here.ps1> will execute the script (my file association was set to notepad :).) – n611x007 – 2013-09-26T11:06:43.077

Well, actually, now that I think of it, I think my original problem was never finished... it got to a point but now it's actually on hold. Hey, thanks, now I can finish the project. :D – n611x007 – 2013-09-26T11:15:53.247

2

1. Use small script, so everything will be listed, but junctions won't be followed.

Works fine on Windows 7, unlike option below in this answer

let's call it dnj.cmd (like in Dir No Junctions)

@(ECHO OFF
    SETLOCAL ENABLEEXTENSIONS
    CHCP 65001>NUL
    IF "%~1"=="" (CALL :listDir .) ELSE CALL :listDir %*
    EXIT /B
)
:listDir <path>
FOR /F "usebackq delims=" %%F IN (`DIR /B /A "%~1"`) DO ECHO %~1\%%~F
(
    IF EXIST "%~1\*.*" FOR /F "usebackq delims=" %%F IN (`DIR /B /AD-L "%~1"`) DO CALL :listDir "%~1\%%~F"
    IF "%~2"=="" EXIT /B
    SHIFT
    GOTO :listDir
)

First FOR/DIR for output, second FOR/DIR for recursion. This way, you can also adjust output parameters (specifically, DIR and ECHO arguments). For example, if you only want flat list of files in output without directories, use

FOR /F "usebackq delims=" %%F IN (`DIR %1 /B /A-D`) DO ECHO %%~F

in place of first FOR.

Invoking: dnj.cmdpath [path [...]]

Output redirection works same way as with conventional DIR, so

cmd /C "dnj.cmd "%USERPROFILE%" | CLIP"

works from Win+R ("Run") prompt.

2. If you're on Windows 10, use DIR /S /A-L

This way, junctions won't be listed, neither will be recurred with /S. On Windows 7, DIR /S /AD-L recurses junctions. Not sure about 8/8.1.

batch with output to text file:

CHCP 65001 & REM for UTF-8
DIR %* /B /S /A-L > out.txt

(pass path and additional DIR args as batch arguments)

cmd.exe to clipboard (can be invoked via Win+R):

cmd /C "CHCP 65001 & DIR "%USERPROFILE%" /B /S /A-L | CLIP"

Clipboard is unicode by default, but without CHCP beforehand, DIR output is in OEM codepage, so some characters may be lost. Quotes after /C are required by CMD syntax, despite it looks weird with inner quotes around %USERPROFILE%.

LogicDaemon

Posted 2013-01-05T11:00:07.927

Reputation: 1 681

1Great suggestion. Your 'dnj' fails if a directory ends in ')'. I think I have it working properly: :listDir <path> ( FOR /F "usebackq delims=" %%F IN (DIR %1 /B /A) DO (SET I="%~1%%F" SET J=!I:"=! ECHO !J! ) FOR /F "usebackq delims=" %%F IN (DIR %1 /B /AD-L) DO (SET I="%~1%%F" CALL :listDir !I! ) EXIT /B ) – trindflo – 2019-05-08T16:14:23.170

@trindflo thanks for pointing it out. I find delayed expansion way slower than normal run, so just excluding ECHO %%~F from (...) group works way better IMO. – LogicDaemon – 2019-05-09T06:48:45.307

2

Using powershell more succinctly:

$items = @(pwd);
while($items){ $newitems = $items | Get-ChildItem | Where-Object -Property Attributes -NotLike *ReparsePoint*; $newitems; $items = $newitems | Where-Object -Property Attributes -Like *Directory* }

To get this all into a text file, just surround the whole thing with parenthesis and pipe it into out-file.

( $items = @(pwd); while($items){ $newitems = $items | gci | where -Property Attributes -NotLike *ReparsePoint*; $newitems; $items = $newitems | where -Property Attributes -Like *Directory* } ) | out-file myfile.txt

kb0

Posted 2013-01-05T11:00:07.927

Reputation: 121

1dont u need a $ at the beginning and | out-file? or is that cuz powershell version diff? – colin lamarre – 2017-12-11T13:00:33.423

1

The original symbolic link for Application Data has special permissions and attributes to prevent Everyone from reading or viewing the content of the target folder (via the symbolic link). This prevents programs from getting caught in a recursive loop. You can easily reapply the permissions and attributes, but be very careful to only apply these permissions and attributes to the symbolic link and not to any actual files or folders. The symbolic link will be an icon in Windows Explorer with a shortcut arrow over the top of a folder icon.

  1. Open Windows Explorer
  2. Tap the alt key, and click Tools, then Options, then click the View tab
  3. Disable the option to [ ] Hide protected operating system files, and enable the option to (x) Show hidden files, folders, or drives.
  4. Navigate to C:\Users\[user]\AppData\Local
  5. Right-click Application Data symbolic link and choose Properties (make sure it's the symbolic link and not a real folder -- it should have a shortcut arrow as an overlay on top of a folder icon)
  6. Click the Security tab, then Advanced, then Change Permissions
  7. Add Everyone, and set the rights for Everyone to:
    • Apply To = This folder only (<-this is important)
    • Permissions = Deny List folder / read data (<- this stops recursion)
  8. Click OK and then click Yes to the warning that a Deny rule is applied before an Allow rule
  9. Click OK again to apply the changes
  10. Click the Owner tab, click Edit, then click Other users or groups
  11. Type SYSTEM, and keep clicking OK until you are back at Windows Explorer

The recursion is now disabled. There is one more thing that you might want to do. Most of these types of symbolic links have special file attributes so that they won't be visible to users. Open a Command Prompt and run the following commands:

cd "%userprofile%\AppData\Local"
attrib +S +H +I /L "Application Data"

The /L option tells attrib to apply +S +H +I to the symbolic link, instead of applying it to the target folder (C:\Users\[user]\AppData\Local).

If you mess up, you can easily remove the symbolic link and recreate it from a Command Prompt. Be careful that you recreate so that it points to the same folder as it did before. The dir /a command will show you where Application Data points to. You should retype the target folder verbatim as the final parameter of the mklink command.

cd "%userprofile%\AppData\Local"
attrib -H -S -I /L "Application Data"
dir /a
rd "Application Data"
mklink /J "%userprofile%\AppData\Local\Application Data" "%userprofile%\AppData\Local"

Now you have an Application Data symbolic link that has the recursion problem. So just following the steps above to fix the recursion issue.

Don't forget to change the settings in step #3 back to their original values, so that you don't continue to see files that should be hidden.

James L.

Posted 2013-01-05T11:00:07.927

Reputation: 322

1

The problem here is that someone has removed the anti recursion from Application Data folder.

It needs to have a DENY to EVERYONE to List Folder/Read Data.

If you want to go into it use the AppData folder which is where it points.

There are two junction points - one in %userprofile% and another in %userprofile%\local.

trigger

Posted 2013-01-05T11:00:07.927

Reputation: 64

1So how does one fix this problem. You have told the author WHAT the problem is but not HOW to fix it. An acceptable answer contains both. – Ramhound – 2015-06-07T18:51:09.493

He can work out him/herself that if it needs something to give it. The poster sabotaged their system. – trigger – 2015-06-07T18:53:01.400

@trigger nice information! I still need the directory walk that avoids the junctions because the Application Data was just an example. It takes too much time to detect all the possible problems. So you need your traversal program to skip recursing into the symlinks. I gave a +1 for the knowledge however! – n611x007 – 2015-06-08T09:43:49.407

I have to agree with Ramhound. Seeing as the person who answered this seems unwilling to provide a complete solution, hopefully someone else will provide both in another answer so that this one can be removed. Also, not sure if the comment about the poster sabotaging their system is supposed to imply that the majority of Windows installs out there don't allow any directory listing tools to recurse into the backwards-compatible WinVista+ %userprofile% junction points, but in my experience this very much depends on the application doing the recursing.

– user66001 – 2016-05-04T18:41:40.997

0

This problem might be due to anti recursion permissions being removed from the Application Data folder (like may apply to any Windows Vista+ Junction Point created for backward-compatibility).

These junction points should have DENY to EVERYONE on List Folder/Read Data permissions, which will take precedence over any ALLOW permissions, and hopefully not be circumvented by any directory recursing program due to the way it accesses the File system.

From https://msdn.microsoft.com/en-us/library/windows/desktop/bb968829(v=vs.85).aspx it would appear Junction points are identified by having the ReparsePoint attribute, alongside the Hidden and System attributes.

The following Powershell command is the basis of what seems to be a simpler solution to excluding Junction Points from directory recursions, than any previous answer.

get-childitem $RootDirectoryForRecusion -recurse -force | where-object {-not ($_.Attributes -match "ReparsePoint" -and $_.Attributes -match "Hidden" -and $_.Attributes -match "System")}

user66001

Posted 2013-01-05T11:00:07.927

Reputation: 1 050

0

In PowerShell 3.0 there is a new parameter Attribute:

Lists directory structure without junction folders:

Get-ChildItem -Path $RootDirectory -Attributes !ReparsePoint -Recurse -Force -ErrorAction SilentlyContinue

Lists junction folders only from a directory structure:

Get-ChildItem -Path $RootDirectory -Attributes ReparsePoint -Recurse -Force -ErrorAction SilentlyContinue

More info: Use PowerShell 3.0 to Filter Files Based on Advanced Attributes

mazarin

Posted 2013-01-05T11:00:07.927

Reputation: 1

2The attribute parameter seems to test the leaf, not the parent directories. When using Get-ChildItem -Attributes !ReparsePoint it seems to happily dive through reparse points and still cause the "path too long" error. – Keith Twombley – 2017-06-06T20:01:52.583

0

In PowerShell,

Get-ChildItem /Recurse <path> |
Where-Object { $_.Mode -notlike 'd*l' } |
ForEach-Object { $_.FullName};

You can just pipe the output to a file if you want to save it.

hwiechers

Posted 2013-01-05T11:00:07.927

Reputation: 253

Should be -Recurse not /Recurse Good answer though. – Marty Neal – 2017-11-06T17:23:23.770

This does not answer the question or solve the problem. The OP put the most important part as the question itself, and repeated it in bold text. "**without recursing into junction points **". This command will absolutely recurse into junctions. It just won't list the symlink on a line by itself. For example: With a symlink named "mysim" pointing to a folder with a file named "myfile", the output generated would not include <path>\mysim but it WOULD recurse and show <path>\mysim\myfile. – Stack of Pancakes – 2018-08-01T16:19:11.800

0

This may not be a native tool but could be useful to someone with windows 7, there's a program called FileLocator Pro which has an option under Advanced/Junction Points to turn off searching junction points.

Once that's done you can use the command line program flpsearch like this:

"C:\Program Files\Mythicsoft\FileLocator Pro\flpsearch.exe" -o c:\biglist.txt -d c:\;d:\;e:\ -ofbs -f *

Echo Started cleanup

for /F "tokens=1-2 delims=  " %%i in (c:\biglist.txt) do if "!prevx!" neq "%%i" (ECHO %%i >> c:\big.txt & (if /i "%%j" neq "on" echo        %%j >> c:\big.txt) & set prevx=%%i) else (if /i "%%j" neq "on" echo     %%j >> c:\big.txt) 

There's literal tabs in there after the echo's and delims so make sure your text editor isn't converting tabs to spaces.

Hope this might be useful to a Win7 user or two.

Ofc the powershell options are good too, but DOS batch is another story.

colin lamarre

Posted 2013-01-05T11:00:07.927

Reputation: 171

0

From a CMD prompt:

dir /A:-L /S /B

EDIT: While this won't list the actual junction in the directory it resides, it will recurse down the junction.

wildotter

Posted 2013-01-05T11:00:07.927

Reputation: 11

Does not work if you boot from a WinPE image: I still see the "Application Data" directory repeat until it crashes. – trindflo – 2019-05-08T16:20:46.567