Menghapus folder (tampaknya) rekursif tanpa batas

46

Entah bagaimana, salah satu kotak Server 2008 kami yang lama (bukan R2) telah mengembangkan folder yang tampaknya berulang sekali. Ini bermain havock dengan cadangan kami, karena agen cadangan mencoba untuk kembali ke folder dan tidak pernah kembali.

Struktur folder terlihat seperti:

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

... dan seterusnya. Ini seperti salah satu set Mandelbrot yang kami gunakan untuk semua bermain di tahun 90-an.

Saya sudah mencoba:

  • Menghapusnya dari Explorer. Ya, saya optimis.
  • RMDIR C:\Storage\Folder1 /Q/S - ini kembali The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - ini berputar melalui folder selama beberapa menit sebelum robocopy.exe macet.

Adakah yang bisa menyarankan cara untuk mematikan folder ini untuk selamanya?

Ken
sumber
1
Saya akan mencoba /MIRsebagai gantinya: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1juga mungkin layak dijalankan chkdskhanya untuk cekikikan.
jscott
1
/MIRtampaknya bertahan lebih lama, tetapi akhirnya dibom juga ("robocopy telah berhenti bekerja"). Saya agak takut melakukan chkdsk; ini adalah server yang cukup lama dan saya khawatir masalah ini menunjukkan masalah sistem file yang lebih besar ...
KenD
7
Coba boot dari CD percobaan desktop Linux (Ubuntu / Centos / Fedora / ...) dan hapus folder dari sana.
Guntram Blohm mendukung Monica
2
@ Ken Jika Anda mencurigai masalah sistem file korupsi, Anda harus mencoba memperbaiki filesystem terlebih dahulu. Mencoba trik penghapusan direktori mungkin memperburuk keadaan.
jscott
1
Karena (dari jawaban Anda di bawah), direktori itu tidak terbatas, hanya sangat dalam, jika Anda menginstal CygWin atau UnxUtils , Anda dapat menggunakan finduntuk melakukan penghapusan direktori pertama yang dalam:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Jawaban:

45

Terima kasih kepada semua orang atas saran yang bermanfaat.

Menyasar jauh ke wilayah StackOverflow, saya telah memecahkan masalah dengan mengetuk potongan kode C # ini. Ini menggunakan perpustakaan Delimon.Win32.IO yang secara khusus mengatasi masalah mengakses jalur file panjang.

Untuk berjaga-jaga kalau ini bisa membantu orang lain, inilah kodenya - kode itu berhasil melewati ~ 1600 tingkat rekursi yang entah bagaimana membuat saya terjebak dan butuh sekitar 20 menit untuk menghapus semuanya.

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--;
        }
    }
}
Ken
sumber
2
Saya mencoba, bahkan menggunakan versi Delimon .Delete(bukan System.IOversi normal ), dan meskipun tidak membuang pengecualian, sepertinya tidak melakukan apa-apa. Tentu saja rekursi menggunakan metode di atas memakan waktu lama, dan .Deletehanya mengunyah selama 5-10 detik. Mungkin itu terkelupas di beberapa direktori dan kemudian menyerah?
KenD
4
Apakah Anda pernah memikirkan bagaimana hal itu terjadi pada awalnya? Kedengarannya seperti kesalahan program userland yang ditulis dengan sangat buruk.
Parthian Shot
8
Berulang ke fungsi 1600 kali? Tumpuklah wilayah yang meluap !
Aleksandr Dubinsky
2
Selain itu, apakah folder-folder tersebut dipenuhi oleh sesuatu? Jika Anda dapat menentukan pada interval berapa folder rekursif dibuat dan mengalikannya dengan jumlah rekursi, Anda akan (semoga) mendapatkan jangka waktu kasar kapan masalah ini dimulai ...
Get-HomeByFiveOClock
8
Wow, senang Anda akhirnya menyelesaikan ini. FYI, perbaikan resmi yang didukung Microsoft untuk situasi seperti ini adalah "format ulang volume." Ya, serius. : /
HopelessN00b
25

Bisa menjadi titik persimpangan rekursif. Hal seperti itu dapat dibuat dengan junctionutilitas file dan disk dari Sysinternals .

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

Dan sekarang Anda dapat turun tanpa henti c: \ Hello \ Hello \ Hello .... (hingga MAX_PATH tercapai, 260 karakter untuk sebagian besar perintah, tetapi 32.767 karakter untuk beberapa fungsi Windows API).

Daftar direktori menunjukkan bahwa ini adalah persimpangan:

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

Untuk menghapus gunakan utilitas persimpangan:

junction -d c:\Hello\Hello
Brian
sumber
4
Sayangnya, DIRhanya menunjukkan saya direktori ole biasa - tidak ada tanda-tanda persimpangan saya khawatir
KenD
2
Bisakah Anda melakukan pengecekan cepat dengan junction -s C:\Storage\Folder1 ?
Brian
3
No reparse points found:(
KenD
3
Saya bertanya-tanya apa yang membuat kekacauan sub direktori yang sebenarnya.
Brian
2
Gunakan dir /auntuk melihat '<JUNCTION>' tanpa menentukan nama spesifik.
Chloe
16

Bukan jawaban, tapi saya tidak punya cukup perwakilan untuk komentar.

Saya pernah memperbaiki masalah ini pada disk FAT16 500MB pada sistem MS-DOS. Saya menggunakan DOS debug untuk secara manual membuang dan mem-parsing melalui tabel direktori. Saya kemudian membalik sedikit untuk menandai direktori rekursif sebagai dihapus. Salinan 'Referensi Programmer' DOS Dettman dan Wyatt saya menunjukkan caranya.

Saya masih sangat bangga akan hal ini. Saya akan kagum dan ketakutan jika ada alat serba guna yang memiliki kekuatan lebih dari volume FAT32 atau NTFS. Hidup lebih sederhana saat itu.

Richard
sumber
8
Saya akan mengatakan bahwa Anda adalah dibenarkan bangga dengan ini.
mfinni
3
+1 punya beberapa perwakilan untuk saya. Solusi yang bagus.
Chris Thornton
3
Anda sekarang dapat menghapus kalimat pertama dari jawaban Anda.
AL
Ini bukan jawaban. Ini adalah kisah tentang sistem operasi yang berbeda dan sistem file yang berbeda. Selain tidak membantu untuk masalah ini (NT, NTFS), itu bahkan tidak akan membantu seseorang dengan masalah yang sama (DOS, FAT16) karena tidak benar-benar berisi perincian.
Andrew Medico
@Andrew Medico: Saya setuju dengan Anda, karenanya kalimat pertama saya. Tapi saya memberi tahu Anda di mana menemukan info untuk menyelesaikan masalah yang sedikit terkait.
Richard
8

Java juga dapat menangani jalur file yang panjang. Dan itu juga bisa lebih cepat. Kode ini (yang saya salin dari dokumentasi Java API) akan menghapus struktur direktori sedalam 1600 level dalam waktu sekitar 1 detik (di bawah Windows 7, Java 8.0) dan tanpa risiko stack overflow karena tidak benar-benar menggunakan rekursi.

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"));
  }
}
SpiderPig
sumber
3
Aku membenci mu. Anda telah memaksa saya untuk mengubah jawaban yang melibatkan penggunaan Java, dan sekarang saya merasa kotor. Saya perlu mandi.
HopelessN00b
Permintaan maaf saya. Saya harap Anda akan pulih pada akhirnya dari trauma Anda. Tetapi apakah bahasa yang dikembangkan oleh Microsoft (c #) benar-benar jauh lebih baik?
SpiderPig
5
Saya terutama orang Windows hari ini, jadi ya, ya itu. Saya juga mungkin pahit dan bias terhadap Jawa karena harus mempertahankan 5 versi JRE spesifik dan berbeda di hampir 1.000 klien di lingkungan perusahaan saya, salah satunya tanggal kembali ke 2009, karena masalah sampah, er, malware ... eh, perangkat lunak "enterprise-y" cocok yang kami gunakan untuk aplikasi penting bisnis.
HopelessN00b
6

Anda tidak perlu nama path panjang jika chdirmasuk ke direktori dan cukup gunakan path relatif ke rmdir.

Atau, jika Anda memiliki shell POSIX yang diinstal, atau port ini ke setara 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

(Menggunakan variabel shell untuk melacak di mana Anda mengganti nama untuk kondisi loop adalah alternatif lain untuk membuka gulungan loop seperti yang saya lakukan di sana.)

Ini menghindari overhead CPU dari solusi KenD, yang memaksa OS untuk melintasi pohon dari atas ke ntingkat th setiap kali level baru ditambahkan, memeriksa izin dll. Sehingga memiliki sum(1, n) = n * (n-1) / 2 = O(n^2)kompleksitas waktu. Solusi yang memotong sebagian dari awal rantai seharusnya O(n), kecuali Windows perlu melintasi pohon ketika mengganti nama direktori induknya. (Linux / Unix tidak.) Solusi yang chdirsemuanya turun ke bawah pohon dan menggunakan jalur relatif dari sana, menghapus direktori saat chdirdicadangkan, juga harus O(n), dengan asumsi OS tidak perlu memeriksa semua Anda direktori induk setiap panggilan sistem, ketika Anda melakukan hal-hal saat CD di suatu tempat.

find Folder1 -depth -execdir rmdir {} +akan menjalankan rmdir saat CD ke direktori terdalam. Atau sebenarnya, -deleteopsi find berfungsi pada direktori, dan tersirat -depth. Jadi find Folder1 -deleteharus melakukan hal yang persis sama, tetapi lebih cepat. Ya, GNU temukan di Linux turun dengan memindai direktori, memindahkan CD ke subdirektori dengan jalur relatif, lalu rmdirdengan jalur relatif, lalu chdir(".."). Itu tidak memindai ulang direktori saat naik, sehingga akan mengkonsumsi O(n)RAM.

Itu benar-benar sebuah pendekatan: stracemenunjukkan itu SEBENARNYA menggunakan unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)dan fchdir(the fd from opening the directory), dengan sekelompok fstatpanggilan dicampur dalam, juga. Tetapi efeknya sama jika pohon direktori tidak dimodifikasi saat find sedang berjalan.

sunting: Hanya untuk iseng, saya mencoba ini pada GNU / Linux (Ubuntu 14.10, pada Core2Duo CPU generasi pertama 2.4GHz, pada sistem file XFS pada WD 2.5TB Green Power drive (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 membuat direktori dan komponen path yang hilang).

Ya, benar-benar 0,05 detik untuk operasi 2k rmdir. xfs cukup bagus dalam batching operasi metadata bersama dalam jurnal, karena mereka memperbaiki operasi data meta yang lambat seperti 10 tahun yang lalu.

Pada ext4, buat take 0m0.279s, hapus dengan find masih butuh 0m0.074s.

Peter Cordes
sumber
penasaran dan mencobanya di Linux. Ternyata alat-alat GNU standar semuanya baik-baik saja dengan jalur panjang, karena mereka berulang turun pohon bukannya mencoba membuat panggilan sistem dengan jalur panjang raksasa. Bahkan mkdir baik-baik saja ketika Anda memberikannya jalur 38k pada baris perintah!
Peter Cordes
0

Saya memang mengalami masalah yang sama dengan kekacauan folder direktori sedalam 5000+ yang dilakukan oleh beberapa aplikasi Java dan saya menulis sebuah program yang akan membantu Anda menghapus folder ini. Seluruh kode sumber ada di tautan ini:

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

Ini menghapus semuanya setelah beberapa saat, tetapi berhasil melakukan pekerjaan, saya harap itu membantu orang-orang yang (seperti saya), mengalami masalah yang sama membuat frustrasi

Imanol Barba Sabariego
sumber
Tolong jangan posting jawaban hanya tautan. Anda harus meletakkan info paling penting dari tautan di pos itu sendiri dan memberikan tautan untuk referensi.
Frederik Nielsen
Oh maaf, ini sebuah program, dan saya benar-benar tidak ingin memposting SEMUA kode sumber di sini ... Saya pikir sudah cukup jelas bahwa saya menulis sebuah program dan saya hosting dengan mengikuti tautan ini, dan motivasi dan semuanya ada di jawabannya, jadi kelihatannya cukup jelas bagi saya itu bukan jawaban hanya-tautan, nontheless, saya akan menentukan (lebih jelas) bahwa itu adalah perangkat lunak yang dimaksudkan untuk dijalankan untuk menyelesaikan masalah ini
Imanol Barba Sabariego
0

Saya juga punya ini, pada sistem Windows 10 mandiri sekalipun. C: \ Pengguna \ Nama \ Ulangi \ Ulangi \ Ulangi \ Ulangi \ Ulangi \ Ulangi yang tampaknya tak terhingga.

Saya dapat menavigasi menggunakan Windows atau Command Prompt ke yang ke 50 dan tidak lebih lanjut. Saya tidak bisa menghapusnya, atau mengkliknya, dll.

C adalah bahasa saya jadi akhirnya saya menulis sebuah program dengan loop panggilan sistem, yang diulang sampai gagal. Anda bisa melakukan ini dalam bahasa apa pun, bahkan batch DOS. Saya membuat direktori bernama tmp dan memindahkan Repeat \ Repeat ke dalamnya, menghapus folder Repeat yang sekarang kosong, dan memindahkan tmp \ Repeat kembali ke folder saat ini. Lagi dan lagi!

 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 hanya menjalankan panggilan sistem () dan memeriksa nilai balik, berhenti jika gagal.

Yang penting, itu gagal beberapa kali. Saya pikir mungkin program saya tidak berfungsi, atau program itu lama sekali. Namun, saya pernah mengalami ini sebelumnya dengan pemanggilan sistem, dengan hal-hal yang tidak disinkronkan, jadi saya hanya menjalankan program lagi dan melanjutkan dari tempat sebelumnya, jadi jangan langsung berpikir program Anda tidak berfungsi. Jadi secara total, setelah menjalankannya sekitar 20 kali, itu membersihkan mereka semua. Secara total, awalnya sekitar 1.280 folder. Tidak tahu apa yang menyebabkannya. Gila.

DenM
sumber