2014/08/15

Windows에서 특정 디렉토리 이하 모든 파일 및 디렉토리 지우기

Linux/Unix 계열에서 특정 디렉토리아래의 모든 디렉토리 파일을 지우려면 다음과 같이 하면 된다.

현재 디렉토리 아래의 some_directory 밑의 모든 파일 및 디렉토리 지움

rm -rf ./some_directory/*

이런 작업을 윈도우에서 하려면 어떻게 하면 될까?

del 명령과 rmdir 명령을 아무리 조합해도 한번에 만족할 만한 결과를 얻기 힘들다.
파일은 다 지워지는데 빈 디렉토리는 남아있다던가 특정 디렉토리 아래의 모든 것을 지우고 싶은데 그 특정 디렉토리도 지우지 않고는 안되는... 그래서 다 지우고 또 그 디렉토리를 만들어 주는 등등.

https://www.google.co.kr/search?q=dos+cmd+completely+delete+sub+director&ie=utf-8&oe=utf-8

위 검색결과를 보면 알겠지만 깔끔하고 만족스러운 방법이 없다.

그래서 Perl을 항상 깔아 쓰는 나는 이럴때 그냥 Perl로 하기로 했다.
Perl one-liner 를 써서 File::Path 모듈을 이용해서 하면

perl -MFile::Path=remove_tree -e "remove_tree('.\some_directory', { keep_root=>1 })"

2014/04/07

Perl의 다단계 deep hash와 autovivification

Perl에서 autovivification은 어떤때는 아주 강력한 장점이 되기도 하지만 잘 모르고 쓰다가는 찾기 힘든 버그를 만들 수 도 있다.

autovivification은 Perl의 해시구조에서 해시키가 존재하지 않을 경우 자동으로 생성해주는 기능으로 잘 활용하면 다음과 같은 집계작업을 아주 쉽게 끝낼 수 있다.

* 과일을 색깔별로 몇개씩 있는지 집계하는 코드

use strict;
use Data::Dumper;
my %h;
while (<DATA>) {
    chomp;
    my ($color, $fruit) = split;
    $h{$color}{$fruit}++;
}
print Dumper(\%h);

__DATA__
green apple
red apple
red strawberry
green banana
green apple
yellow banana
red apple
green apple
yellow apple
green strawberry

* 결과

$VAR1 = {
          'red' => {
                     'apple' => 2,
                     'strawberry' => 1
                   },
          'yellow' => {
                        'banana' => 1,
                        'apple' => 1
                      },
          'green' => {
                       'strawberry' => 1,
                       'banana' => 1,
                       'apple' => 3
                     }
        };


하지만 다단계 hash를 사용할때 가장 실수하기 쉬운 것은 만약에 hash키의 존재로 판단해야 하는 작업시 exists로 해당 해시키가 존재하는지 채크하게 되는데

if (exists $h{a}{b}) { say "exists"; }

처럼 $h{a} 즉 a키가 존재하지 않는데 이런식으로 하위 키에 대해 바로 존재여부를 채크하게 되면 중간 단계 a가 autovivification 기능에 의해 예기치 않게 생성되어 버린다. 그래도 문제 없을 경우도 있겠지만 만약에 첫 단계 해시키의 존재유무로 어떤 작업하는 로직이 같은 프로그램 안에 있다면 언제 용이 튀어 나올지 모른다. 그래서 이런 동작을 방지하려면

if (exists $h{a} && exists $h{a}{b}) { say "exists"; }

처럼 단계적으로 채크해 들어가야 한다.  그런데 여러단계면 이런식으로 계속적으로 채크 코드를 나열하는건 손가락이 아픈 일이라 생각되어 예전에 언뜻 본 autovivification 여부를 조정할 수 있는 autovivification 모듈을 사용하면 편하지 않을까 싶어 한 번 테스트를 해보았다.

아래 코드는 if 문의 판단부분에 do블럭으로 autovivification으로 비활성화 시키고 해시키 여부 판단코드를 쓴것과 다단계 채크 방식의 속도를 비교했고, 추가로 매번 if문 안에서 비활성화 시키지 않고 기본으로 비활성화 시킨상태에서 비교한 것이다.


#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
use autovivification;
use Benchmark qw/cmpthese/;

cmpthese(1000000, {
    step => sub {
        my %h;
        if (exists $h{a} && exists $h{a}{b}) { say "exists"; }
    },
    autovivification => sub { 
        my %h;
        if (do { no autovivification; exists $h{a}{b} }) { say "exists"; }
    },
});
no autovivification;
# 이 이하는 autovivification 비활성화
cmpthese(1000000, {
    step2 => sub {
        my %h;
        if (exists $h{a} && exists $h{a}{b}) { say "exists"; }
    },
    autovivification2 => sub { 
        my %h;
        if (exists $h{a}{b}) { say "exists"; }
    },
});
.

벤치마크 결과는 다음과 같다.

            (warning: too few iterations for a reliable count)
                      Rate autovivification             step
autovivification  265957/s               --             -92%
step             3194888/s            1101%               --
            (warning: too few iterations for a reliable count)
                       Rate autovivification2             step2
autovivification2  348432/s                --              -89%
step2             3194888/s              817%                --


다단계로 채크해 들어가는게 autovivification 모듈을 사용하는 것 보다 10배 가까이 빠른 속도를 보여준다.

결론:
 속도가 중요하다면 손가락이 아파도 해시키의 존재 여부는 다단계로 채크하자.

2013/12/03

2013년 Advent calendar들


올해도 어김없이 찾아온 Perl Korea Advent Calendar 2013

http://advent.perl.kr/2013/

전 세계적으로도 다른 Perl관련 Advent Calendar들이 시작되었음

Perl Advent Calendar
http://www.perladvent.org/2013/

Perl6 Advent Calendar
http://perl6advent.wordpress.com/

Catalyst Advent Calendar
http://www.catalystframework.org/calendar/

Dancer Advent Calendar
http://advent.perldancer.org/2013

Future Advent Calendar
http://leonerds-code.blogspot.co.uk/search/label/advent

일본 Perl Advent Calendar
http://qiita.com/advent-calendar/2013/perl

일본 Mojolicious Advent Calendar
http://qiita.com/advent-calendar/2013/mojolicious


그외에도 
Sysadmin Advent Calendar
http://sysadvent.blogspot.kr/

일본에서는 오픈소스 커뮤니티에 Advent Calendar 열풍이 불어서
Dog이나 Cow나 Advent Calendar를 만들고 있음
http://qiita.com/advent-calendar/2013



2013/06/21

PERL_NEW_COPY_ON_WRITE on Windows Perl.

I've found PERL_NEW_COPY_ON_WRITE option at https://metacpan.org/module/DAGOLDEN/perl-5.19.1/pod/perldelta.pod#Performance-Enhancements

This feature was already available in 5.18.0, but wasn't enabled by default.

So I recompiled Perl 5.18.0 with PERL_NEW_COPY_ON_WRITE option on Windows and compared with Non-PERL_NEW_COPY_ON_WRITE Strawberry Perl 5.18.0.

This is the result.


<Test code>

perl -MBenchmark=cmpthese -e "sub A {my ($s) = @_; length($s) } sub B { my ($s) = @_; $s .= 'B'; length($s) } cmpthese(10000, { test1 => sub { my $s = 'A'x1000000; A($s) }, test2 => sub { my $s = 'A'x1000000; B($s) } })"

"sub A" doesn't modify the input string but "sub B" modfies the input string.

* Strawberry Perl 5.18.0

test1  3374/s    --   -2%
test2 3428/s    2%    --

* PERL_NEW_COPY_ON_WRITE version

test1  8905/s  539%    --
test2 1393/s    --  -84%




PERL_NEW_COPY_ON_WRITE option accelerates "sub A" but slows down "sub B".
I'm wondering why "sub B" is much slower in PERL_NEW_COPY_ON_WRITE option.


2013/06/17

Hooking MS windows messages on Wx Perl.

Sometimes you may need to catch a Windows message that is not already handled by wxWidgets, so there is no Wx::Event for it. With a bit of help from the WIN32::API modules it is possible to hook into the WndProc chain for a wxWindow and watch for the message you are interested in.

The magic is in the SetWindowLong function. When used with the GWL_WNDPROC flag it causes a new WndProc to be set for the window, and returns the old one. This lets you write a function in Perl that can get first crack at all the Windows messages being sent to the window, and if you are not interested in them then pass them on to the original wxWidgets WndProc.

#!/usr/bin/env perl
use strict;
use Win32::API;
use Win32::API::Callback;
use Wx;

# Perl port of Python Code: http://wiki.wxpython.org/HookingTheWndProc

{

    package MyFrame;
    use base 'Wx::Frame';

    use constant GWL_WNDPROC => -4;

    # LONG  SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong);
    Win32::API->Import('user32', 'SetWindowLongW', 'NIK', 'N');
    # LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    Win32::API->Import('user32', 'CallWindowProcW', 'NNIII', 'N');

    sub new {
        my $ref = shift;
        my $self = $ref->SUPER::new( undef,           # parent window
            -1,              # ID -1 means any
            'wxPerl rules',  # title
            [-1, -1],        # default position
            [150, 100],      # size
        );
        # controls should not be placed directly inside
        # a frame, use a Wx::Panel instead
        my $panel = Wx::Panel->new( $self,            # parent window
            -1,               # ID
        );
        # create a button
        my $button = Wx::Button->new( $panel,         # parent window
            -1,             # ID
            'Click me!',    # label
            [30, 20],       # position
            [-1, -1],       # default size
        );
        $self->{newWndProc} = Win32::API::Callback->new(sub { $self->_MyWndProc(@_) }, 'NIII', 'N');
        $self->{oldWndProc} = SetWindowLongW( $self->GetHandle(), GWL_WNDPROC, $self->{newWndProc} );

        return $self;
    }
    
    sub _MyWndProc {
        my ($self, $hWnd, $msg, $wParam, $lParam) = @_;
        # You can process MS Windows messages here.
        print join (',',@_),"\n";
        CallWindowProcW($self->{oldWndProc}, $hWnd, $msg, $wParam, $lParam);
    }

}

{

    package MyApp;
    use base 'Wx::App';

    sub OnInit {
        my $frame = MyFrame->new;
        $frame->Show( 1 );
    }

}

my $app = MyApp->new;
$app->MainLoop;



2013/05/19

Perl 5.18.0 출시

2013년 5월 18일 Perl 5.18.0 버젼이 출시되었다. 5.18은 우리 현대사에서도 의미 있는 날인데 Perl 5.18.0 버젼을 5월 18일에 내놓는 센스를 발휘하다니...

Perl 5.18.0 에서 바뀐 내용들은 https://metacpan.org/module/RJBS/perl-5.18.0/pod/perldelta.pod 에 잘 정리되어 있다.

5.18.0버젼은 5.16.0 버젼이후 1년동안의 개발기간 동안 113명의 개발자에 의해 2100개의 파일, 400000라인의 수정/추가가 이루어 졌다고 한다.

중요한 점만 짚어 보자면..

Unicode 6.2를 지원.

예전에는 암묵적으로 허용되었던 foreach my $item qw/A B C/ { ... } 같은 코드는 ( )를 명시적으로 싸서 foreach my $item (qw/A B C/)  { ... } 처럼 해 주지 않으면 에러가 난다.  

given ~ when 구문과 ~~스마트매치 연산자가 실험적기능(experimental)으로 빠졌다. 그동안 너무 복잡하고 설계에 문제점이 있다고 말이 많았는데 이 기회에 일단 빼놓고 다시 검토할 계획인가 보다. 5.18에서는 해당 문법을 쓰면 경고가 난다.
 참고: https://metacpan.org/module/RJBS/perl-5.18.0/pod/perldelta.pod#The-smartmatch-family-of-features-are-now-experimental

그리고  encoding, Archive::Extract, CPANPLUS 등 코어모듈에서 제외된 모듈들이 제법 있다.
 참고: https://metacpan.org/module/RJBS/perl-5.18.0/pod/perldelta.pod#Module-removals

또 hash의 취약점 공격을 막기 위해 hash에서 값을 뽑아낼때 마다 랜덤하도록 바뀌었다. 이 때문에 테스트에서 에러가 나는 모듈이 종종 있다.  특히 많이 쓰는 JSON::XS모듈도 그 중 하나인데 패치 되기전에 일단 cpan -T JSON::XS 나 cpanm -n JSON::XS 명령으로 테스트과정을 생략하고 설치해서 쓰도록 하면 된다고 한다.(JSON::XS 모듈은 2.34버젼에서 고쳐졌음)
 참고: http://blog.twoshortplanks.com/2013/05/20/5-18-hash-keys/


마지막으로 Perl 5.18.0 버젼은 안타깝게도 우리 곁을 일찍 떠나버린 @am0c 군을 추모하는 글( https://metacpan.org/module/RJBS/perl-5.18.0/pod/perldelta.pod#Obituary ) 이 포함되어 더욱 뜻깊은 버젼이기도 하다.

2013/04/12

How to use ActiveState Perl's unique features in Strawberry Perl

ActiveState Perl supports unique features like PerlScript ActiveX control and new Tcl and  Tkx module.
But they are close-sourced products, So strawberry perl can't provide those features and can't install through CPAN.

I will show you how to steal the features from ActiveState Perl.

1. 
Install Strawberry perl

2.
Get the same version of ActiveState Perl .zip file format from http://downloads.activestate.com/ActivePerl/releases/ and unzip it.

3. PerlScript
Copy ActivePerl-OOOOOO-MSWin32-OOOOO\perl\bin\PerlSE.dll to C:\strawberry\perl\bin
Run cmd.exe as administrator
cd C:\strawberry\perl\bin
Execute "regsvr32 PerlSE.dll" command. (You can uninstall it with "regsvr32 /u PerlSE.dll" command.)

* Open editor and save the following code as "test.wsf" file

<job id="test">
<script language=PerlScript>
    $WScript->Echo("Hello World!");
</script>
</job>

Execute "cscript test.wsf" or double-click "test.wsf" to check to see if PerlScript works or not.

4. Tkx

Copy
ActivePerl-OOOOOO-MSWin32-OOOOO\perl\lib\Tcl.pm
ActivePerl-OOOOOO-MSWin32-OOOOO\perl\lib\Tcl\*

ActivePerl-OOOOOO-MSWin32-OOOOO\perl\lib\Tkx.pm
ActivePerl-OOOOOO-MSWin32-OOOOO\perl\lib\Tkx\*

ActivePerl-OOOOOO-MSWin32-OOOOO\perl\lib\auto\Tcl\*

ActivePerl-OOOOOO-MSWin32-OOOOO\perl\lib\auto\Tkx\*
files and the same folder structure to C:\strawberry\perl\site\lib folder.

ActivePerl-OOOOOO-MSWin32-OOOOO\perl\bin\tkx-ed
ActivePerl-OOOOOO-MSWin32-OOOOO\perl\bin\tkx-ed.bat
ActivePerl-OOOOOO-MSWin32-OOOOO\perl\bin\tkx-prove
ActivePerl-OOOOOO-MSWin32-OOOOO\perl\bin\tkx-prove.bat
files to C:\strawberry\perl\site\bin folder.

Run tkx-ed to check to see if Tkx works or not.



WARNING:
USE AT YOUR OWN RISK. :)