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

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

Posted feedbacks - Nested

Flatten 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
それはどうかな。
[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
for /D /R %%d in (*~) do (echo "%%d")
しかも間違ってるorz
1
for /D /R %%d in (*~) do (rd "%%d" /s /q)
 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)
う。~か~~と読み間違えた・・・orz
修正しました
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")
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 *~
これ今*.pycを消すのに使ってみて便利さに感動しました!

	
 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
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
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 ()
      }
プログラム(修正版)にまだバグがあった.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 ()
      }
まぁ、わざわざオブジェクト思想にこだわる必要もないか。
 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.
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);
}
-type f つけたほうが良いかもしれませんね。
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,