web-dev-qa-db-ja.com

(見かけ上)無限に再帰的なフォルダーを削除する

どういうわけか、私たちの古いServer 2008(R2ではない)ボックスの1つが、無限に繰り返されるように見えるフォルダーを開発しました。これは、バックアップエージェントがフォルダに再帰的に戻ろうとして戻らないため、バックアップで大混乱を引き起こしています。

フォルダ構造は次のようになります。

C:\Storage\Folder1
C:\Storage\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1\Folder1

... 等々。それは90年代にすべて一緒に使用していた Mandelbrotセット の1つに似ています。

私はもう試した:

  • エクスプローラから削除します。ええ、私は楽天家です。
  • RMDIR C:\Storage\Folder1 /Q/S-The directory is not emptyを返します
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE-robocopy.exeがクラッシュする前に、これがフォルダーを数分間回転させます。

誰かがこのフォルダを永久に殺す方法を提案できますか?

45
KenD

有益なアドバイスをくださった皆さんに感謝します。

StackOverflow領域に迷い込んで、このC#コードのスニペットをノックアップすることで問題を解決しました。長いファイルパスへのアクセスの問題に特に対処する Delimon.Win32.IO ライブラリを使用します。

これが誰かを助けることができるように、ここにコードがあります-それは私が何らかの方法で立ち往生していた1600レベルまでの再帰を通過し、それらすべてを削除するのに約20分かかりました。

using System;
using Delimon.Win32.IO;

namespace ConsoleApplication1
{
    class Program
    {
        private static int level;
        static void Main(string[] args)
        {
            // Call the method to delete the directory structure
            RecursiveDelete(new DirectoryInfo(@"\\server\\c$\\storage\\folder1"));
        }

        // This deletes a particular folder, and recurses back to itself if it finds any subfolders
        public static void RecursiveDelete(DirectoryInfo Dir)
        {
            level++;
            Console.WriteLine("Now at level " +level);
            if (!Dir.Exists)
                return;

            // In any subdirectory ...
            foreach (var dir in Dir.GetDirectories())
            {
                // Call this method again, starting at the subdirectory
                RecursiveDelete(dir);
            }

            // Finally, delete the directory, and any files below it
            Dir.Delete(true);
            Console.WriteLine("Deleting directory at level " + level);
            level--;
        }
    }
}
45
KenD

再帰的なジャンクションポイントである可能性があります。このようなものは、_ Sysinternals からファイルとディスクユーティリティjunctionを使用して作成できます。

mkdir c:\Hello
junction c:\Hello\Hello c:\Hello

そして、c:\ Hello\Hello\Hello ....(MAX_PATHに到達するまで、ほとんどのコマンドでは260文字ですが、一部のWindows API関数では32,767文字です)まで無限に進むことができます。

ディレクトリリストは、それがジャンクションであることを示しています。

C:\>dir c:\hello
 Volume in drive C is DR1
 Volume Serial Number is 993E-B99C

 Directory of c:\hello

12/02/2015  08:18 AM    <DIR>          .
12/02/2015  08:18 AM    <DIR>          ..
12/02/2015  08:18 AM    <JUNCTION>     hello [\??\c:\hello]
               0 File(s)              0 bytes
               3 Dir(s)  461,591,506,944 bytes free

C:\>

削除するには、junctionユーティリティを使用します。

junction -d c:\Hello\Hello
25
Brian

回答ではありませんが、コメントに対する十分な担当者がいません。

私はかつてこの問題をMS-DOSシステムの巨大な500MB FAT16ディスクで修正しました。 DOSデバッグを使用して、ディレクトリテーブルを手動でダンプおよび解析しました。次に、1ビット反転して、再帰ディレクトリを削除済みとしてマークしました。デットマンとワイアットの 『DOSプログラマーズリファレンス』の私のコピーは私に道を示しました。

私は今でもこれを過度に誇りに思っています。 FAT32ボリュームやNTFSボリュームに対してこのような機能を持つ汎用ツールがあるとしたら、私は驚き、恐怖を感じるでしょう。当時の生活はもっと単純だった。

17
Richard

Javaは長いファイルパスも処理できます。そして、それはそれをはるかに速くすることもできます。このコード(Java APIドキュメントからコピーしたもの)は、1600レベルの深いディレクトリ構造を約1秒で削除します(Windows 7では、Java 8.0))実際に再帰を使用しないため、スタックオーバーフローのリスクがありません。

import Java.nio.file.*;
import Java.nio.file.attribute.*;
import Java.io.*;

public class DeleteDir {

  static void deleteDirRecur(Path dir) throws IOException {
    Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
         @Override
         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
             throws IOException
         {
             Files.delete(file);
             return FileVisitResult.CONTINUE;
         }
         @Override
         public FileVisitResult postVisitDirectory(Path dir, IOException e)
             throws IOException
         {
             if (e == null) {
                 Files.delete(dir);
                 return FileVisitResult.CONTINUE;
             } else {
                 throw e;
             }
         }
     });
  }

  public static void main(String[] args) throws IOException {
    deleteDirRecur(Paths.get("C:/Storage/Folder1"));
  }
}
8
SpiderPig

chdirをディレクトリに入れ、rmdirへの相対パスを使用するだけであれば、長いパス名は必要ありません。

または、POSIXシェルがインストールされている場合、またはこれを同等のDOSに移植する場合:

_# untested code, didn't bother actually testing since the OP already solved the problem.

while [ -d Folder1 ]; do
    mv Folder1/Folder1/Folder1/Folder1  tmp # repeat more times to work in larger batches
    rm -r Folder1     # remove the first several levels remaining after moving the main tree out
    # then repeat to end up with the remaining big tree under the original name
    mv tmp/Folder1/Folder1/.../Folder1 Folder1 
    rm -r tmp
done
_

(Shell変数を使用して、ループ条件の名前を変更した場所を追跡することは、ここで行ったようにループを展開する別の方法です。)

これにより、KenDのソリューションのCPUオーバーヘッドが回避され、新しいレベルが追加されるたびにOSがツリーを上からnthレベルまでトラバースし、権限などをチェックします。つまり、sum(1, n) = n * (n-1) / 2 = O(n^2)時間の複雑さがあります。チェーンの最初からチャンクを切り離すソリューションは、親ディレクトリの名前を変更するときにWindowsがツリーを走査する必要がない限り、O(n)である必要があります。 (Linux/Unixはサポートしていません。)ツリーの最下部までchdirまで相対パスを使用し、ディレクトリをchdirがバックアップするときにそれらを削除するソリューションもO(n)である必要があります。 CDのどこかでCDを作成しているときに、システムコールのたびにOSがすべての親ディレクトリをチェックする必要はありません。

_find Folder1 -depth -execdir rmdir {} +_は、最下位のディレクトリにCDされている間にrmdirを実行します。あるいは、実際には、findの_-delete_オプションはディレクトリで機能し、_-depth_を意味します。したがって、_find Folder1 -delete_はまったく同じことを行う必要がありますが、高速です。そうです、GNU Linuxでは、ディレクトリをスキャンし、相対パスを使用してサブディレクトリにCDで移動し、次に相対パスを使用してrmdirを実行し、次にchdir("..")を実行します。ディレクトリは再スキャンしません。昇順の間、O(n) RAMを消費します。

straceは実際にunlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR)open("..", O_DIRECTORY|...)、およびfchdir(the fd from opening the directory)を使用していることを示しています。fstatの呼び出しも混在しています。ただし、findの実行中にディレクトリツリーが変更されない場合でも、効果は同じです。

編集:キックのためだけに、私はこれをGNU/Linuxで試しました(Ubuntu 14.10、2.4GHzの第1世代Core2Duo CPU、WD 2.5TB Green Powerドライブ(WD25EZRS)のXFSファイルシステム)。

_time mkdir -p $(Perl -e 'print "annoyingfoldername/" x 2000, "\n"')

real    0m1.141s
user    0m0.005s
sys     0m0.052s

find annoyingfoldername/ | wc
   2000    2000 38019001  # 2k lines / 2k words / 38M characters of text


ll -R annoyingfoldername
... eventually
ls: cannot access ./annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername: File name too long
total 0
?????????? ? ? ? ?            ? annoyingfoldername

time find annoyingfoldername -delete

real    0m0.054s
user    0m0.004s
sys     0m0.049s

# about the same for normal rm -r,
# which also didn't fail due to long path names
_

(mkdir -pは、ディレクトリと欠落しているパスコンポーネントを作成します)。

はい、2k rmdir opsで実際に0.05秒。 xfsは、メタデータ操作を10年前のように遅くなるのを修正したため、ジャーナルでメタデータ操作をバッチ処理するのに非常に適しています。

Ext4では、作成に0m0.279秒かかり、検索付きの削除には0m0.074秒かかりました。

6
Peter Cordes

5000以上のディレクトリの深いフォルダで同じ問題が発生しましたJavaアプリケーションが実行したため、このフォルダを削除するのに役立つプログラムを作成しました。ソースコード全体はこの中にありますリンク:

https://imanolbarba.net/gitlab/imanol/DiREKT

しばらくするとすべてが削除されましたが、なんとかうまくいきました。(私と同じように)同じイライラする問題に遭遇した人々を助けることを願っています

スタンドアロンのWindows 10システムでも、私もこれを持っていました。 C:\ User\Name\Repeat\Repeat\Repeat\Repeat\Repeat\Repeat\Repeatは無限に見える。

私はWindowsまたはコマンドプロンプトを使用して約50番目までナビゲートでき、それ以上はできませんでした。削除したり、クリックしたりできませんでした。

Cが私の言語なので、最終的には、システムコールのループを含むプログラムを作成しました。ループは失敗するまで繰り返されます。ただし、DOSバッチを含め、どの言語でもこれを実行できます。 tmpというディレクトリを作成して、Repeat\Repeatをその中に移動し、空になったRepeatフォルダーを削除して、tmp\Repeatを現在のフォルダーに戻しました。何度も何度も!

 while (times<2000)
 {
  ChkSystem("move Repeat\\Repeat tmp");
  ChkSystem("rd Repeat");
  ChkSystem("move tmp\\Repeat Repeat");
  ++times;
  printf("Removed %d nested so far.\n", times);
 }

ChkSystemはsystem()呼び出しを実行して戻り値を確認し、失敗した場合は停止します。

重要なのは、何度も失敗したことです。私のプログラムが機能していない、または結局のところそれが無限に長いと思っていました。ただし、以前はシステムコールでこれが発生しており、同期が取れていないため、プログラムをもう一度実行すると、中断したところから実行されたままなので、すぐにプログラムが機能していないとは思わないでください。つまり、合計で約20回実行すると、すべてがクリアされました。合計すると、元々は約1280フォルダーの深さでした。何が原因かわからない。クレイジー。

0
DenM