差分FTPを考える(9 – 削除

削除機能を考えてみました。

最後にハッシュをファイルに保存する直前に実行します。

# 削除
while ( my($key ,$value) = each %btmp ){
    if (exists $ftmp{$key}){
    }else{
        print qq(del $key\n);
        $ftp->delete($key);
    }
}

%btmpは前回のハッシュです。
前回のハッシュのキー(つまりファイルパス)をすべて取り出して
%ftmp(今回のハッシュ)の中にそのキー(ファイルパス)があるかないかをチェックしていきます。

あった場合にはスルー、
なかった場合には削除を行います。

ファイルを転送する際はちゃんとそのディレクトリに移動(cwd)しないとうまく転送できません。

つまり、a.txtというファイルをtest/a/b/のディレクトリにputしたい場合は、

  1. test/a/b/ にCD
  2. a.txt をput

としなければなりません。「test/a/b/a.txt」として転送、と一発でいってほしいところですが、NGとなってしまいます。

こう考えると転送先の「test/a/b/a.txt を削除」はエラーになりそうですが、
削除する場合はこれで行けるようです。

その為CDはしていません。

 

2017/09/27 22:55:48 [put] ./2/4/cc.txt
del ./2/4/c.txt

これは、./2/4/c.txt というファイルを ./2/4/cc.txt と変更してみた場合の結果です。

cc.txtは新規と認識されますので、転送されます。

その後、./2/4/c.txt はなくなっていると認識され、削除されます。

どうやらうまく動いているようです。

差分FTPを考える(8 – 組み込み

差分のみ転送するように修正しました。
findを-lsをつけて実行すると以下のように出力します。

1125899906910144    4 -rwx------   1 Admin None         1094 2月 21  2013 test.lnk

0番目がiノード、10番目がファイル名になっています。
あとは前述した、リトライと削除されてしまったファイルのハッシュキーの削除が追加されています。

更に前述したように考慮されていない点が2点あります。

  1. ファイルが削除されても既にアップされたファイルは消さない
  2. ディレクトリが削除されても、削除しない

ファイルについてはハッシュのキーを比較すればできそうですが、
ディレクトリについてはちょっと難しいかもしれません。
ディレクトリについては妥協しても良いかもしれませんが。

#!/usr/bin/perl

use strict;
use Net::FTP;
use Storable qw(nstore retrieve);
use Cwd;
my $cwd = getcwd();

# 引数を読み取る
if ($#ARGV ne 5 ){
    print "引数が誤っています。\n";
    print "$0 [hostname] [user] [password] [fromdir] [todir] [dat]\n";
    exit 1;
}

my $hostname=$ARGV[0];
my $user =$ARGV[1];
my $password=$ARGV[2];
my $from =$ARGV[3];
my $to =$ARGV[4];
my $bdata =$ARGV[5];

my $from_dir =`dirname $from`;
my $from_base=`basename $from`;

my $ret;
chomp $from_base;
chomp $from_dir;

# 前回の状態を読み込む
#### Read ###########################
my %btmp;
if ( -f $bdata ){
    %btmp=%{ retrieve(${bdata}) };
}
#####################################

?
# 転送予定のフォルダのリストを取る
my $ret=chdir("$from");
my %ftmp;
open (FNAME,qq(find . -type f -ls|));
while (<FNAME>){
    chomp;
    my $inode =(split(" ",$_))[0];
    my $filepath=(split(" ",$_))[10];
    my $dn=`dirname $filepath`;
    my $fn=`basename $filepath`;
    chomp($dn);
    chomp($fn);
    $ftmp{$filepath} = $inode;
}

# 接続
my $ftp= Net::FTP->new(qq($hostname), Debug => 0);
$ftp->login($user , $password );
$ftp->binary();
$ret=$ftp->cwd($to );
if($ret eq ''){
    print qq(cwd error \[$to\]\n);
    exit 1;
}

# ディレクトリをfindで検索してput先にmkdirする。
open (DIRNAME,qq(/usr/bin/find . -type d|));
while (<DIRNAME>){
    chomp;
    $ftp->mkdir($_);
}
close DIRNAME;

# ファイルをfindで検索してput先にputする。
open (FNAME,qq(/usr/bin/find . -type f -ls|));
while (<FNAME>){
    chomp;
    my $inode =(split(" ",$_))[0];
    my $filepath=(split(" ",$_))[10];
    # 前回と同じかどうかをチェック
    if ( $btmp{$filepath} eq $ftmp{$filepath} ){
        next;
    }
    my $dn=`dirname $filepath`;
    my $fn=`basename $filepath`;
    chomp($dn);
    chomp($fn);
    $ftp->cwd($dn);
    if($ret eq ''){
        print qq(cwd error \[$dn\]\n);
        exit 1;
    }
    print &getdate . qq( [put] $filepath\n);
    # エラーになることがある.
    for my $var (3,5,10,30,60){
        #ファイルが無くなることがある?
        if ( -f $filepath ){
            $ret=$ftp->put($filepath);
            if($ret eq ''){
                print qq(put error \[$filepath\] sleep $var\n);
                sleep $var;
            }else{
                if ( $var ne 3 ){
                    print qq(retry \[$filepath\] ok.\n);
                }
            last;
            }
        }else{
            print qq(not found \[$filepath\] skip.\n);
            delete $ftmp{$filepath};
            last;
        }
    }
    $ftp->cwd($to);
}
close FNAME;

#### Write ##########################
chdir $cwd;
nstore \%ftmp , $bdata;
#####################################

sub getdate{
my ($sec,$min,$hh,$dd,$mm,$yy,$weak,$yday,$opt) = localtime(time);
    #my $t="$yy/$mm/$dd $hh:$min:$sec";
    my $t=sprintf("%04d/%02d/%02d %02d:%02d:%02d",$yy+1900,$mm+1,$dd,$hh,$min,$sec);
    return $t;
}

1;

 

差分FTPを考える(7 – いろいろ不具合があったのでその話

スピードもアップして、これで!、と思ってテストしていたら色々問題がありました。
こういう問題を修正していくのも面白いものです。

連続ファイル転送ができなくなることがある

元々はワードプレスのバックアップのためにつくり始めたのですけど、
ワードプレスのプラグインやテーマのファイルってかなり多くのファイルからなっているようで、私の環境で5000以上ものファイルがあります。

これをラズパイ→ラズパイと言うようにローカルでテストした場合には問題なかったのですが、
ラズパイ→さくらインターネット(スタンダード)としてみた所、数百個ファイル転送が済んだところで転送が止まってしまう現象が発生しました。
リトライするとまた別のファイルでも止まってしまう減少が起きましたので、ファイルに起因する現象ではないようです。何か転送量のブロックでもしているのでしょうか?でも容量的には40MB程度ですのでそれほど問題だと思えませんが・・・。

止まってしまっても、エラー発生後にやり直すとあっさりつながるので、リトライするようにしました。

for my $var (3,5,10,30,60){
  # 転送処理
}

要素5個の配列で回しています。$varには順に3,5,10,30,60が入ります。
リトライを繰り返すほどなにかおかしいということですので、$var秒だけスリープします。

何回か試してみた所、1回リトライすればつながるようでしたけど・・・。

ファイルが無くなることがあった

ワードプレスのプラグインの中にはキャッシュコントロールするものがあります。
どうもこのファイルが自動削除される事があるようです。
それに、こんなにたくさんのファイルが有るとは思っていませんでしたので、こんなに時間がかかると思いませんでしたので、そこが盲点でした。ファイルリストをとったタイミングと実際にそのファイルが転送されるまでの間にタイムラグが結構あるので、変更がわりかしあるディレクトリが対象だった場合、実際の転送時には既にファイルが変更・削除されている可能性は割りとあると思います。これは考慮せねばなりませんね。

if ( -f $filepath ){
}

ここは単純に転送する直前にファイルが有るかどうかをチェックするようにしました。

リストをとったタイミングと実際の転送のタイムラグがあると動機にならないじゃないかという問題があります。この問題については、次回の転送時にファイルの変更を検出できるので良し、としました。また、これだけ頻繁に内容が変わるとなると完全なバックアップは無理ということですね。キャッシュのバックアップがそこまで厳密に必要とは思えませんので、動悸したいファイル群がほんとうに必要かどうかというのも重要なファクターになると思います。

削除されたファイルには非対応

この方式では削除されたファイルを転送先から取り除くことができません。
ファイルの存在は一意に決まるので、余計なファイルが有って何か不具合とはあまりないような気もしますが、あるような気もしますね・・・。

これについてはまた考えたいですね。

差分FTPを考える(6 – スピードアップ

十数個のファイルであれば問題ないのですが、数百もある場合cksumが結構負荷が高いので時間もかかります。
なんとか早くする方法を考えてみました。

findでファイル情報を取得できないだろうか

findでcksumをするオプションがあれば一番良いですが、そのようなオプションはないようでです。

-lsを使うとファイル情報も出してくれるようです。

-ls

結果をファイル詳細付きで表示する。”ls -dils”と同様な形式を指定する

試してみます。

$ find test -type f -ls
1979221 4 -rw-r--r-- 1 mlin mlin 2 9月 22 10:31 test/3/5/6/b.txt
1979223 4 -rw-r--r-- 1 mlin mlin 2 9月 22 10:33 test/3/5/d.txt
1979222 4 -rw-r--r-- 1 mlin mlin 2 9月 22 10:32 test/2/4/c.txt
1979220 4 -rw-r--r-- 1 mlin mlin 2 9月 22 10:31 test/a.txt
1979219 4 -rw-r--r-- 1 mlin mlin 2 9月 22 14:31 test/1/e.txt

一番左にチェックサムっぽい数値が出ています。これ実はiノードと言うものらしいです。

http://kazmax.zpp.jp/linux_beginner/inode.html

ノード(inode)番号とは、ファイルやディレクトリに割り振られる、重複しない番号の事をいいます。

全てのファイル、ディレクトリに対しiノード番号が割り振られます。

iノードは、iノード番号、UID、GID、パーミッション、ファイルサイズ、ファイル作成時間、更新日時、実際のデータの位置(ディスク上の物理的な場所)、そのファイル自身への参照数をデータとして管理しています。

ということで、ファイルが変化した?、というフラグに使うには十分そうです。

 

テストしてみます。

#!/usr/bin/perl
use strict;
use Storable qw(nstore retrieve);
use Cwd;
my $cwd = getcwd();

#----- conf --------
my $hdata='hdata.dat';
#-------------------
#### Read ###########################
my %htmp;
if ( -f $hdata ){
%htmp=%{ retrieve(${hdata}) };
}
#####################################

# testディレクトリに移る
chdir "test";

open (FNAME,qq(find . -type f -ls|));
while (<FNAME>){
    chomp;
    my $inode=(split(" ",$_))[0];
    my $filepath=(split(" ",$_))[10];

    my $dn=`dirname $filepath`;
    my $fn=`basename $filepath`;
    chomp($dn);
    chomp($fn);
    $htmp{$filepath} = qq($inode);
}
#### Write ##########################
chdir $cwd;
nstore \%htmp , $hdata;
#####################################

inodeが0番目、パスが10番目、であるので、splitで切り出しています。

いちいちチェックサムするよりもかなりの高速化が期待できそうです。

差分FTPを考える(5 – 組み込み

ここまで出来たら、もとのFTPプログラムに組み込みました。

#!/usr/bin/perl

use strict;
use Net::FTP;
use Storable qw(nstore retrieve);
use Cwd;
my $cwd = getcwd();

# 引数を読み取る
if ($#ARGV ne 4 ){
    print "引数が誤っています。\n";
    print "$0 [hostname] [user] [password] [fromdir] [todir]\n";
    exit 1;
}

my $hostname=$ARGV[0];
my $user =$ARGV[1];
my $password=$ARGV[2];
my $from =$ARGV[3];
my $to =$ARGV[4];

my $from_dir =`dirname $from`;
my $from_base=`basename $from`;
my $ret;
chomp $from_base;
chomp $from_dir;

# 前回の状態を読み込む
#----- conf --------
my $bdata='before.dat';
#-------------------
#### Read ###########################
my %btmp;
if ( -f $bdata ){
    %btmp=%{ retrieve(${bdata}) };
}
#####################################

# 転送予定のフォルダのリストを取る
my $ret=chdir("$from");
my %ftmp;
open (FNAME,qq(find . -type f|));
while (<FNAME>){
    chomp;
    my $dn=`dirname $_`;
    my $fn=`basename $_`;
    chomp($dn);
    chomp($fn);
    my $cksum=`cksum $_`;
    my @cksums=split( " " , $cksum );

    $ftmp{$_} = qq($cksums[0],$cksums[1]);
}

# 接続
my $ftp= Net::FTP->new(qq($hostname), Debug => 0);
$ftp->login($user , $password );
$ftp->binary();
$ret=$ftp->cwd($to );
if($ret eq ''){
    print qq(cwd error \[$to\]\n);
    exit 1;
}

# ディレクトリをfindで検索してput先にmkdirする。
open (DIRNAME,qq(/usr/bin/find . -type d|));
while (<DIRNAME>){
    chomp;
    $ftp->mkdir($_);
}
close DIRNAME;

# ファイルをfindで検索してput先にputする。
open (FNAME,qq(/usr/bin/find . -type f|));
while (<FNAME>){
    chomp;
    # 前回と同じかどうかをチェック
    if ( $btmp{$_} eq $ftmp{$_} ){
        next;
    }
    my $dn=`dirname $_`;
    my $fn=`basename $_`;
    chomp($dn);
    chomp($fn);
    $ftp->cwd($dn);
    if($ret eq ''){
        print qq(cwd error \[$dn\]\n);
        exit 1;
    }
    print &getdate . qq( [put] $_\n);
    $ret=$ftp->put($_);
    if($ret eq ''){
         print qq(put error \[$_\]\n);
        exit 1;
    }
    $ftp->cwd($to);
}
close FNAME;

#### Write ##########################
chdir $cwd;
nstore \%ftmp , $bdata;
#####################################

sub getdate{
    my ($sec,$min,$hh,$dd,$mm,$yy,$weak,$yday,$opt) = localtime(time);
    #my $t="$yy/$mm/$dd $hh:$min:$sec";
    my $t=sprintf("%04d/%02d/%02d %02d:%02d:%02d",$yy+1900,$mm+1,$dd,$hh,$min,$sec);
    return $t;
}

1;

ファイル情報はハッシュに詰めています。
%btmpが前回のファイル情報、%ftmpが現在のファイル情報です。
キーがファイルパスで、値にチェックサム値を入れています。
%btmpはbefore.datから読み込んで、全てが正常に終わったら%ftmpに上書きしています。
こうして常に前回の情報が保存されるようにしています。

ファイルの変化を確かめるためにcksumコマンドで確認しています。

$ cksum test.dat
3517503372 127 test.dat

チェックサム値 ファイルサイズ ファイル名

という表記になっています。
チェックサム値だけでも良いのですが、”3517503372,127″のようにカンマでつなげて精度を高めています。
とは言え、実際には単にファイルが変わったかどうかを知りたいだけなので、チェックサム値だけで十分だと思います。

差分FTPを考える(4 – 前回のファイル情報をstoreableで保存する

一気にうまくやろうとするのは危険です。
というわけで、まずはあるディレクトリのファイルを取得して、その情報をハッシュにする方法を確立しましょう。

サンプルディレクトリを作る

とりあえずtestというディレクトリを作ってその中に適当にファイルを作ります。

  • test/1/a.txt
  • test/2/b.txt
  • test/3/c.txt

私はこんなふうに作ってみましたよ。もちろんもっと深くしたりするほうが有効です。

このディレクトリをどうやって検索するのかというと、
unixのコマンド’find’を使ってしまうのが手っ取り早いです。

open (FNAME,qq(find . -type f|));
while (<FNAME>){
    chomp;
    print qq($_\n) unless($_ eq '.');
}

perlからunixコマンドを呼ぶ方法は色々ありますが、
複数行の結果を1行ずつ処理するためにはopenを使うのが便利です。
chompしているのは改行が入ってしまうからです。除去しています。

これを実行してみると、きれいにパス付きでリストが取れることがわかると思います。

ハッシュにしてファイルに出してみる

とりあえず、
ハッシュ{’パス’}=’チェックサム,ファイルサイズ’
というハッシュをhdata.datというファイルに保存する簡単なプログラムを書いてみました。
チェックサムの結果をsplitしているのは結果が
4156328750 1577 ファイル名
のように出るからです。1番目がチェックサム値、2番めがファイルサイズです。
それをカンマ区切りでセットしています。

#!/usr/bin/perl
use strict;
use Storable qw(nstore retrieve);
use Cwd;
my $cwd = getcwd();

#----- conf --------
my $hdata='hdata.dat';
#-------------------
#### Read ###########################
my %htmp;
if ( -f $hdata ){
    %htmp=%{ retrieve(${hdata}) };
}
#####################################

# testディレクトリに移る
chdir "test";

open (FNAME,qq(find . -type f|));
while (<FNAME>){
    chomp;
    my $cksum=`cksum $_`;
    my @cksums=split( " " , $cksum );

    print qq($_\t$cksums[0],$cksums[1]\n);
    $htmp{$_} = qq($cksums[0],$cksums[1]);
}
#### Write ##########################
chdir $cwd;
nstore \%htmp , $hdata;
#####################################

実行すると hdata.dat というファイルが出来ます。
次はこのファイルがちゃんと出来ているかを確認します。

#!/usr/bin/perl
use strict;
use Storable qw(nstore retrieve);

#----- conf --------
my $hdata='hdata.dat';
#-------------------
#### Read ###########################
my %htmp;
if ( -f $hdata ){
    %htmp=%{ retrieve(${hdata}) };
}
#####################################



while( my($key , $value) = each %htmp ){
    print qq($key = $value\n);
}

保存する必要が無いので、開いているだけです。

実行すると以下のように出ます。

./3/c.txt = 4294967295,0
./2/b.txt = 2410625446,123
./1/a.txt = 4294967295,0

どうやらうまく出来ているようですね。

差分FTPを考える(3 – 前回のディレクトリ状態を保存する方法

基本的には毎回ファイルリストを取って保存しておき、
前回にとったファイルリストのうち

  • 変更されたファイル
  • 新しく追加されたファイル

がわかればいいということになります。
単純にファイルリストを作っておいて単純に突き合わせをする方法もありますが、
Perlでやるのならもっといいやり方があります。

DBM?

パスに対するファイルは一意で決まるので、パスをキーにして、値をcksumの値を入れたハッシュとしてとっておけば比較が楽そうです。
このようなハッシュ値を取っておくにはDBMが便利です。

dbmopen %HOGE, "db" ,0644 or die "dbm open error";
~
~
dbmclose %HOGE;

この%HOGEがハッシュとして使え、ファイルとして吐き出します。
そのため、次回起動時にも保存できるというわけです。
この機構にはロック機構がありません。なので自前でロックする仕組みをつけたほうが無難です。

簡易的なDBですので、変更を破棄することが出来ません。この点が少し使いづらいな、と思っていました。

Storeableを使う

実は非常にこのDBMと似ているのですが、もう少し使いやすいStoreableというのがあります。

use Storable qw(nstore retrieve);
#### Read
@atmp=@{ retrieve("adata.dat") };
%htmp=%{ retrieve("hdata.dat") };

#
# 処理
#
#### Write
nstore \@atmp , $adata;
nstore \%htmp , $hdata;

変数の内容をファイルから開くのがReadで、書き込むのがWriteです。
これが特徴的なのは、配列や変数でも保存できることです。
上の例は配列とハッシュの例を書きましたが、変数でも可能です。$に変更するだけです。
それと、オープン・クローズ、の概念がないので、扱いが楽です。
変数の内容を変更しても、nstoreを実行しない限りは保存されているファイルはそのままです。
なので、変更結果を繁栄したくない場合はnstoreを実行しなければいいだけです。

次回は具体的にこのStoreableを使って見たいと思います。

差分FTPを考える(2

まずは以前作ったディレクトリFTPのPerlスクリプトです。

 

#!/usr/local/bin/perl

#use strict;
use Net::FTP;


# 引数を読み取る
if ($#ARGV ne 4 ){
    print "引数が誤っています。\n";
    print "$0 [hostname] [user] [password] [fromdir] [todir]\n";
    exit 1;
}

my $hostname=$ARGV[0];
my $user    =$ARGV[1];
my $password=$ARGV[2];
my $from    =$ARGV[3];
my $to      =$ARGV[4];

my $from_dir =`dirname $from`;
my $from_base=`basename $from`;

my $ret;

chomp $from_base;
chomp $from_dir;

my $ret=chdir("$from");
system("pwd");

# 接続
my $ftp= Net::FTP->new(qq($hostname), Debug => 0);
   $ftp->login($user , $password );
   $ftp->binary();
   $ret=$ftp->cwd($to );
   if($ret eq ''){
     print qq(cwd error \[$to\]\n);
     exit 1;
   }
print qq($ret : $from\n);

# ディレクトリをfindで検索してput先にmkdirする。
print qq(/usr/bin/find . -type d\n);
open (DIRNAME,qq(/usr/bin/find . -type d|));
while (){
  chomp;
  print qq($_\n) unless($_ eq '.');
  $ftp->mkdir($_);
}
close DIRNAME;

# ファイルをfindで検索してput先にputする。
open (FNAME,qq(/usr/bin/find . -type f|));
while (){
  chomp;
  print qq($_\n) unless($_ eq '.');
  my $dn=`dirname $_`;
  my $fn=`basename $_`;
  chomp($dn);
  chomp($fn);
 print qq(DIR $dn\nFILE $fn\n);
 $ftp->cwd($dn);
 if($ret eq ''){
   print qq(cwd error \[$dn\]\n);
   exit 1;
 }
 $ret=$ftp->put($_);
 if($ret eq ''){
   print qq(put error \[$_\]\n);
   exit 1;
 }
 $ftp->cwd($to);
}
close FNAME;

1;

perlスクリプトではありますが、
Linuxコマンドをいくつか使っているので、Windowsでは動作しません。
今見るとfindをフルパスで指定していたり、駄目ですね^^;
なぜわざわざfindを使っているかというと、ファイルのフルパスでの取得が綺麗にできるからです。
このスクリプトの欠点は、前にもいいましたが、すべてのファイルを転送してしまう点です。
これをファイルの変更がなかったら何も転送しないように修正していこうと思います。

続きます。

差分FTPを考える

WordPress(ワードプレス)のバックアップはデータベースのバックアップだけでは足りません。写真ファイルやプラグインもバックアップしたいところです。

さて、通常日次的なデータを他のサーバーにバックアップする場合、
tarでアーカイブして転送する、という方法が一般的です。

ところがこの方式はtarアーカイブするためのディスク容量とIOのリソースが必要です。
これが意外に馬鹿にならないです。

これを解決するためには、アーカイブせずにあるディレクトリ配下を全て転送することです。

しかしながらデフォルトのFTPコマンドでは*(アスタリスク)は使えませんし、ディレクトリの転送にも対応していません。

NCFTPコマンドを使えばディレクトリをそのまま転送することができます。

> put -R bar

試しにやってみましたが、とても便利だと感じました。

ところが、NCFTPで日次のバックアップをしようと考えた時、イマイチです。
なぜかといいますと、全てのファイルを転送してしまうからです。
出来たら、とあるディレクトリ配下のその日に更新されたファイルのみの転送をしたいところですよね。更新されたファイルがない日は何もしない、というのが理想です。

差分FTPを考える

実は昔一度作ったはずなのですが、紛失・・・。
というわけで作成したいと思います。

  1. 指定したディレクトリ配下のファイルリストを作る
  2. ファイルはcksumコマンドの実行結果を付与
  3. 前回のファイルリストと今回のフィアルリストを比べる
  4. ディレクトリリストを取得
  5. ディレクトリのみを送信先に作成する
  6. 変化のあるファイルのみを転送する

こんな動きでいいでしょう。

前回のファイルが存在しない場合は全てのファイルの転送となります。
また、日次実行前に削除されたファイルは転送先には残ってしまいます。が、これは仕様とします。

次回に続きます。

ラジコの録音、東京の放送を取得する一案(9 WEBアプリ

WEBアプリはPerlで簡単に組みました。
単に、mp3フォルダの中身をテーブル組にするだけのものです。
やってみるとわかりますが、ラジオって毎日だったりするのでどんどん溜まっていきます 汗
その為、デリートボタンを各録音ファイルの横に表示するようにしています。
クリックするとunlink(削除)します。削除をCGIから実行するのは結構危険なので、dustとか言うフォルダを作っておいて単に移動させるようにしても良いかもしれません。
実際の削除は時々ログインして自分で消すようにするのです。

#!/usr/bin/perl
use strict;
use CGI;
use Storable qw(nstore retrieve);

my $debug;

my $q=new CGI;
my @param=$q->param;
my %HASH;
for (@param){
$HASH{$_}=$q->param($_);
    $debug.=qq($_ , $HASH{$_} | );
}

my $mes;
if ( -f "./mp3/" . $HASH{'del'} ){
    unlink "./mp3/$HASH{'del'}";
    $mes=qq(./mp3/$HASH{'del'} を削除しました。);
}elsif( $HASH{'del'} ne "" ){
    $mes=qq($HASH{'del'} がありません。);
}

opendir DIR, "mp3";
my @files =
reverse( grep ( /mp3|aac/, readdir DIR ));
close DIR;
#フィルタ
if ( $HASH{'a'} ne "" ){
    @files = grep /$HASH{'a'}/i , @files;
}
my $src;
for my $var (@files){
    $src.=qq(<tr><td><a href="./mp3/$var">$var</a></td><td><button onclick='test("$var")' style="WIDTH:50px; HEIGHT: 20px">del</button></td></tr>\n);
}

if ( -d "debug.flg" ){
    $debug=$debug . "\n<hr>\n";
}else{
    $debug="";
}
print "Content-type: text/html\n\n";
print <<HTML;
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <title></title>
<script type="text/javascript">
<!--
function test(msg){
  var element = document.getElementById("1");
  element.value = msg;
}
function ReloadAddr(){
  window.location.reload(); //ページをリロード
}
-->
</script>
</head>
<body>$debug
$mes
<form action="rj.cgi" method="post">
  <input type="text" name="del" size="30" id="1"><input type="submit" value="削除">
  <button onclick='ReloadAddr' style="WIDTH:50px; HEIGHT: 20px">reload</button>
  <a href="#endjump">一番下へ</a>
</form>
<table border="1" cellpadding="0">
$src
</table>
<form action="rj.cgi" method="get">
  <input type="text" name="a" size="30" value=$HASH{'a'}>
  <input type="submit" value="フィルタ"><a name="endjump"><a href="#top">TOP.</a></a>
  <input type="reset" value="riset">
</form>
</body>
</html>
HTML