Chinaunix

标题: opendir和rename的bug [打印本页]

作者: davexu2    时间: 2014-05-19 18:03
标题: opendir和rename的bug
我有一个文件,名字是name.txt,里面有2列,\t分隔,如下:
AM56-4TS        0508-65
AM56-5TS        0508-66
AM56-6TS        0508-67
AM56-7TS        0508-68
我想通过这个,找到对应的名字,并改成后者,像下面这样,第一列是原始文件,第二类是要改成名字的文件,对应name.txt的第一行。
0508-65__Sg-F_A14050_G04_1405170031Y.ab1    AM56-4TS.ab1

脚本源代码如下:
#!usr/bin/perl -w
use strict;

open FILE, "< name.txt";
my %hash;
while (<FILE>) {
        chomp;
    my @a = split;
        $hash{$a[1]} = $a[0];
}
close FILE;
my $dirname = "ab1-1/";
opendir (DIR,$dirname) || die "Error in opening dir $dirname\n";
while (my $filename = readdir (DIR)){
        next unless $filename=~/\.ab1/;
        my @b = split/__/,$filename;
        if (exists $hash{$b[0]}){
                my $newname = "$hash{$b[0]}".".ab1";
                rename ( "$dirname/$filename", "$dirname/$newname");
        }else{
                print "$filename\n";
        }
}
close DIR;
print "$total\n";
运行以后,我发现,所有文件名字都被改对了,但是有如下信息输出到屏幕上:
OF07-11.ab1
OF08-6.ab1
OF07-3.ab1

这不正常啊,因为这是更改名字之后的文件,而我想输出的是没改名字的文件,至少也是原来的文件名字。求大神解惑!

作者: Monox    时间: 2014-05-19 19:27
你不是说opendir和rename的bug吗?那你应该给perl 5的维护者发邮件而不是应该在这里发帖。
不过,如果我是perl 5的维护者的话我会reject你的bug report就是了。而解释你说的现象我建议你盯着你写的代码的if else看三遍,如果看三遍还认为是opendir和rename的bug,不认为是自己的问题的话,建议你再盯着看三遍,如果仍然不认为自己有错的话,我想在我给你指出错误在哪之前就已经有很多人帮你指出了。
作者: doiob    时间: 2014-05-19 22:40
这真是你自己写的?你输出的是你那文件里没有而目录里有的文件。
作者: davexu2    时间: 2014-05-21 16:46
想听听高人怎么解惑呢。回复 2# Monox


   
作者: davexu2    时间: 2014-05-21 16:48
是的,是我写的,就是想确保每个文件都被改对了名字回复 3# doiob


   
作者: davexu2    时间: 2014-05-21 16:53
输出那三个在原文件列表的第二列,而原文见名字里面是没有那三个的。回复 3# doiob


   
作者: Monox    时间: 2014-05-21 19:07
回复 4# davexu2

既然我建议你盯着if else看三到六遍,我就从if else说起吧。
我都建议你盯着它看三到六遍了,看来你应该不理解什么是if else吧(很多时候我们自以为是的我们理解了什么,但是事实是我们常常什么都没有理解)。
    if else 的意思是要么这样,要么那样。不会是你强烈突显出来的至少也可能会是既这样也那样的逻辑。既然if else是要么这样,要么那样,但是绝不会是既这样又那样,那么,对于一个文件名,它当然要么改名成功,要么输出这个文件名,永远也不可能既改名成功又输出那个文件名。所以对于你推论的
这不正常啊,因为这是更改名字之后的文件,而我想输出的是没改名字的文件,至少也是原来的文件名字
这是绝不可能的,除了你比较挑衅的标题以外,你还特意很有把握的加了一个“至少”来加重你的语句,所以才会出现二楼我那样的回复方式,如果你的标题和语气换一下,我二楼的回复方式肯定也会换,很有可能就没有后续回复的必要了。

好,前面那一段是用来攻击你的“至少”两字这个言论的。接下来说说为什么
这不正常啊,因为这是更改名字之后的文件,而我想输出的是没改名字的文件

计算机很终实的,你让它怎样做它就怎样做,不会自作聪明,所以它给你的反应是很正常的,并不是不正常的。而另一方面,之所以会[quote]因为这是更改名字之后的文件,而我想输出的是没改名字的文件[/code]是因为你又没有没改名字的文件,当然没有输出了。那输出的是什么呢?它们还真是没改名字的文件。是的,这里我没打错字。那个目录下所有的文件都被你改名了,但是改名后的文件也在那个目录下,那些文件会被当作新的需要改名字的文件进行处理,而根据你的改名列表,那些新需要处理的文件并不会(再)被改名一次,因此就成了(新的)没改名字的文件。

至于导致上面这种现象的原因并不是opendir的bug,也不是rename的bug,问题的原因在于你不理解函数调用和循环是这样实际工作的。关于此点我不想继续扩展了,我直接给建议吧,可能你可以从我给的建议要自己体会出我这里所说的工作原理,你不理解的话也无所谓,因为反正我肯定是不解释的,其他人可能会替你解释了,那是他们的自由,我是觉得有很多东西是必须得自己去体会去领悟才会有成长的。

所以,要达到你真正的目的,你有两条路可走,一种是rename的时候,源目录和目标目录不要设成一样的,第二种是不要在while里readdir,而又在同一个while里改变这个dir里的内容(是不是觉得和小骆驼里说的在foreach里改变foreach列表的值那句话很像),你可以先把readdir的内容全存起来,比如存在一个数组里,然后对这个数组里的文件名做循环,循环的时候就是该改名的改名,该输出的输出了。
作者: davexu2    时间: 2014-05-21 20:50
你说的这个情况很可能啊,不过为什么值单单输出那3个呢,我删了3个文件的任意一个,最终的结果是输出后另外2个,这是为什么?100个文件,算上 . 和 .. ,加上那三个,while循环一共读了105个文件,按照你说的,如果循环进行下去,为什么仅仅多读了3个,而且是特定的3个?看你好像懂得很多的样子,也谢谢你的建议,但是那没用啊,我又没改错文件,而且用glob 或者其他方式改就不会出现这个问题,谁都知道,我更想知道发生着个问题的原因。如果你是真大神,能不能解惑呢?回复 7# Monox


   
作者: Monox    时间: 2014-05-21 22:26
davexu2 发表于 2014-05-21 20:50
你说的这个情况很可能啊,不过为什么值单单输出那3个呢,我删了3个文件的任意一个,最终的结果是输出后另外2个,这是为什么?100个文件,算上 . 和 .. ,加上那三个,while循环一共读了105个文件,按照你说的,如果循环进行下去,为什么仅仅多读了3个,而且是特定的3个?看你好像懂得很多的样子,也谢谢你的建议,但是那没用啊,我又没改错文件,而且用glob 或者其他方式改就不会出现这个问题,谁都知道,我更想知道发生着个问题的原因。如果你是真大神,能不能解惑呢?

是啊,“谁都知道”,就我不知道,而且我从头到尾都没说过我是大神,就像我不喜欢你的标题和描述方式一样,你不喜欢我相对应的回复这很正常。就连之前的回复我也是出于自愿,因为没有义务,我完全可以不了你。明显你没有真想让我“解惑”的诚意,我也就没必要回答你的问题。不光我不会再回复这个帖子,以后所有你的帖子我都不会回复。
作者: rubyish    时间: 2014-05-22 06:03
本帖最后由 rubyish 于 2014-05-22 02:41 编辑

1: 3楼 doiob 解惑了。
2: Monox 建议你盯着你写的代码的 if else 看三遍
3: 建议你盯着 3楼 看三遍
作者: davexu2    时间: 2014-05-22 09:06
真心不懂,能详细点不?回复 10# rubyish


   
作者: davexu2    时间: 2014-05-22 09:17
本来嘛,这个社区就是大家互相帮助,互相解决疑问的地方,大家有不懂互相解答,互相学习。看你的回复,似乎什么都懂一样,可惜,10句话里的回复仅有2句是有用的,一到关键的地方就避而不谈,啧啧,装的什么都懂,其实会的东西有限得很啊。还说什么不回复我的帖子,好像我求着你回复一样,不就是找个借口逃避你的无知和狂妄嘛。的确,这种人多了去了,多你一个不多,少你一个不少,不回复正好让我不浪费时间看你的帖子。加油,争取在您的道路上越走越远啊,期待您的好消息。 回复 9# Monox


   
作者: stanley_tam    时间: 2014-05-22 09:27
这么多字。。都手打的啊,膜拜下。。。{:3_193:}
作者: davexu2    时间: 2014-05-22 09:29
我似乎遗漏了什么。原文件列表里面有100个想要改名的文件,原文件夹里也有100个名字,这两个100 是一一对应的,不多也不少,更没有名字对不上的。脚本运行以后,100个文件都被改了名字。脚本运行中,while 一共读取了 105 个文件,包括 . .. 100个文件,以及最后输出那3个(这个后来验证过)。如果把原文见夹中的3个文件(原文件名,非改后)删了任意一个,就只剩下另外2个会被输出。我也知道这个脚本写的不严谨,改自然也容易,不过我想知道的是:为什么会多读取3个特定的文件,不多不少,而且就是这3个,不是其他的。能帮忙解惑吗?回复 10# rubyish


   
作者: rubyish    时间: 2014-05-22 20:45
本帖最后由 rubyish 于 2014-05-22 22:06 编辑

SORRY! wo wujie timu le.
du le yixie DATA yiji ceshi zhihou.
wo caice?
# wo budong C, meiyou kan SOURCE CODE.
diyici READDIR de shihou,
queding le FILES de shuliang, yiji ta de FILE NAME
xiangxiang ta STORE TO AN ARRAY?
ruguo zai yige WHILE LOOP, ta de LOOP cishu buhui gaibian le.
suiran women jiaru le renhe shuliang de FILE zai zhege FOLDER.

generate file:
  1. my $d = '/tmp/test/';
  2. my @dup;
  3. my @r = ( 'A' .. 'Z', 1 .. 100, 'a' .. 'z' );
  4. for ( 1 .. 10 ) {
  5.     my $R = int rand @r;
  6.     redo if $dup[$R]++;
  7.     my $r = $r[$R];
  8.     `touch $d$r`;
  9. }
复制代码
wode TEST:
  1. my @r = 'a' .. 'z';
  2. my $d = '/tmp/test/';
  3. opendir my $dir, '/tmp/test/';

  4. # zhege shihou hai meiyou queding READDIR de FILES

  5. while ( my $f = readdir $dir ) {

  6.     # diyi ci READDER zhihou
  7.     # [ queding le ] READDER de FILES
  8.     next if $f =~ /\./;
  9.     my $rand = $r[ rand @r ];
  10.     `touch $d$rand$f`;

  11.     # jiaru FILE zai zhege FOLDER
  12.     # WHILE LOOP de cishu buhui gaibian
  13.     # FILE buhui gaibian
  14.    
  15.     my @count     = `ls $d`;
  16.     my $files_now = @count;
  17.     my $tell      = telldir $dir;
  18.     say "$tell $f\tcount: $files_now";
  19. }
复制代码
wo CAICE ta de BEHAVIOR keneng ruci?
  1. my ( $INDEX, @FILES, $DH ) = 0;

  2. sub OPENDIR {
  3.     my $dir = shift;
  4.     $DH = $dir;
  5. }

  6. sub CLOSEDIR {
  7.     my $dh = shift;    # example
  8.     $INDEX = 0;
  9.     undef @FILES;
  10.     undef $DH;
  11. }
  12. sub TELLDIR { $INDEX + 1 }
  13. sub REWINDDIR { $INDEX = 0 }

  14. sub READDER {
  15.     return if $INDEX > @FILES;
  16.     my $dh = shift;
  17.     @FILES = grep chomp, `ls $dh` if !@FILES; # ignore '.', '..'
  18.     if (wantarray) {
  19.         my @want = @FILES[ $INDEX++ .. $#FILES ];
  20.         $INDEX = @FILES;
  21.         @want;
  22.     }
  23.     else {
  24.         $FILES[ $INDEX++ ];
  25.     }
  26. }
复制代码
DATA:
http://stackoverflow.com/questio ... luous-dir-structure
  1. #define NAME_MAX 14   /* longest filename component; */
  2.                               /* system dependent */

  3. typedef struct {      /* portable director-entry */
  4.     long ino;                 /* inode number */
  5.     char name[NAME_MAX+1];    /* name + '\0' terminator */
  6. } Dirent;

  7. typedef struct {      /* minimal DIR: no buffering, etc. */
  8.     int fd;                   /* file descriptor for directory */
  9.     Dirent d;                 /* the directory entry */
  10. } DIR;

  11. DIR *opendir(char *dirname);
  12. Dirent *readdir(DIR *dfd);
  13. void closedir(DIR *dfd);
复制代码
opendir() basically uses the system call open() to open a directory
and malloc() to allocate space for a DIR structure.
The file descriptor returned by open() is then stored
in the variable fd of that DIR.

# Nothing is stored in the Dirent component.

readdir() uses the system call read() to get the next
(system-dependent) directory entry of an opened directory

# and copies the so obtained inode number and
# filename into a static Dirent structure

(to which a pointer is returned).
The only information needed by readdir()
is the file descriptor stored in the DIR structure.

wode jielun:
ruguo you wenti? keneng shi yige BUG?
作者: davexu2    时间: 2014-05-23 09:06
多谢你回复这么多!我也不懂C,需要时间消化下你的拼音和代码,先行谢过了!非常感谢,非常感谢!回复 15# rubyish


   




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2