• ベストアンサー

想定通りに動かずループしてしまうのは何故?

benderの回答

  • bender
  • ベストアンサー率45% (108/236)
回答No.2

else は必要です。 else が無い場合、関数 rdir が呼ばれると、必ず一回 "print @data" を実行することがわかります。そこで、このプログラムを実行して「すべてのディレクトリを一回再帰的に探査」するとき、この関数が再帰的に呼ばれた回数だけ、この print 文が実行されなくてはならないことになります。 というわけで、このプログラムは、一回だけ「最後に@dataを出力」するプログラムではないことがわかります。 ところで、探索するディレクトリの一つが、例えば、親ディレクトリへの symbolic link である場合、このプログラムはとまらなくなってしまうと思います。

tochanx
質問者

補足

回答ありがとうございます。 シンボリックとは思いもよりませんでした。確かにこのままではまずいですね。 指摘して下さりありがとうございます。 シンボリックリンクについてはファイルテスト演算子の-lを利用して判別することにしました。 ただ、やはりどうしてelseが必要なのかよくわかりません。 最後のprint @dataのところに処理がくる前にrdir()を呼んでいるのだから、 printはされずに次のrdirの中に処理が移るはずで、printがされるのは @list(発見されたディレクトリのリスト)が空の状態、 つまり再起的に探索し終わったときになると思っているのですが、しかしそうはならないようです。 どうしてなのでしょうか? すみません。

関連するQ&A

  • 集計プログラム

    sub fanc{ my $dir = "ディレクトリ";my @array = @_; my $n = $array[0];my $id = $array[1]; my @csv1 = ('AAA.csv','BBB.csv','CCC.csv','DDD.csv',EEE.csv'); my @csv2 = ('FFF.csv','GGG.csv','HHH.csv','III.csv',JJJ.csv'); my @csv3 = ('KKK.csv','LLL.csv','MMM.csv','NNN.csv','OOO.csv'); opendir(DIR, $dir) or exit; @pairs = grep { -f "$dir/$_" ? $_ : '' } readdir(DIR); close(DIR); if($id == 1){ $pattern = $csv1[$n -1]; } elsif($id == 2){ $pattern = $csv2[$n -1]; } elsif($id == 3){ $pattern = $csv3[$n -1]; } }else{ undef; } @files = grep(/$pattern/, @pairs); until(@files == 0){ my $data = shift @files; open(IN, $data) or exit; @file = <IN>;chomp @file; close(IN); foreach my $i (@file){ my @m = split(/,/, $i); push my @rec1, $m[1]; push my @rec2, $m[2]; } $val1 += $rec2[0]; shift @rec2; $val2 += $rec2[0]; shift @rec2; $val3 += $rec2[0]; shift @rec2; $val4 += $rec2[0]; shift @rec2; $val5 += $rec2[0]; shift @rec2; $val6 += $rec2[0]; shift @rec2; $val7 += $rec2[0]; shift @rec2; $val8 += $rec2[0]; shift @rec2; $val9 += $rec2[0]; shift @rec2; $val10 += $rec2[0]; shift @rec2; } %tmp; my @rec = grep( !$tmp{$_}++, @rec1 ); foreach my $x (@rec){$sum1 += $x;} $res1 = $val1 / $sum1 * 100; $res2 = $val2 / $sum1 * 100; $res3 = $val3 / $sum1 * 100; ・ ・ こんな感じでパーセンテージを出していますが、csvのデータは全て違うのに、引数を変えて並べて実行すると 答えが同じように返ってきます。どこかおかしいのでしょうか?

    • ベストアンサー
    • Perl
  • perl サブルーチンでのファイル出力結果おかしい

    以下のコードを実行するとカレントディレクトリの配下にある すべてのファイルのリストがコンソールとファイルに出力される はずですが、コンソールに表示されているファイルの一部しか ファイルに出力されていません。 どうも、最後に do_file()を呼び出したときのファイルしか リストされていないようなのですがなぜでしょうか。 どのようにすればよいのでしょうか。 よろしくお願いします。 (Windows7, ActivePerl(v5.16.3)) ----test.pl--------------------------------------------- &do_dir('.'); sub do_dir{  open(FILE2,'>list.txt') or die "$!";  my $dirname=shift;  my $delim='/';  opendir(DIR,$dirname) or die "$!";  foreach $entry (readdir(DIR)){   next if($entry eq '.');   next if($entry eq '..');   if ($dirname=~/[\\\/]$/) {    my $delim='';   }   my $filename="$dirname$delim$entry";   if(-d $filename){    &do_dir($filename);   } else {    &do_file($filename);   }  }  close(DIR);  close(FILE2); } sub do_file{  my $filename=shift;  return unless ($filename=~/\.*$/);  print "$filename\n";  print FILE2 "$filename\n"; }

    • ベストアンサー
    • Perl
  • perl ディレクトリ ツリー表示

    下記ソースコードでカレントディレクトリのツリー表示ができますが、これを特定のディレクトリ内をツリー表示できるようにする方法がわかりません。 opendir関数の$dirの前にtestをつけましたが、うまく行きません。 特定のディレクトリは任意で変えることができることとします。 sub treelist_ { my ($dir, $lv) = @_; opendir my $dh, $dir; my @files = grep { $_ !~ /^\.\.?$/; } readdir $dh; closedir $dh; for my $file (@files) { print ">" x $lv, "$dir/$file\n"; treelist_("$dir/$file", $lv+1) if -d "$dir/$file"; } } sub tree { for my $dir (@_) { print "$dir\n"; treelist_($dir, 0); } } tree('.');

  • Windows漢字フォルダ名の扱い(chdir編)

    先回、次の質問をさせて頂いた者です。http://oshiete1.goo.ne.jp/qa2230450.html 今回、同様な環境にて、chdir()実行しようとすると、 D:\temp ├─フォルダ名の末尾に機能 ├─フォルダ名の末尾に表 ├─途中に機能がある └─途中に表がある スクリプト内では、次のように書いています(サブルーチン部) sub dir_recurs { my $sdir = @_; my ($d,$rc); my $cwd = Cwd::getcwd(); opendir(DIR,$sdir) || die "$cwd/$sdir") . " $! stopped"; my @dirs=(); my @files=(); foreach(sort readdir(DIR)){ if( -d "$_/" ){ next if(/^\.+/); # '.' '..' はパス push(@dirs,"$_"); }elsif( -f "$_" ){ push(@files,$_); # ディレクトリ以外有り }else{ print "ERROR: '$_' in $cwd.\n"; } } closedir(DIR); #----- 下位ディレクトリへ潜る if(@dirs > 0){ foreach $d (@dirs){ if(!chdir("$d/")){ print "ERROR: chdir($d) from $cwd\n"; next; } $rc = &dir_recurs("."); chdir(".."); } } : 前後関係は、はしょりますが、この処理部が走っているときに、 ERROR: 'temp.txt' in D:\temp\フォルダ名の末尾に機・ ERROR: chdir(フォルダ名の末尾に表) from 'D:\temp' ERROR: chdir(途中に機能がある) from 'D:\temp' ERROR: chdir(途中に表がある) from 'D:\temp' のような結果になってしまいます。 一番先頭でのエラーメッセージを見る限りは、 一度は、「D:\temp」の下の「フォルダ名の末尾に機能」ディレクトリに 潜ることを成功しているようなのですが、次回以降に失敗しています。 この症状について、理由等、お分かりの方、よろしくお願いいたします。

  • フォルダ関連のライブラリについてアドバイスしてもらえないでしょうか?

    フォルダ関連のライブラリについてアドバイスしてもらえないでしょうか? phpのフォルダ関連の操作がそのままだと少し面倒な感じがしたので 自分でライブラリのようなものを作りたいと思いました。 ------------------------------------------------------------------------------------------ calss My_Directory { const SIZE_FORMAT_B = 0; const SIZE_FORMAT_KB = 1; const SIZE_FORMAT_MB = 2; const SIZE_FORMAT_GB = 3; // フォルダへのパスを指定してオブジェクトを作成。 __construct($dir_path); // フォルダのサイズを取得。$recursiveがtrueで再帰的に全階層のファイルを調べる。 getSize($recursive = true, $sizeFormat = self::SIZE_FORMAT_B, $round = 2); // フォルダの削除。中に入っているファイルも削除される。 delete(); // フォルダに入っているファイル名を取得。$recursiveがtrueで再帰的に全階層のファイル名を取得。 // その場合、配列の配列形式にする。 getFileNames($recursive = true); } $path = 'C:\myfolder';// ローカル環境 $dir_obj = new My_Directory($path); echo $dir_obj->getSize(true, My_Directory::SIZE_FORMAT_MB, 1).'MB';// 例えば、「22.3MB」と表示される print_r($dir_obj->getFileNames()); /*例えば、 Array ( [0] => file_1 [1] => file_2 [2] => file_3 [sub_dir] => Array ( [0] => sub_dir_file_1 [1] => sub_dir_file_2 [sub_sub_dir] => Array ( [0] => sub_sub_dir_file_1 ) ) ) でも結局foreachのループで再帰的に調べることになるから、やっぱりめんどうだなぁ・・・ */ $dir_obj->delete();// myfolderが削除される ------------------------------------------------------------------------------------------ ただ、deleteした後にgetSizeとか実行した場合に、 例外を投げればいいのかなぁと考えたりしたのですが、 そもそもオブジェクトにして扱うこと自体が適切なのかどうか? だからフォルダ関連のライブラリがあまり存在しないのかなと思ったりしたのですが、 今回のようにライブラリを作ることに対してのアドバイス(自分ならこうするとか、ここはそんな風にやらないとか) など些細なこともでいいので教えてもらいたいです。

    • ベストアンサー
    • PHP
  • 関数の中から別の関数内のforeachの実行結果を得られない

    お世話になります。 環境 perl 5.8.0 RHLinux9 以下のようにsub _Htmlからsub _List1を呼び出す場合は正常にリスト表示できるのですが、sub _List2を呼び出した場合、 Content-Type: text/html <HTML lang=ja > <HEAD>...として表示されてしまいます。 sub _List1{ @list = (0, 1, 2, 3, 4, 5); foreach (@list) { print "$_\n"; } } sub _List2{ $a1 = "./dir/data.dat"; $a2 = "./dir/data.dat"; $a3 = "./dir/data.dat"; &FileOpen('FILE1',"$a1"); &FileOpen('FILE2',"$a2"); &FileOpen('FILE3',"$a3"); @DATA = <FILE1>; push @DATA, <FILE2>,<FILE3>; close(FILE1); close(FILE2); close(FILE3); foreach (@DATA) { ($key,$val)=split(/=/,$_); print "$key=$val"; $StartNum++; } } sub _Html{ print <<EOM; <table> <tr><td> .. EOM &_List(''); print <<EOM; <tr><td> .. </table> EOM } sub _List2の内容を、CGIからhtmlを表示する際のスキン.htmlファイル内に下記のように記述した場合も 正常にリスト表示されます。 --スキン.html------ print <<"<!---HTML--->"; <HTML lang=ja > <HEAD>... <!---HTML---> $a1 = "./dir/data.dat"; $a2 = "./dir/data.dat"; $a3 = "./dir/data.dat"; &FileOpen('FILE1',"$a1"); &FileOpen('FILE2',"$a2"); &FileOpen('FILE3',"$a3"); @DATA = <FILE1>; push @DATA, <FILE2>,<FILE3>; close(FILE1); close(FILE2); close(FILE3); foreach (@DATA) { ($key,$val)=split(/=/,$_); print "$key=$val"; $StartNum++; } } print <<"<!---HTML--->"; ... </BODY></HTML> <!---HTML---> 1; ---

    • ベストアンサー
    • Perl
  • perl-cgiのリネームについて

    Perl-CGIで ABCという、ディレクトリの中のファイルの名前を、ランダムな名前に変換したいのですがうまくいきません。 これを動作させるたびになぜか、どんどんファイルが減っていってしまいます。 どなたか、教えていただけないでしょうか? 宜しくお願い致します。 #!/usr/local/bin/perl print "Content-Type: text/plain\n\n"; $| = 1; my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime(time); my ($nowtime) = sprintf("%02d_%02d_%02d_%02d_%02d_",$year+1900,$mon+1,$mday,$hour,$min,$sec); #ディレクトリのファイル個数を記録する $dir = "./ABC/"; # ← ディレクトリを変数にセットする opendir DIR, $dir; @files = grep { !m/^(\.|\.\.)$/g } readdir DIR; # ← 「.」 「..」 以外のファイルを取得 close DIR; srand; for (my $i = @files; --$i; ) { my $j = int rand ($i + 1); next if $i == $j; @files[$i, $j] = @files[$j, $i]; } $num = 0; use File::Copy; foreach(@files){ $getpath = "$dir"."$_"; if( copy($getpath, "$dir".$nowtime.$num++.'.dat') eq 1){ $num++; unlink($getpath);}else{print "Copy Error"; exit;} }

    • ベストアンサー
    • CGI
  • perl 曜日を入れる

    perl初心者です。試行錯誤の連続です・・・ このように ↓ ( )と曜日を追加するにはどうしたら良いでしょう?。 2008/07/05(土) また、変なところもありましたら指摘してください <(_ _)> #-------------------------------------- # 時間 $time = time(); # 範囲日 $day1 = day0($time + (86400 * 2)); $day2 = day0($time + (86400 * 3)); $day3 = day0($time + (86400 * 4)); ### @weeeeek = ('$day1','$day2','$day3','$day4','$day5','$day6'); # 日計算 sub day0{ my $time = shift || time(); my $day0 = $time + (24 * 60 * 60); my ($yyyy, $mm, $dd) = (localtime($day0))[5,4,3]; $yyyy += 1900; $mm += 1; return( sprintf('%4d/%2d/%2d', $yyyy, $mm, $dd) ); } #-------------------------------------- ### @list = ('(日)','(月)','火','水','木','金','土'); ### ( "$mm/$dd + "(" + list + ")" );

    • ベストアンサー
    • Perl
  • cshの条件式について教えてください

    cshで該当ディレクトリにファイルがなければOK、あればエラーというような処理をしようと思っています。 しかし、下記のような記述ではうまくできません。 1) *************************** if ( -e $DIR/*.* ) then echo "NG" exit 1 else echo "OK" endif exit 0 *************************** if文でなく、外にだせばうまくいくのですが... 2) *************************** test -e $DIR/*.* if($status == 0) then echo "NG" exit 1 else echo "OK" endif exit 0 **************************** if ( test -e $DIR/*.* ) then... でもだめでした。 どうにかしてif文で一発判定をしたいと思っているのですが、正しい記述方法を教えてください。 よろしくお願いします。

  • サブルーチンの結果

    my @data; my ($rows,$cols); sub Gettest { use Text::ParseWords; my $dfile = shift; # CSVファイル my @array = @_; @data = (); open(IN, $dfile) or exit(-1);# while(<IN>) { chomp; my @fields = quotewords("," => 0 , $_); # カンマデータの取込 foreach my $field (@fields){ if(index($field, ":") >= 0) { my @range = split(':',$field);# 範囲の取出し $field = sub { my $v = shift; return $range[0] <= $v && $v <= $range[1];}; } elsif(index($field, ",") >= 0) { my @list = split(',',$field); #種類の取出し $field = sub { my $v = shift; return grep($v == $_, @list); };}} push @data, [@fields];} close(IN); $rows = @data; $cols = @{$data[0]}; return squeezed(@array);#// 該当範囲の絞り込み} sub squeezed { my @para = @_; my @pos = (0 .. ($cols -1)); my $i; my @wk; for($i = 0; $i < $rows -1; $i++) { @wk = (); foreach my $p (@pos) {# 有効な位置 my $test = $data[$i]->[$p]; if("CODE" eq ref($test)){ # 範囲テストコードの場合 push @wk, $p if &$test($para[$i]); # test がOK } elsif($para[$i] eq /$test/) {push @wk, $p; # マッチ位置を配列に}} @pos = @wk;} if(@pos == 1){ return $data[-1]->[$pos[0]]; } else {return undef;#// 該当なしか2個以上ならundefを返却 }}1; 引数によってCSVデータの範囲を絞って結果を返すといった関数を、ご提供して頂いた のですが、「1:4」や「1,3,4」等の答えがCODE(XXX)になってしまいます。ご提供者様から、ループで変換するのではなく 最終行のみ変更を加えないようにするというアドバイスを頂いて色々ためしてみたのですが、 私のレベルではサブルーチンの理解が出来なくて全然うまくいきません。他力本願な お願いで申し訳ないのですが、解る方教えて下さい。

    • ベストアンサー
    • Perl