Xóa một thư mục đệ quy vô hạn (dường như)


46

Bằng cách nào đó, một trong các hộp Server 2008 cũ (không phải R2) của chúng tôi đã phát triển một thư mục dường như vô hạn đệ quy. Đây là chơi havock với các bản sao lưu của chúng tôi, vì tác nhân sao lưu cố gắng lặp lại vào thư mục và không bao giờ quay trở lại.

Cấu trúc thư mục trông giống như:

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

... và cứ thế. Nó giống như một trong những bộ Mandelbrot mà chúng ta từng chơi cùng trong những năm 90.

Tôi đã thử:

  • Xóa nó khỏi Explorer. Vâng, tôi là một người lạc quan.
  • RMDIR C:\Storage\Folder1 /Q/S - cái này trả về The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - điều này sẽ quay qua các thư mục trong vài phút trước khi gặp sự cố robocopy.exe.

Bất cứ ai có thể đề nghị một cách để giết thư mục này cho tốt?


1
/MIRThay vào đó tôi sẽ thử : ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1cũng có thể đáng để chạy chkdskchỉ để cười khúc khích.
jscott

1
/MIRdường như tồn tại lâu hơn, nhưng cuối cùng cũng bị ném bom ("robocopy đã ngừng hoạt động"). Tôi hơi sợ phải làm một chkdsk; Đây là một máy chủ khá cũ và tôi lo lắng rằng sự cố này là do sự cố hệ thống tệp lớn hơn ...
KenD

7
Hãy thử khởi động từ đĩa CD dùng thử máy tính để bàn Linux (Ubuntu / Centos / Fedora / ...) và xóa thư mục khỏi đó.
Guntram Blohm hỗ trợ Monica

2
@KenD Nếu bạn nghi ngờ vấn đề tham nhũng hệ thống tệp, bạn chắc chắn nên thử sửa chữa hệ thống tệp trước. Thử các thủ thuật loại bỏ thư mục có thể làm cho mọi thứ tồi tệ hơn.
jscott

1
Vì (từ câu trả lời của bạn bên dưới), thư mục không phải là vô hạn, chỉ rất sâu, nếu bạn đã cài đặt CygWin hoặc UnxUtils , bạn có thể sử dụng findđể thực hiện xóa thư mục đầu tiên sâu:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Câu trả lời:


45

Cảm ơn mọi người vì những lời khuyên hữu ích.

Đi lạc vào lãnh thổ StackOverflow, tôi đã giải quyết vấn đề bằng cách gõ đoạn mã C # này. Nó sử dụng thư viện Delimon.Win32.IO đặc biệt giải quyết các vấn đề khi truy cập các đường dẫn tệp dài.

Chỉ trong trường hợp điều này có thể giúp người khác thoát ra, đây là mã - nó đã vượt qua ~ 1600 cấp độ đệ quy mà bằng cách nào đó tôi đã bị mắc kẹt và mất khoảng 20 phút để loại bỏ tất cả.

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--;
        }
    }
}

2
Tôi đã thử, thậm chí sử dụng phiên bản Delimon của .Delete(thay vì System.IOphiên bản bình thường ), và mặc dù nó không có ngoại lệ, nó dường như không làm gì cả. Chắc chắn đệ quy sử dụng phương pháp trên mất nhiều thời gian và .Deletechỉ nhai mọi thứ trong 5-10 giây. Có lẽ nó sứt mẻ ở một vài thư mục và sau đó từ bỏ?
KenD

4
Bạn đã bao giờ tìm hiểu làm thế nào điều đó xảy ra để bắt đầu? Âm thanh như một số lỗi thực sự của chương trình người dùng bằng văn bản.
Bắn Parthian

8
Đệ quy thành hàm 1600 lần? Chồng tràn lãnh thổ thực sự!
Alexanderr Dubinsky

2
Chỉ là một bên, các thư mục được điền bởi bất cứ điều gì? Nếu bạn có thể xác định các khoảng thời gian mà các thư mục đệ quy được tạo và nhân số đó với số lần truy cập, bạn sẽ (hy vọng) có được một khung thời gian sơ bộ khi vấn đề này bắt đầu ...
Get-HomeByFiveOClock 13/215

8
Wow, rất vui vì bạn đã giải quyết điều này. FYI, Microsoft chính thức hỗ trợ sửa lỗi cho loại tình huống này là "định dạng lại âm lượng". Vâng, nghiêm túc. : /
HoplessN00b

25

Có thể là một điểm giao tiếp đệ quy. Một thứ như vậy có thể được tạo ra với junctionmột tiện ích tệp và đĩa từ Sysiternals .

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

Và bây giờ bạn có thể đi xuống vô tận c: \ Hello \ Hello \ Hello .... (cho đến khi đạt được MAX_PATH, 260 ký tự cho hầu hết các lệnh nhưng 32.767 ký tự cho một số chức năng API của Windows).

Một danh sách thư mục cho thấy rằng đó là một ngã ba:

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:\>

Để xóa sử dụng tiện ích ngã ba:

junction -d c:\Hello\Hello

4
Thật không may, một DIRchỉ hiển thị cho tôi các thư mục ole đơn giản - không có dấu hiệu của bất kỳ mối nối nào tôi sợ
KenD

2
Bạn có thể làm một kiểm tra nhanh với junction -s C:\Storage\Folder1 ?
Brian

3
No reparse points found:(
KenD

3
Tôi tự hỏi những gì đã tạo ra một mớ hỗn độn của các thư mục phụ thực tế.
Brian

2
Sử dụng dir /ađể xem '<JUNCTION>' mà không chỉ định tên cụ thể.
Chloe

16

Không phải là một câu trả lời, nhưng tôi không có đủ đại diện cho một bình luận.

Tôi đã từng khắc phục sự cố này trên đĩa FAT16 500 MB rất lớn trên hệ thống MS-DOS. Tôi đã sử dụng DOS gỡ lỗi để tự đổ và phân tích cú pháp thông qua bảng thư mục. Sau đó tôi lật một bit để đánh dấu thư mục đệ quy là đã xóa. Bản sao của tôi về Dettman và Wyatt 'Tài liệu tham khảo cho các lập trình viên DOS đã chỉ cho tôi cách.

Tôi vẫn vô cùng tự hào về điều này. Tôi sẽ ngạc nhiên và kinh hoàng nếu có bất kỳ công cụ đa năng nào có sức mạnh như vậy đối với các ổ đĩa FAT32 hoặc NTFS. Cuộc sống đã đơn giản hơn khi đó.


8
Tôi muốn nói rằng bạn tự hào chính đáng về điều này.
mfinni

3
+1 có một số đại diện cho tôi. Giải pháp tốt đẹp.
Chris Thornton

3
Bây giờ bạn có thể xóa câu đầu tiên của câu trả lời của bạn.
AL

Đây không phải là một câu trả lời. Đó là một câu chuyện về một hệ điều hành khác và một hệ thống tập tin khác. Ngoài việc không giúp ích gì cho vấn đề (NT, NTFS) này, nó thậm chí sẽ không giúp được ai đó có cùng vấn đề (DOS, FAT16) vì nó không thực sự chứa bất kỳ chi tiết nào.
Andrew Medico

@Andrew Medico: Tôi đồng ý với bạn, vì vậy câu đầu tiên của tôi. Nhưng tôi nói cho bạn biết nơi tìm thông tin để giải quyết vấn đề hơi liên quan.
Richard

8

Java cũng có thể xử lý các đường dẫn tệp dài. Và nó cũng có thể làm điều đó nhanh hơn rất nhiều. Mã này (mà tôi đã sao chép từ tài liệu API Java) sẽ xóa cấu trúc thư mục sâu 1600 cấp trong khoảng 1 giây (trong Windows 7, Java 8.0) và không có nguy cơ tràn ngăn xếp vì nó không thực sự sử dụng đệ quy.

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"));
  }
}

3
Tao ghét mày. Bạn đã buộc tôi phải đưa ra một câu trả lời liên quan đến việc sử dụng Java và bây giờ tôi cảm thấy tất cả đều bẩn thỉu. Tôi cần tắm
HoplessN00b

Lời xin lỗi của tôi. Tôi hy vọng bạn sẽ phục hồi cuối cùng từ chấn thương của bạn. Nhưng một ngôn ngữ được phát triển bởi Microsoft (c #) có thực sự tốt hơn nhiều không?
SpiderPig 14/2/2015

5
Tôi chủ yếu là một anh chàng Windows ngày nay, vì vậy, vâng, đúng vậy. Tôi cũng có thể cay đắng và thiên vị với Java vì phải duy trì 5 phiên bản JRE cụ thể, khác nhau trên gần 1.000 khách hàng trong môi trường sử dụng lao động của tôi, một trong số đó có từ năm 2009, trên tài khoản của phần mềm độc hại ... ... uh, bộ phần mềm "doanh nghiệp-y" mà chúng tôi sử dụng cho các ứng dụng quan trọng trong kinh doanh.
HoplessN00b

6

Bạn không cần tên đường dẫn dài nếu bạn chdirvào thư mục và chỉ sử dụng các đường dẫn tương đối rmdir.

Hoặc, nếu bạn đã cài đặt vỏ POSIX hoặc chuyển cổng này sang DOS tương đương:

# 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

(Sử dụng biến shell để theo dõi nơi bạn đổi tên nó cho điều kiện vòng lặp là cách khác để hủy bỏ vòng lặp như tôi đã làm ở đó.)

Điều này tránh được chi phí CPU của giải pháp KenD, điều này buộc HĐH phải vượt qua cây từ cấp cao nhất đến ncấp độ mỗi khi thêm cấp độ mới, kiểm tra quyền, v.v. Vì vậy, nó có sum(1, n) = n * (n-1) / 2 = O(n^2)độ phức tạp về thời gian. Các giải pháp cắt bỏ một đoạn từ đầu chuỗi nên được O(n), trừ khi Windows cần duyệt qua cây khi đổi tên thư mục mẹ của nó. (Linux / Unix không.) Các giải pháp chdirđi xuống tận cùng của cây và sử dụng các đường dẫn tương đối từ đó, xóa các thư mục khi chúng chdirsao lưu, cũng nên O(n), giả sử HĐH không cần kiểm tra tất cả thư mục cha mẹ mỗi cuộc gọi hệ thống, khi bạn làm mọi thứ trong khi CD ở đâu đó.

find Folder1 -depth -execdir rmdir {} +sẽ chạy rmdir trong khi CD vào thư mục sâu nhất. Hoặc thực tế, -deletetùy chọn find hoạt động trên các thư mục và ngụ ý -depth. Vì vậy, find Folder1 -deletenên làm điều tương tự chính xác, nhưng nhanh hơn. Vâng, GNU tìm thấy trên Linux xuống bằng cách quét một thư mục, ghi vào thư mục con với các đường dẫn tương đối, sau đó rmdirvới một đường dẫn tương đối, sau đó chdir(".."). Nó không quét lại các thư mục trong khi tăng dần, vì vậy nó sẽ tiêu thụ O(n)RAM.

Đó là thực sự là một xấp xỉ: stracechương trình nó thực sự sử dụng unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)fchdir(the fd from opening the directory), với một loạt các fstatcuộc gọi trộn lẫn trong, quá. Nhưng hiệu ứng là như nhau nếu cây thư mục không được sửa đổi trong khi find đang chạy.

chỉnh sửa: Chỉ để thực hiện, tôi đã thử điều này trên GNU / Linux (Ubuntu 14.10, trên CPU Core2Duo thế hệ đầu tiên 2,4 GHz, trên hệ thống tệp XFS trên ổ đĩa Green Power WD WD (WD25EZRS)).

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 tạo một thư mục và bất kỳ thành phần đường dẫn bị thiếu nào).

Vâng, thực sự 0,05 giây cho 2k rmdir ops. xfs khá giỏi trong việc kết hợp các hoạt động siêu dữ liệu với nhau trên tạp chí, vì chúng đã sửa lỗi các dữ liệu meta bị chậm như 10 năm trước.

Trên ext4, tạo mất 0m0.279s, xóa với find vẫn mất 0m0.074s.


đã tò mò và thử nó trên Linux. Hóa ra các công cụ GNU tiêu chuẩn đều ổn với các đường dẫn dài, bởi vì chúng lặp lại trên cây thay vì cố gắng thực hiện các cuộc gọi hệ thống với các đường dẫn dài khổng lồ. Ngay cả mkdir cũng ổn khi bạn vượt qua nó một đường dẫn 38k trên dòng lệnh!
Peter Cordes

0

Tôi đã gặp vấn đề tương tự với một mớ thư mục sâu hơn 5000 thư mục mà một số ứng dụng Java đã làm và tôi đã viết một chương trình sẽ giúp bạn xóa thư mục này. Toàn bộ mã nguồn nằm trong liên kết này:

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

Nó đã loại bỏ toàn bộ sau một thời gian, nhưng nó đã xoay sở để thực hiện công việc, tôi hy vọng nó sẽ giúp những người (như tôi), gặp phải vấn đề bực bội tương tự


Xin vui lòng không đăng câu trả lời chỉ liên kết. Bạn nên đặt thông tin quan trọng nhất từ ​​liên kết trong chính bài đăng và cung cấp liên kết để tham khảo.
Frederik Nielsen

Ôi xin lỗi, đây là một chương trình và tôi thực sự không muốn đăng TẤT CẢ mã nguồn ở đây ... Tôi nghĩ khá rõ ràng rằng tôi đã viết một chương trình và tôi đang lưu trữ nó theo liên kết này, và động lực và tất cả là ở câu trả lời, vì vậy nó có vẻ khá rõ ràng đối với tôi nó không phải là câu trả lời chỉ liên kết, không cần trả lời, tôi sẽ chỉ định (rõ ràng hơn) rằng đó là một phần mềm được dùng để giải quyết vấn đề này
Imanol Barba Sabariego

0

Tôi cũng đã có điều này, trên một hệ thống Windows 10 độc lập. C: \ Người dùng \ Tên \ Lặp lại \ Lặp lại \ Lặp lại \ Lặp lại \ Lặp lại \ Lặp lại \ Lặp lại dường như vô cùng.

Tôi có thể điều hướng bằng Windows hoặc Command Prompt đến khoảng thứ 50 và không cần thêm nữa. Tôi không thể xóa nó, hoặc bấm vào nó, v.v.

C là ngôn ngữ của tôi nên cuối cùng tôi đã viết một chương trình với một vòng lặp các cuộc gọi hệ thống, lặp lại cho đến khi chúng thất bại. Bạn có thể làm điều này bằng bất kỳ ngôn ngữ nào, ngay cả lô DOS. Tôi đã tạo một thư mục có tên là tmp và di chuyển Lặp lại \ Lặp lại vào đó, xóa thư mục Lặp lại trống rỗng và di chuyển tmp \ Lặp lại vào thư mục hiện tại. Lặp đi lặp lại!

 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 chỉ chạy một lệnh hệ thống () và kiểm tra giá trị trả về, dừng lại nếu thất bại.

Điều quan trọng, nó đã thất bại một số lần. Tôi nghĩ có lẽ chương trình của tôi không hoạt động, hoặc nó dài vô tận. Tuy nhiên, tôi đã có điều này trước đây với các cuộc gọi hệ thống, với những thứ không được đồng bộ hóa, vì vậy tôi chỉ chạy chương trình một lần nữa và nó tiếp tục từ nơi nó dừng lại, vì vậy đừng nghĩ rằng chương trình của bạn không hoạt động. Vì vậy, tổng cộng, sau khi chạy nó khoảng 20 lần, nó đã xóa tất cả. Tổng cộng, ban đầu nó có khoảng 1280 thư mục. Không biết điều gì gây ra nó. Khùng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.