challenge 指定されたフォルダ以下のゴミ掃除

指定したフォルダ以下にある、ファイル名が"~"で終わるファイルを削除するプログラムを作ってください。 指定したフォルダの中にあるフォルダのさらに中にあるファイルも削除の対象です。

Posted feedbacks - Flatten

Nested Hidden
エラーハンドリングは省略してます。
1
2
3
4
5
6
7
import os

def remove_backup(root_path):
    for w in os.walk(root_path):
        for f in w[2]:
            if f.endswith('~'):
                os.remove('%s/%s' % (w[0], f))


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 import java.io.File;
import java.io.FileFilter;

public class FileCleaner {

    private File rootDir;

    public FileCleaner(String rootDir) {
        this(new File(rootDir));
    }

    public FileCleaner(File rootDir) {
        this.rootDir = rootDir;
    }

    public void deleteFilesBySuffix(String suffix) {
        FileFilter filter = new FileFilter() {
            public boolean accept(File pathname) {
                return pathname.isFile() && pathname.getName().endsWith("~");
            }
        };
        deleteTargetFiles(rootDir, filter);
    }

    private void deleteTargetFiles(File dir, FileFilter filter) {
        File[] fileList = dir.listFiles();
        for (int i=0; i<fileList.length; i++) {
            File file = fileList[i];
            if (file.isDirectory()) {
                deleteTargetFiles(file, filter);
            } else {
                if (filter.accept(file)) {
                    file.delete();
                    System.out.println(file + " を削除しました。");
                }
            }
        }
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            System.err.println("ディレクトリを指定してください。");
            return;
        }
        new FileCleaner(args[0]).deleteFilesBySuffix("~");
    }
}

なんとなくCで
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h> 
#include <dirent.h>
#include <unistd.h>

void remove_backup(const char *dn){
    DIR *dp;
    struct dirent *de;
    char path[PATH_MAX];
    struct stat st;
    if(!(dp = opendir(dn))) return;
    while((de = readdir(dp))){
        if(!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
        snprintf(path, PATH_MAX, "%s/%s", dn, de->d_name);
        stat(path, &st);
        if(S_ISREG(st.st_mode) && de->d_name[strlen(de->d_name) - 1] == '~')
            unlink(path);
        else if(S_ISDIR(st.st_mode))
            remove_backup(path);
    }
}

Ruby.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Dir
  def delete_rec(pat)
    self.each do |e|
      next if e == '.' or e == '..'
      p = self.path + '/' + e
      File.delete(p) if pat =~ e and File.writable?(p)
      Dir.open(p).delete_rec(pat).close if File.directory?(p)
    end
    self
  end
end

Dir.open(ARGV[0]).delete_rec(/~$/).close

普段はFile_Find使うけど
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
remove_file('.', '~$');
function remove_file($dir, $regex){
    $d = dir($dir);
    while (($file = $d->read()) !== false) {
        if ($file == '.' || $file == '..') continue;
        $path = $d->path . "/$file";
        if (is_dir($path)){
            remove_file($path, $regex);
        }else if (preg_match("/$regex/", $path)){
            echo "delete $path\n";
            unlink($path);
        }
    }
    $d->close();
}
?>

普通は find . -type f -regex ".*~$" | xarg rm
1
perl -MFile::Find -e "find(sub {/.*~$/ && unlink \$File::Find::name;}, '.');"

Scheme (Gauche) で。
1
2
3
4
(use file.util)
(define (remove-backup dir)
  (directory-fold "." (lambda (path seed) (and (#/~$/ path) (sys-unlink path))) #f))
(define (main args) (for-each remove-backup (cdr args)) 0)

うわ、違うバージョンを貼り付けてしまった。

3行目、s/print/sys-unlink/ です。

find 版。
1
2
3
4
5
6
7
require 'find'

Find.find(".") do |fn|
  if File.file?(fn) && ?~ == fn[-1]
    File.unlink(fn)
  end
end

Dir.globでしょ。
1
2
3
Dir.glob('**/*~') do |path|
  File.unlink(path)
end

古のバッチファイルが今こそ火を噴く!


調子に乗りましたすいません。
1
for /D /R %%d in (*~) do (echo "%%d")

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import os.path
import os
from optparse import OptionParser


parser = OptionParser(usage="%prog <folder to start>", description='remove files end with "~" recursively.')
(opsions, args) = parser.parse_args()
if len(args) != 1:
  parser.error("need argument <folder to start>")

path = os.path.abspath(args[0])
def remove_them(parser, dirname, names):
  for name in names:
    t = os.path.join(dirname, name)
    if name.endswith('~'):
      try:
        os.remove(t)
      except e:
        print e, " skip to remove: ", t

os.path.walk(path, remove_them, parser)

vimスクリプト
1
2
3
4
5
6
fun! DeleteBackupFiles(dir)
  for v in split(globpath(a:dir, "*~"), "\n")
    call delete(v)
  endfor
endfun
call DeleteBackupFiles("/tmp")

う。~か~~と読み間違えた・・・orz

C# サブディレクトリ消さなくて良いならSearchOption.AllDirectoriesでらくちん。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.IO;

namespace Doukaku.CS
{
	public class DelNyoro
	{
		static void Main(string[] args)
		{
			foreach(string dir in args)
			{
				if (Directory.Exists(dir))
				{
					foreach (string nyoroFile in Directory.GetFiles(dir, "*~", SearchOption.AllDirectories))
						File.Delete(nyoroFile);
				}
				else
					Console.WriteLine(string.Format("Directory %s is not exists.", dir));
			}
		}
	}
}

つか、del /s *~というのは反則ですか? そうですか...。
1
del /s *~


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Control.Monad
import System
import System.Directory

main :: IO ()
main = do [root] <- getArgs
          removeBackup root

removeBackup :: FilePath -> IO ()
removeBackup dir
    = getDirectoryContents dir >>= mapM_ (removeIfBackupFile dir)
    where
      removeIfBackupFile :: FilePath -> FilePath -> IO ()
      removeIfBackupFile _ "."  = return ()
      removeIfBackupFile _ ".." = return ()
      removeIfBackupFile dir file
          = do let path = dir ++ "/" ++ file

               isDir <- doesDirectoryExist path

               if last path == '~' then
                   if isDir then
                       removeDirectoryRecursive path
                   else
                       removeFile path
                 else
                   when isDir $ removeBackup path

それはどうかな。
[5:51PM shyouhei]% mkdir tmp~
[5:51PM shyouhei]% ruby -e"
dquote> Dir.glob('**/*~') do |path|
dquote>   File.unlink(path)
dquote> end"
-e:3:in `unlink': Is a directory - tmp~ (Errno::EISDIR)
        from -e:3
        from -e:2:in `glob'
        from -e:2
zsh: exit 1     ruby -e" Dir.glob('**/*~') do |path|   File.unlink(path) end "

まぁ、わざわざオブジェクト思想にこだわる必要もないか。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.File;

public class DelteFile {
	
	public static void main(String[] args) throws Exception {
		for (int i = 0; i < args.length; i++) delete(new File(args[i]));
	}
	
	private static void delete(File f) throws Exception {
		if (f.isDirectory()) {
			for (int i = 0; i < f.listFiles().length; i++) {
				delete(f.listFiles()[i]);
			}
		} else {
			if (f.getName().endsWith("~")) {
				if (!f.delete()) {
					throw new Exception("何らかの原因で削除できず");
				}
			}
		}
	}
}

1
main(){system("find . -type f -name '*~' | xargs rm");}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#! /bin/bash

shopt -s dotglob #for dotfile

function rm_g(){
    local file proc_dir=$1
    for file in ${proc_dir}/*; do
        [ ${file:(-1):1} == '~' ] && rm $file
        [ -d $file ] && rm_g $file
    done
}

rm_g ~/tmp

erl -noshell -eval 'delbackup:delbackup("."), halt().'
のように実行します.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
-module(delbackup).
-export([delbackup/1]).

delbackup(RootPath) ->
        check_dir(RootPath).

check_dir(Path) ->
        case file:list_dir(Path) of
                {ok, FileList} ->
                        check_file(Path, FileList);
                {error, Reason} ->
                        io:format("Error. ~s (~p)~n", [Path, Reason])
        end.

check_file(Path, [File|RestFile]) ->
        FilePath = Path ++ "/" ++ File,
        io:format("~s~n", [FilePath]),
        IsDir = filelib:is_dir(FilePath),
        IsFile = filelib:is_file(FilePath),
        case {IsDir, IsFile} of
                {true, _} ->
                        check_dir(FilePath);
                {_, true} ->
                        check_backup(FilePath);
                _Other ->
                        ok
        end,
        check_file(Path, RestFile);
check_file(_Path, []) ->
        ok.

check_backup(FilePath) ->
        case regexp:match(FilePath, "~$") of
                {match, _, _} ->
                        file:delete(FilePath),
                        io:format("delete ~s~n", [FilePath]);
                _Else ->
                        ok
        end.

しかも間違ってるorz
1
for /D /R %%d in (*~) do (rd "%%d" /s /q)

find -name '*~' -exec rm -f \{\} \;

なにもかんがえずにコマンド1行 coreutils/findutiles使うのはBash扱いでよい?
1
find . -name '*~' -exec /bin/rm {} \;

上の人のを見ていて、 -type f もつけておいた方が良いことに気づいた:)

Bash というか find コマンド。
1
find $dir -type f -name \*~ -print0 | xargs -0 rm -f

コマンドっぽく
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# dma.pl
$ARGV[0] or die "Usage: $0 DIRECTORY...\n";

foreach my $dir (@ARGV) {
    dfswalk($dir, sub {
            my $path = shift;
            if ($path =~ /~$/ and -f $path) {
                unlink($path) or warn("$path: $!\n");
            }
    });
};

sub dfswalk
{
    my ($dir, $fn) = @_;
    
    unless (opendir(D, $dir)) {
        warn "$dir: $!\n";
        return;
    }
    foreach my $name (readdir(D)) {
        next if ($name eq '.' or $name eq '..');
        my $path = sprintf("%s/%s", $dir, $name);
        
        &$fn($path);
        if (-d $path) {
            dfswalk($path, $fn);
        }
    }
    closedir(D);
}

とりあえずこんなところでしょうか。 「指定されたフォルダ」はカレントディレクトリに置き換えてしまいました。引数で指定すべきであれば . を $1 に変更してください。ちまりに試験はしてません :-)
1
2
#!/bin/sh
find . -name '*~' -print | xargs rm

awk で。 普通に実行した方が楽です。
1
BEGIN{system("find . -type f -name '*~' | xargs rm")}

既に回答済みですが、メモがわりに
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using System;
using System.IO;
class Program
{
  static void Main()
  {
    string dir = @"C:\test";
    foreach (string s in Directory.GetFiles(dir, "*~", SearchOption.AllDirectories))
      File.Delete(s);
  }
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
open System;;
open System.IO;;
open Array;;
open Printf;;

let del path =
    let dirFiles = Directory.GetFiles(path, "*~", SearchOption.AllDirectories) in
    iter File.Delete dirFiles;;

if length Sys.argv = 2
then
    del Sys.argv.(1)
else
    ();;

これ今*.pycを消すのに使ってみて便利さに感動しました!

Squeak Smalltalk で手続き的に。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
| startDir queue |
startDir := FileDirectory default.
queue := OrderedCollection with: startDir.
[queue notEmpty] whileTrue: [
   | dir subDirs fileNames |
   dir := queue removeFirst.
   subDirs := dir directoryNames collect: [:dirName | dir directoryNamed: dirName].
   queue addAll: subDirs.
   fileNames := dir fileNames select: [:fileName | fileName endsWith: '~'].
   fileNames do: [:fileName | dir deleteFileNamed: fileName]]

Windows PowerShell です。rm (Remove-Item コマンドレット) にも -recurse があるのですがヘルプには「このコマンドレットでは、Recurse パラメータは正常に機能しません。」との注意が・・・
1
ls $args[0] -include *~ -recurse | rm

Windows専用。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <windows.h>
#include <string>

void traverse(const std::string& parent, void (*func)(const std::string& path))
{
    WIN32_FIND_DATA wfd;

    HANDLE h = ::FindFirstFile((parent + "\\*").c_str(), &wfd);

    if (h == INVALID_HANDLE_VALUE)
    {
        return;
    }

    do
    {
        const std::string name = wfd.cFileName;

        if (name == "." || name == "..")
        {
            continue;
        }

        const std::string child = parent + "\\" + name;

        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            traverse(child, func);
        }
        else // file
        {
            func(child);
        }
    }
    while (::FindNextFile(h, &wfd));

    ::FindClose(h);
}

void delete_garbage(const std::string& path)
{
    if (!path.empty() && path[path.size() - 1] == '~')
    {
        ::DeleteFile(path.c_str());
    }
}

int main()
{
    traverse("foo", &delete_garbage);
}

フォルダ名が指定できるようになってなかったので修正。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
--- orig.cpp	Fri Jul 13 01:13:55 2007
+++ main.cpp	Fri Jul 13 01:15:08 2007
@@ -45,7 +45,10 @@
     }
 }
 
-int main()
+int main(int argc, char* argv[])
 {
-    traverse("foo", &delete_garbage);
+    if (argc == 2)
+    {
+        traverse(argv[1], &delete_garbage);
+    }
 }

とりあえず作った. 散らかし器付き.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require "pathname"

class Pathname
  def scatter( upper, upper_h = 0 )
    raise "`#{ self }' is not directory, ;-<" unless self.directory?
    (10**upper).times {|i|
      r = rand( 2 + ( upper_h > 0 ? 1 : 0 ) )
      case r
      when 0, 1
        nfile = self + ( "%0#{ upper }d#{ r == 0 ? "" : "~" }" % i)
        nfile.open( "w" ) {|fd|
          fd.write( File.open( "/dev/random" ) {|rdev| rdev.read( 8 ) } )
        } unless nfile.exist? and nfile.directory?
      when 2
        ndir = self + ("%0#{ upper }d" % i)
        ndir.mkdir unless ndir.exist?
        ndir.scatter( upper, upper_h - 1 ) if ndir.directory?
      end
    }
    self
  end
  
  def cleaner
    raise "`#{ self }' is #{ self.ftype }, :-)" unless self.directory?
    self.children.each {|epath|
      if epath.file?
        epath.delete if /~\Z/ =~ epath
      elsif epath.directory?
        epath.cleaner
      end
    }
  end
end
Pathname.new( "temp" ).scatter( 2, 2 ).cleaner

CL-FAD ライブラリの walk-directory 関数を使ってます。
1
2
3
4
5
6
(defun remove-backup ()
  (let ((str (or (pathname-type path) (pathname-name path))))
    (when (char= #\~ (char str (1- (length str))))
      (delete-file path))))

(fad:walk-directory ディレクトリ名 #'remove-backup)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(require 'cl)
(defun directory-files-recursively (dir &optional match-regexp nosort)
  (loop for f in (directory-files dir t "." nosort)
        when (and (not (string-match "/\\.+$" f)) (file-directory-p f))
        appending (directory-files-recursively
                   f match-regexp nosort)
        when (string-match match-regexp f) collect f))

(defun delete-backup-files-recursively (dir)
  (mapcar #'delete-file (directory-files-recursively dir "~$" t)))


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import java.io._

object FileUtil {
  def removeFilesBySuffix(d:String, s:String):Unit = {
    removeFilesBySuffix(new File(d), s)
  }
  def removeFilesBySuffix(d:File, s:String):Unit = {
    d.list.foreach(f => {
      val c = new File(d, f)
      if(c.isDirectory) removeFilesBySuffix(c, s)
      else if(f.endsWith(s)) c.delete
    })
  }
}

ファイルを弄くるなんて、普段やらないのでこういう書き方で良いのかよくわかんね。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def file_delete (path="")
  Dir.foreach(path) do |file|
    filepath = path + '/' + file
    # ディレクトリで、かつ . か .. 以外だったら再帰的にメソッドを呼ぶ
    if File.ftype(filepath) == 'directory' && file != '.' && file != '..'
      file_delete(filepath)
    end
    File.delete(filepath) if file =~ /~$/
  end
end

file_delete ("/hoge/file/path")

Lua は ANSI C の範囲内(ディレクトリの概念がない)で実装されているので、この問題を解くのにも外部モジュールを使わなければいけません。ここでは LuaFilesystem <http://www.keplerproject.org/luafilesystem/> を使いました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function dir_walk(path, f)
  if lfs.attributes(path, "mode") == "directory" then
    for file in lfs.dir(path) do
      if not(file == "." or file == "..") then
        dir_walk(path .. "/" .. file, f)
      end
    end
  else
    f(path)
  end
end

dir_walk(arg[1], function(path)
  if string.find(path, "~$") then
    print(path)
    os.remove(path)
  end
end)

1
2
3
4
5
6
7
8
9
remove.backup <- function(root = "."){
   list <- dir(root, all.files=TRUE)
   list <- list[-grep("^\\.$|^\\.\\.$", list)]
   list <- file.info(file.path(root, list))
   rmfiles <- grep("~$", rownames(list[which(list$isdir==FALSE),]), value=TRUE)
   dirs    <- rownames(list[which(list$isdir==TRUE),])
   file.remove(rmfiles)
   for(d in dirs) remove.backup(d)
}

GNU Prologです。
notも無いなんて…。
 $ gprolog --entry-goal '[del],main,halt'
で音もなく静かに実行。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
del_tilda(D):-del_tilda([],[D]).

not(P):-P,!,fail.
not(_).

del_tilda(_,[]).
del_tilda(D,[L|Ls]):-(D=[]->L1=L;atom_list_concat([D,'/',L],L1)),
                     (file_property(L1,type(directory)),
                      not(member(L,['..','.']))
                     -> directory_files(L1,L2),del_tilda(L1,L2),del_tilda(D,Ls)
                     ;  atom_chars(L,C),last(C,'~'),delete_file(L1),del_tilda(D,Ls)),!.
del_tilda(D,[_|Ls]):-del_tilda(D,Ls).

atom_list_concat([A],A).
atom_list_concat([A|As],B):-atom_list_concat(As,Bs),atom_concat(A,Bs,B).

main:-del_tilda('test').

修正しました

修正しました


	
1
2
3
4
5
function cleanup(d){
  for (f : walkDirectory(d)){
    if (!f.directory && f.name.endsWith("~"))  delete(f)
  }
}

Myを使えば楽ちん
1
2
3
4
5
6
7
Public Sub RemoveFile(ByVal directory As String)

    For Each file As String In My.Computer.FileSystem.GetFiles(directory, FileIO.SearchOption.SearchAllSubDirectories, "*~")
        My.Computer.FileSystem.DeleteFile(file)
    Next

End Sub

Win限定。
1
2
3
4
5
6
7
8
9
// Run as *.js or *.hta //<script language="jscript">
with(new ActiveXObject("Scripting.FileSystemObject")) (function(fold, spec){
	for(var cf = new Enumerator(fold.SubFolders); !cf.atEnd(); cf.moveNext())
		arguments.callee(cf.item(), spec);
	try { DeleteFile(fold.Path +"\\"+ spec) } catch(e){}
})(GetFolder(
	this.WSH ? (WSH.Arguments.Length ? WSH.Arguments(0) : ".\\") : prompt("", ".\\")
	), "*~");
//</script>

setCurrentDirectory を使ってパスの扱いを単純化
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import System.Directory
import System.Environment

main :: IO ()
main = getArgs >>= mapM_ cleaning

cleaning :: FilePath -> IO ()
cleaning = (>> removeBackupFiles) . setCurrentDirectory

removeBackupFiles :: IO ()
removeBackupFiles = getDirectoryContents "." >>= mapM_ rmbk . drop 2

rmbk :: FilePath -> IO ()
rmbk path
 = do { dir <- doesDirectoryExist path
      ; let backup = '~'== last path
      ; if dir 
           then if backup then removeDirectoryRecursive path else cleaning path
           else if backup then removeFile path               else return ()
      }

特に工夫なし。
1
2
3
4
5
6
7
8
9
def clean_directory(file) {
  if(file.isFile() && file.name.endsWith("~")) {
    file.delete()
  } else if (file.isDirectory()) {
    file.listFiles().each { f ->
      clean_directory(f)
    }
  }
}


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
file_delete := method(path,
    dir := Directory clone setPath(path)
    dir files foreach(f,
        if(f name endsWithSeq("~"),
            f remove
        )
    )   
)

file_delete("/foo/bar")

-type f つけたほうが良いかもしれませんね。

ごめんなさいバグがありました。
ディレクトリを降りていったあとにカレントディレクトリを
もとへ戻していなかった。こちらが正しいはずのコード
です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import System.Directory
import System.Environment

main :: IO ()
main = getArgs >>= mapM_ cleaning

cleaning :: FilePath -> IO ()
cleaning = (>> removeBackupFiles) . setCurrentDirectory

removeBackupFiles :: IO ()
removeBackupFiles
 = do { cwd <- getCurrentDirectory
      ; getDirectoryContents "." >>= mapM_ (rmbk cwd) . drop 2
      }

rmbk :: FilePath -> FilePath -> IO ()
rmbk cwd path
 = do { dir <- doesDirectoryExist path
      ; let backup = '~'== last path
      ; if dir 
           then if backup 
                   then removeDirectoryRecursive path 
                   else cleaning path >> setCurrentDirectory cwd
           else if backup 
                   then removeFile path
                   else return ()
      }


	
1
dir * -recurse -include *~ | rm


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//ソースファイルのエンコーディングはUTF-8にしてください
//コンパイルオプション:-std=gnu99 -fobjc-exceptions

#import <Foundation/Foundation.h>

#define WithUTF8Format(a, ...)	[NSString stringWithFormat:[NSString stringWithUTF8String:(a)], __VA_ARGS__]
#define JLog(...)	[(NSFileHandle*)[NSFileHandle fileHandleWithStandardError] \
					writeData:[[NSString stringWithFormat:__VA_ARGS__] \
					dataUsingEncoding:NSUTF8StringEncoding]];

BOOL deleteGarbage( NSString* folder ) {
	BOOL result = YES;
	NSAutoreleasePool* pool1 = [[NSAutoreleasePool alloc] init];
	
	JLog( WithUTF8Format( "フォルダの中を検索します: %@\n", folder ) );
	
	@try {
		NSFileManager* manager = [NSFileManager defaultManager];
		NSDictionary* folderAttributes = [manager fileAttributesAtPath:folder traverseLink:NO];
		
		if ( folderAttributes == nil )
			@throw WithUTF8Format( "フォルダの属性を取得できませんでした: %@\n", folder );
		
		if ( [[folderAttributes fileType] isEqualToString:NSFileTypeSymbolicLink] )
			@throw WithUTF8Format( "フォルダではなくシンボリックリンクです: %@\n", folder );
		else if ( ![[folderAttributes fileType] isEqualToString:NSFileTypeDirectory] )
			@throw WithUTF8Format( "フォルダではなくファイルです: %@\n", folder );
		
		NSArray* paths = [manager directoryContentsAtPath:folder];
		if ( paths == nil )
			@throw WithUTF8Format( "フォルダの内容を取得できませんでした: %@\n", folder );
		
		for ( int i = 0; i < [paths count] && result; i++ ) {
			NSAutoreleasePool* pool2 = [[NSAutoreleasePool alloc] init];
			
			@try {
				NSString* path = [folder stringByAppendingPathComponent:[paths objectAtIndex:i]];
				BOOL isDirectory;
				
				if ( ![manager fileExistsAtPath:path isDirectory:&isDirectory] )
					@throw WithUTF8Format( "ファイルまたはフォルダが存在しません: %@\n", path );
				
				if ( isDirectory )
					result = deleteGarbage( path );
				else if ( [path hasSuffix:@"~"] ) {
					if ( ![manager removeFileAtPath:path handler:nil] )
						@throw WithUTF8Format( "ファイルを削除できませんでした: %@\n", path );
					JLog( WithUTF8Format( "ファイルを削除しました: %@\n", path ) );
				}
			} @finally {
				[pool2 release];
			}
		}
	} @catch ( NSString* error ) {
		JLog( error );
		result = NO;
	}
	
	[pool1 release];
	return result;
}

int main( int argc, const char** argv ) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	deleteGarbage( @"./Trash" );
	
    [pool release];
    return 0;
}

jmuk さんのを参考にさせていただいた。
というより、ほぼパクリ。すいません。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
require 'find'

def delete_bak(dir_path = './')
  Find.find("#{dir_path}") do |path|
    if File.file?(path) && path =~ /~$/
      File.unlink(path)
    end
  end
end
delete_bak

参考URLの文字列入れるの忘れたので再投稿。
ついでに、delete_bak の引数として複数のディレクトリパス名を取るようにした。
引数なしの場合は、カレントディレクトリ(./)がデフォルトでセットされる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
require 'find'

def delete_bak(dir_path = './')
  Find.find("#{dir_path}") do |path|
    if File.file?(path) && path =~ /~$/
      File.unlink(path)
    end
  end
end

if ARGV.empty?
  delete_bak
else
  ARGV.each{|arg| delete_bak(arg)}
end

SML# 0.30ではreadDirでparentArcとcurrentArcも拾ってしまうので、
明示的にパターンマッチしています。
Basis Libraryの仕様通りの挙動なら

| SOME "." => loop ()
| SOME ".." => loop ()

このコードは不要です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
fun remove_backup path =
let
  open OS.FileSys

  val dir = openDir path

  fun loop () =
    case readDir dir of
      NONE => ()
    | SOME "." => loop ()
    | SOME ".." => loop ()
    | SOME file => if isDir file then (remove_backup file;loop ())
                   else if String.isSuffix "~" file then (remove file;loop ())
                   else loop ()
in
  chDir path;
  loop ();
  closeDir dir;
  chDir (OS.Path.parentArc)
end


プログラム(修正版)にまだバグがあった.POSIX系のファイルシステムでは,
シンボリックリンクでディレクトリパスに循環があると停止しなくなってしまう.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module Main (main) where

import System.Directory ( setCurrentDirectory, getCurrentDirectory
                        , getDirectoryContents
                        , removeDirectoryRecursive, removeFile )
import System.Posix.Files (getSymbolicLinkStatus, isDirectory)
import System.Environment (getArgs)

main :: IO ()
main = getArgs >>= mapM_ cleaning

cleaning :: FilePath -> IO ()
cleaning = (>> removeBackupFiles) . setCurrentDirectory

removeBackupFiles :: IO ()
removeBackupFiles
 = do { cwd <- getCurrentDirectory
      ; getDirectoryContents "." >>= mapM_ (rmbk cwd) . drop 2
      }

rmbk :: FilePath -> FilePath -> IO ()
rmbk cwd path
 = do { fstat <- getSymbolicLinkStatus path
      ; let backup = '~'== last path
      ; if isDirectory fstat
           then if backup then removeDirectoryRecursive path 
                else cleaning path >> setCurrentDirectory cwd
           else if backup then removeFile path
                else return ()
      }

dmd 2.007で。
listdir(".","*~")を使ったら固まった。
てことで、if使うことに。
1
2
3
4
5
import std.file;
import std.regexp;
void main(string[] argv) {
    foreach (string d; listdir(argv[1])) if(d[$-1]=='~') remove(d);
}

ワンライナーで。
1
path="";pathの全ファイル列挙を反復;もし((正規表現マッチ(対象,"~$"))<>"")なら,対象をファイル削除

boost::filesystemはあまり使ったことないので勉強がてら.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <boost/filesystem/fstream.hpp>

void removeBackupFile(const boost::filesystem::path& dir)
{
    namespace fs = boost::filesystem;
    if (fs::exists(dir)) {
        for (fs::directory_iterator i(dir), end; i != end; ++i) {
            if (fs::is_directory(i->status())) {
                removeBackupFile(i->path());
            } else {
                const std::string& name = i->path().leaf();
                if (name[name.size() - 1] == '~') {
//                    std::cout << "remove:" << i->path() << std::endl;
                    fs::remove(i->path());
                }
            }
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc == 1) {
        std::cerr << argv[0] << " dir" << std::endl;
        return 1;
    }
    try {
        removeBackupFile(argv[1]);
    } catch (boost::filesystem::filesystem_error& e) {
        std::cerr << "err:" << e.what() << std::endl;
    }
    return 0;
}

いきなり消すと怖いので、確認機能付き。
FileUtils.rmでまとめて消します。
(いちど言語を指定せずに投稿してしまったので再投稿します。以後気をつけますorz)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
require "fileutils"

dir = "/どこかのディレクトリ/"
pattern = /~$/
rm_path_list = []

#パターンにマッチするファイルを探す
Dir.glob("#{dir}**/*").each do |path|
  if File.basename(path) =~ pattern
    rm_path_list << path
    puts "- " + path
  end
end

if rm_path_list != []
  answer = ""
  #削除するか質問する(yかnが入力されるまで繰り返す)
  until /[yn]/ =~ answer
    print "削除しますか[yn]? "
    answer = gets.chomp
    case answer
      when "y"
        begin
          #該当ファイルの配列からまとめて削除
          FileUtils.rm(rm_path_list)
        rescue
          puts "削除に失敗しました。"
        end
      when "n"
        puts "中止しました。"
    end
  end
else
  puts "パターンにマッチするファイルがありません。"
end

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import System
import System.Directory
import System.Posix.Directory
import System.IO.Unsafe
import System.Posix.Files
import System.FilePath
import System.Environment

readAllEntries :: DirStream -> IO [FilePath]
readAllEntries st = 
  do s <- readDirStream st
     case s of 
       [] -> return []
       _  -> do rest <- unsafeInterleaveIO $ readAllEntries st
                return (s:rest)

readChildren :: FilePath -> IO [FilePath]
readChildren p =
  do st <- getFileStatus p
     if isDirectory st 
      then unsafeInterleaveIO $ (walkDir p) 
      else return []

walkDir :: FilePath -> IO [FilePath]
walkDir path = 
  do ents <- openDirStream path >>= readAllEntries
     child <- mapM readChildren $ map join $ filter ((/=) '.'.head) ents
     return $ map join ents ++ concat child
 where join = joinPath.(++) [path].flip (:) []

rmbk :: FilePath -> IO()
rmbk a = walkDir a >>= mapM_ removeFile.filter ((==) '~'.last)

main :: IO()
main = getArgs >>= mapM_ rmbk

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
require 'ftools'
def clean_nyoro(file)
  if File.directory?(file)
    Dir.glob(file+"/*") do |f|
      clean_nyoro(f)
    end
  elsif /~$/ =~ file
    File.rm_f(file)
  end
end
clean_nyoro(ARGV.shift || exit)

動作環境 Fedora7 Perl5.8.8

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/perl
use strict;

exit main();

sub main {
        if(@ARGV != 1){
                print "usage: perl 3361.pl <target-directory>\n";
                return -1;
        }

        my $dir = $ARGV[0];
        my $loop = 0;
        $dir =~ s/ (.*)\/$/$1/;

        if(! -d $dir){
                print "Can't open $dir\n";
                return -2;
        }

        return del_file($dir,$loop);
}

sub del_file {
        no strict 'refs';
        my ($dir,$loop) = @_;

        if(!opendir(DIR.$loop,$dir)){
                print "$dir $!\n";
                return ($loop > 0) ? $loop - 1 : $loop;
        }

        while(my $file = readdir(DIR.$loop)){
                next if($file =~ / \.{1,2}$/);

                my $dir_file = "$dir/$file";

                if(-d $dir_file){
                        $loop = del_file($dir_file,++$loop);
                }
                elsif($file =~ /~$/){
                        if(!unlink($dir_file)){
                                print "$dir_file $!\n";
                        }
                        else{
                                print "$dir_file delete\n";
                        }
                }
        }

        closedir DIR.$loop;

        return ($loop > 0) ? $loop - 1 : $loop;
}

特にひねりなしです。

1
2
3
4
5
6
7
def dir = new File("c:/work")

dir.eachFileRecurse{
    if( it.file && it.name.endsWith("~") ){
        file.delete()
    }
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#load "unix.cma";;

let dir_fold f acc path =
  let dir = Unix.opendir path in
  let acc = ref acc in
  try 
    while true do
      match Unix.readdir dir with
      | "." | ".." -> ()
      |  s  -> acc := f !acc (Filename.concat path s)
    done; assert false
  with err -> Unix.closedir dir;
    if err=End_of_file then !acc else (raise err);;

let dir_iter_rec =
  let file_iter f path =
    dir_fold (fun acc s ->
      match (Unix.stat s).Unix.st_kind with
      | Unix.S_DIR -> (s::acc)
      | _ -> f s; acc
      ) [] path in
  let rec iter_rec f ls =
    List.iter (fun path -> iter_rec f (file_iter f path)) ls
  in 
    (fun f path -> iter_rec f (file_iter f path));;

let rmbak path =
  dir_iter_rec (fun s -> 
    if Filename.check_suffix s "~" then Unix.unlink s
    ) path;;

VBA for Excel (2003)。デストローイ。ノーフューチャー。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Sub Main()
  Dim p, o
  With Application.FileDialog(msoFileDialogFolderPicker)
    .Show
    p = .SelectedItems(1)
    Set o = CreateObject("Scripting.FileSystemObject")
    Call Df(p, o)
  End With
End Sub

Sub Df(p, o)
  Dim f, d
  With o.GetFolder(p)
    For Each f In .Files
      If Right(f.Name, 1) = "~" Then
        f.Delete
      End If
    Next
    For Each d In .SubFolders
      Call Df(p + "\" + d.Name, o)
    Next
  End With
End Sub

os.walkは便利ですが、再帰を書きたかったのであえて使わずに書いてみました。

あとは本当は例外処理部分を追加したほうが良さそうですね(コマンドライン引数が正しくない場合や、引数に不正なパスを指定した場合の処理等)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import os
import sys

def delete_tilde(path):
  
  for f in (os.path.join(path, x) for x in os.listdir(path)):
    if os.path.isdir(f):
      delete_tilde(f)
    elif f[-1] == "~":
      os.remove(f)
      print f, "is deleted."

if len(sys.argv) != 2:
  print "usage: %s pathname" % (sys.argv[0])
else:
  target_dir = os.path.abspath(sys.argv[1])
  delete_tilde(target_dir)

AntBuilderを使ってみました。
なお、「**/*~」はデフォルト除外パターンらしいので、
「defaultexcludes:'no'」とすることで、これを回避しています。
1
2
def ant = new AntBuilder()
ant.delete(dir:'c:/work', includes:'**/*~' defaultexcludes:'no')

daleteFilesBySuffixメソッド内で"~"ベタ書きしてたら引数の意味なくね?
1
2
3
4
5
6
7
8
    public void daleteFilesBySuffix(final String suffix) {
        FileFilter filter = new FileFilter() {
            public boolean accept (File pathname) {
                return pathname.isFile() && pathname.getName().endsWith(suffix);
            }
        };
        deleteTargetFiles(rootDir, filter);
    }

File::Find::Rule で削除対象ファイルをリストとして取得。あとでまとめて削除している。
コードが長いのは消す確認を2度しているのと、Optionをお約束で入れてみたから。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#! /opt/local/bin/perl
use strict;
use warnings;

use Getopt::Long;
use File::Find::Rule;

# Remove all files matching $TARGET_REGEX
my $TARGET_REGEX = qr/~$/;

my %options = (
    "debug"       => undef,     # debug mode
);

GetOptions (
    "debug"     => \$options{debug},
) || die("failed GetOptions");



sub main()
{
    my @dir = ("."); # default
    @dir    = @ARGV if @ARGV;

    firstConfirm(\@dir);
    rmR(\@dir);
}

sub firstConfirm(\@)
{
    my $dir = shift;
    for my $d (@$dir) {
        print $d, "\n";
        die("Usage: $0 dir1 dir2 ") if ! -d $d;
    }

    print STDERR qq/[WARNING] removing files ending with "~" under "@$dir" directory.\n/;

    my $confirm_str = "I want to continue";
    print STDERR qq/Execute? [Type "$confirm_str"]: /;
    chomp(my $line = <STDIN>);

    if ($line !~ $confirm_str) {
        print STDERR "bye\n";
        exit(0);
    }
}

sub rmR(\@)
{
    my $dir = shift;

    print STDERR "[DEBUG] removing under @$dir\n" if defined $options{debug};

    my @files_to_remove = File::Find::Rule->file()
                        ->name($TARGET_REGEX)
                        ->in(@$dir);

    my $f_count = @files_to_remove;
    if ( defined $f_count && ($f_count == 0) ) {
        print STDERR "\nNo file[s] to remove found.\n";
        exit(0);
    }

    doRm(\@files_to_remove) if finalConfirm(\@files_to_remove);

}

sub finalConfirm(\@)
{
    my $files_to_remove_ref = shift;
    my $result ;
    print "unlinking:\n";
    print map "\t\t$_\n", @$files_to_remove_ref;
    print "continue?[y/n]: ";

    my $ans = <STDIN>;
    $result = 1 if $ans =~ m/y(es){0,1}/i;

    return $result;
}

sub doRm(\@)
{
    my $dir_ref = shift;

    unlink @{$dir_ref};
}


main();

Lost_dogです。Haskellでディレクトリ操作をしようとしたら、いろんな困難にぶち当たりました。IOモナドははびこるし、環境変数とかいう見えないグローバル変数に悩まされるし…

これは現行のOSインターフェースとHaskellがいかに相性が悪いかということを物語ってます。

GHCiをシェル代わりに使いたいんですが、現行のディレクトリ関係のモジュールは、完全にOSインターフェースをなぞっているだけで、まったくHaskellらしさがなく、使い勝手が極めて悪いです。まあ、自分で作れってことですね。。

で今回のお題ですが、ディレクトリを再帰的に扱うっていう、よくある作業です。最初は

recursive f dir

みたいなインターフェースを考えました。でもディレクトリっていうのはツリー構造なんだから、ツリーとして扱うべきなんじゃないかと思い、

lsTree dir >>= mapM f

みたいな形にしました。いかがでしょうか。

ちなみに、2010年の私の夢は、bashがすべてhaskellに置き換わることですw

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module Main where

import Prelude hiding (mapM)
import Data.Tree
import Data.List
import Data.Traversable
import System.Directory
import System.FilePath
import System.Environment

main = getArgs 
       >>= lsTree.head 
       >>= mapM (doOnFileIf (isSuffixOf "~".takeFileName) removeFile)

isDir  = doesDirectoryExist

isFile = doesFileExist

ls :: FilePath -> IO [FilePath]
ls d = fmap (map (d</>).(\\[".",".."])) $ getDirectoryContents "."

lsTree :: FilePath -> IO (Tree FilePath)
lsTree = unfoldTreeM f
  where f x = do p <- isDir x
                 if p then do fs <- ls x; return (x, fs)
                      else return (x, [])

doOnFileIf :: (FilePath -> Bool) -> (FilePath -> IO ()) -> FilePath -> IO ()
doOnFileIf p f x = do q <- isFile x
                      if p x && q then f x else return ()

すみません、コードみすってました。。lsが間違ってます。正しくは以下です。

1
2
ls :: FilePath -> IO [FilePath]
ls d = fmap (map (d</>).(\\[".",".."])) $ getDirectoryContents d

Index

Feed

Other

Link

Pathtraq

loading...