9
votes

RUBY: Comment puis-je trouver et supprimer de manière récursive des répertoires vides?

J'essaie d'écrire un peu de rubis qui rechercherait récursivement un répertoire donné pour tous les annuaires d'enfants vides et les supprimer.

pensées?

Remarque: je voudrais une version de script si possible. Ceci est à la fois un besoin pratique et une chose pour m'aider à apprendre.


3 commentaires

Première pensée: Système "Trouver. -Type D | Xargs RMDIR -P 2> / dev / null"


Juste une note, je ne veux pas faire cette opération en un coup de la ligne de commande. Sa va être dans un script rubis. Qu'est-ce que vous avez ci-dessus est une version cmd ligne non?


Eh bien, c'est une commande shell, oui, mais invoquée à partir de rubis en utilisant kernel.system ;)


9 Réponses :


2
votes

Pourquoi pas simplement utiliser Shell?

trouver. -Type D -empty -exec rmdir '{}' \;

fait exactement ce que vous voulez.


2 commentaires

J'espérais une version de script Ruby pour saisir la saisie de fichiers. Je pourrais appeler ce qui précède avec 'sh ' d'un script de rake je suppose. Vous avez une version de script?


La commande de script serait exactement la même. Cependant, la poste ci-dessous a une réponse qui vous conviendra.



19
votes

dans Ruby:

Dir['**/*']                                            \
  .select { |d| File.directory? d }                    \
  .select { |d| (Dir.entries(d) - %w[ . .. ]).empty? } \
  .each   { |d| Dir.rmdir d }


7 commentaires

Pourriez-vous expliquer le "-" en ligne 3?


Je soustraire le tableau contenant les chaînes "". et ".." à partir du tableau des entrées d'annuaire, car chaque répertoire contient ces deux entrées spéciales.


Aussi, pourriez-vous expliquer ce que sont les "\" sont pour? continuation de la ligne? sont-ils nécessaires?


Ils sont la continuation de la ligne, oui. Ils ne sont pas nécessaires si vous mettez le point à la fin de la ligne précédente, mais je les aime au début pour préciser que cette ligne se poursuit de la précédente. (Bien que vraiment, je viens de se formater en tant que telle à des fins d'affichage. Je laisse généralement ces choses en une seule ligne, jusqu'à ~ 160 caractères. Certaines personnes me détestent, oui. Je pense qu'ils utilisent des tys.)


haha. Merci Kch tu m'as sauvé un peu de temps. J'ai eu tout le doco qui s'est arrêté, mais il manquait une partie de la plus grande image pour tout mettre ensemble.


Vous voyez, la façon dont j'ai écrit le code, si j'ai laissé tomber le \ s, Ruby pourrait interpréter la ligne comme une expression complète et que cela le ferait.


Vous devez supprimer dans l'ordre inverse, sinon vous ne supprimerez pas tous les répertoires vides.



0
votes
Dir.glob('**/*').each do |dir|
  begin
    Dir.rmdir dir if File.directory?(dir)
  # rescue # this can be dangereous unless used cautiously
  rescue Errno::ENOTEMPTY
  end
end

3 commentaires

Sauver de toute exception Willy-Nilly n'est pas la meilleure idée du code de production réel.


Habituellement, je devrais être d'accord, mais c'est une sorte de situation limite. En tout cas, j'ai mis à jour la déclaration de sauvetage.


Oui, c'est une de ces choses qui vont aussi longtemps que vous savez ce que vous faites. Mais un googler frappe cette page pourrait être considéré comme un script rapide ou pour une bibliothèque qui aura un rôle majeur dans une application intensive de systèmes de fichiers. Ainsi, fondamentalement, N00BS Obtenez le code de sécurité par défaut et les personnes qui savent censeusement savoir ce qu'ils font le feront à leurs propres risques.



1
votes

J'ai testé ce script sur OS X, mais si vous êtes sous Windows, vous devrez apporter des modifications.

Vous pouvez trouver les fichiers d'un répertoire, y compris des fichiers cachés, avec les entrées n ° DIR.

Ce code supprimera des annuaires qui deviennent vides une fois que vous avez supprimé des sous-répertoires. P>

def entries(dir)
  Dir.entries(dir) - [".", ".."]
end

def recursively_delete_empty(dir)
  subdirs = entries(dir).map { |f| File.join(dir, f) }.select { |f| File.directory? f }
  subdirs.each do |subdir|
    recursively_delete_empty subdir
  end

  if entries(dir).empty?
    puts "deleting #{dir}"
    Dir.rmdir dir
  end
end


0 commentaires

1
votes
module MyExtensions
  module FileUtils
    # Gracefully delete dirs that are empty (or contain empty children).
    def rmdir_empty(*dirs)
      dirs.each do |dir|
        begin
          ndel = Dir.glob("#{dir}/**/", File::FNM_DOTMATCH).count do |d|
            begin; Dir.rmdir d; rescue SystemCallError; end
          end
        end while ndel > 0
      end
    end
  end

  module ::FileUtils
    extend FileUtils
  end
end

0 commentaires

2
votes
Dir['/Users/path/Movies/incompleteAnime/foom/**/*']. \
select { |d| File.directory? d }. \
sort.reverse. \
each {|d| Dir.rmdir(d) if Dir.entries(d).size ==  2}
just like the first example, but the first example doesn't seem to handle the recursive bit. The sort and reverse ensures we deal with the most nested directories first.I suppose sort.reverse could be written as sort {|a,b| b <=> a} for efficiency

0 commentaires

3
votes

Vous devez supprimer dans l'ordre inverse, sinon si vous avez un répertoire vide FOO avec une barre de sous-répertoire, vous supprimerez la barre, mais pas FOO.

  Dir.glob(dir + "/**/*").select { |d| 
    File.directory?(d)
  }.reverse_each { |d| 
    if ((Dir.entries(d) - %w[ . .. ]).empty?)
      Dir.rmdir(d)
    end
  }


0 commentaires

10
votes

Regarder les exemples de KCH, DB. et Vishnu ci-dessus, j'ai mis en place un one-liner que je pense est une solution plus élégante: xxx

i utilise '** /' au lieu de < Code> '/ ** / *' Pour le GLOB, qui ne renvoie que des annuaires, je n'ai donc pas à tester s'il s'agit d'un répertoire ultérieurement. J'utilise inverse_each au lieu de trier.reverse.each car il est plus court et supposé plus efficace, selon ce POST . Je préfère dir.entries (d) .Size == 2 à (dir.entries (D) -% w [. ..]). Vide? parce que c'est un Peu plus facile de lire et de comprendre, bien que (dir.entries (d) -% w [. ..]). Vider? fonctionnerait probablement mieux si vous deviez exécuter votre script sur Windows.

J'ai testé cela assez un peu sur Mac OS X et cela fonctionne bien, même avec des répertoires vides récursifs.


4 commentaires

Une compilation très élégante de solutions.


Nice refacteur mais je pense que mentionnant. et .. est plus intuitif que la taille == 2 Donc j'irais avec `dir ['lib / ** /']. Reverse_each {| D | Dir.RMDIR D Si DIR.Entries (D) .sort ==% w (..)} `


Oui, ce genre de monde est plus intuitif.


Nice approche, j'irais pour ce qui suit ces jours-ci: dir [* ** / ']. Reverse_each {| D | Dir.RMDIR (D) Si Dir.empty? (D)} ou même plus court DIR ['** /']. Reverse_each {Dir.RMDIR (_1) Si Dir.empty? (_ 1) } . Cependant, vous devez noter que cette solution construit une matrice de tous les répertoires en mémoire. Vous ne devez donc pas l'utiliser, mais au cas où vous sachiez que le nombre de répertoires (tous, pas seulement les vides) est limité.



1
votes

Pour une solution pure Shell, j'ai trouvé ce très utile xxx pré>

mais si vous avez installé GNU-Trouvez (pas encore universel) ... p>

  find . -depth -type d -print0 |
    xargs -0n1 sh -c '[ "`cd "$0"; echo .* * ?`" = ". .. * ?" ] &&
                        echo "rmdir $0";'


1 commentaires

C'est vraiment cool. Je cherchais à l'origine une solution de rubis, mais c'est très pratique de le faire dans Bash si Ruby n'est pas installé sur un système. Merci!