$path = "/path/to/dir/"; opendir DIR, $path; @arr1 = readdir DIR; @arr2 = grep{-T "$path$_"} @arr1; #text files only @arr3 = grep{!-d "$path$_"} @arr1; #no directories @arr4 = grep{-s "$path$_" < 1024} @arr1; #less than 1K
代码解释:假如被测试的目录项是一个文本文件,那么 -T 文件操作符就会返回真。其实针对目录项的测试操作还有很多。(注:文件和目录在系统中都是以目录项的形式来管理的,所以要区别一个目录项指向的是一个文件 还是一个目录需要相应的操作符)。注意上面的 readdir 函数返回指定目录下的所有目录项。因为在 grep 函数中对目录项的测试需要文件的完全路径,所以我们把 $PATH(存储了目录项的部分路径) 和 $_(存储了目录项的名字)中的内容联合起来得到文件的完全路径
use File::Find; find(\&handleFind, 'imac:documents:code'); sub handleFind{ my $foundFile = $File::Find::name; print "$foundFile\n" if ($foundFile =~ /\.html?$/i); }
运行结果: imac:documents:code:index.html imac:documents:code:perl:example.HTM
代码讨论:那些工作于 Unix 系统的 Perl 程序员可以非常简便的利用 UNIX 上提供的工具来完成许多日常的工作,比如递归的列出指定目录下的所有目录项(也就是列出指定目录及指定目录子目录下的所有目录项目)。然而 Perl 的一个最大的特征就是可以运行于很多的平台上。所以如果你碰巧工作在一个非 UNIX 的平台,或者如果你虽工作在 UNIX 平台,但不喜欢使用系统工具写脚本,你可以选择 Perl。要完成这些巧妙的工作,你需要使用 perl 中的 File:Find 模块。当你加载了这个模块的时候,你就可以使用其中的 find 子函数,在调用这个函数的时候,需要带参数:第一个参数是一个函数的引用,这个函数由你自己建立,每次一个文件被找到的时候,它都会运行。接下来的一个参 数是一串你想要搜索的路径。我写的这个示例脚本是运行在 Macintosh OS 8.x 系统上的,所以我使用了 Mac 系统的路径分隔符 :。如果是在 Windows,你可以用反斜杠,如果是在 Unix 系统则是正斜杠(至于在 Amiga 系统上用什么我就不知道了)。总之,find 函数将会在每次找到一个文件的时候调用你给出的子函数,而且会对子目录进行查找。在我的 handledfind 子函数中,我通过这个模块特定变量 $File::Find::name 来获得每次 find 找到的文件名。然后,就可以对该文件执行任何你想的测试,在上面的例子中,我们输出有 .html 的扩展名文件名。
open FH, "< anthem"; $/ = undef; $slurp = <FH>; print $slurp;
运行结果:一下就显示了所有的文件内容,此刻你应该非常的自豪。:) 代码讨论:尖括号 <> 对文件句柄进行操作,在标量上下文中它将返回文件的下一条记录,在数组上下文中它将返回所有的记录。在默认的情况下,文件中的记录被认为是由换行符分开 (例如回车或其他代表新行开始的字符)。你可以重新设定这个默认的分隔符,然后 Perl 将会以你指定的分隔符为准来替代换行符。全局变量 $/ 里存储了输入文件的分隔符,如果你把 $/ 的值设置为 undef ,那么 Perl 将会认为整个文件是一条记录(因为此刻已经没有文件分隔符了)。牢记 $/ 是全局变量,千万不要在脚本的其他地方不经意的改变它,这个错误将很难被发现。你可能会问,我们能否不改变 $/,而采用把文件的所有记录读到一个数组中,然后把数组联合成一个很长的字符串(比如 $slurp = join("",<FH>);)的方法实现一次读入文件。当然这也是一个有效的解决办法,但是你会发现它很慢,是否选用它取决你的应用,取决 于你是否关心运行速度。
open(MYOUT, "> bottle.txt"); *STDOUT = *MYOUT; print "message";
运行结果:文本文件 bottle.txt 现在包含 message 字符串。 代码讨论:以前可能你配合使用过 Print 函数和文件句柄,但是你是否知道就算你没有使用文件句柄,Perl 也默认你在使用一个称为 STDOUT 的句柄?C 程序员知道 STDOUT 代表标准输出,也就是通常的屏幕,或终端窗口(或者是 CGI 程序的输出端 - 浏览器)。在这里我们完成的工作是创建我们自己的文件句柄,它指向一个给定的文件,然后我们做了一件比较鬼的工作,使用 * 前缀把 STDOUT 转换为 typeglob 类型。Typeglob 类型的数据可以有别名,这样一个变量可能会指向另一个其他名字的变量。上面第二行代码使 STDOUT 指向 MYOUT 变量。所以执行 print 操作时的默认输出对象也就成为了我们创建的文件句柄。
use IO::Tee; $tee = IO::Tee->new(">> debuglog.txt", \*STDOUT); print $tee "an error ocurred on ".scalar(localtime)."\n";
运行结果:an error ocurred on Fri Feb 23 21:44:20 2001 代码讨论:如果,由于种种原因你想要同时向两个位置写入同一个字符串,这和 UNIX 下的 tee 工具的用途一样。即使你不是工作在 Unix 平台上,Perl 也通过 Tee 模块为你提供这个功能。Tee 模块可以在 CPAN 下载,你应该把它安装到 Perl 的 IO 库文件夹中。Tee 模块以 OOP 方式编写,所以使用它之前你应该首先使用它的 new 方法来创建一个 Tee 对象,整个过程需要两个参数,每个参数既可以是代表文件句柄的字符串,也可以是一个对已打开的文件句柄的引用。在上面的例子中,我们用一个字符串来代表一 个以附加模式打开的文件句柄,它指向名为 debuglog.txt 的文件,另一个参数是系统内置的文件句柄 STDOUT,整个句柄是系统自动创建的,print 函数默认情况对它进行操作。为了得到一个文件句柄的引用我们需要对一个 typeglob 类型的数据使用反斜杠。Typeglob 可以代表任何已命名的某个变量,不论它是数组,散列还是标量等。使用 * 很有必要,因为文件句柄自己没有前缀符号。new 操作符返回 Tee 类的一个实例对象,然后我们把整个实例赋给 $tee 标量。现在,无论什么时候我们向 $tee 进行写入操作,我们都同时向两个位置进行写操作。
use File::Basename; $path = "/docs/sitecircus.com/html/tricks/trick.of.the.week.html"; $basename = basename($path, ".html"); print $basename;
运行结果:trick.of.the.week 代码讨论:好了,成功了。问题是要找出文件的名字,要不带任何路径前缀,不带任何扩展名。File::Basename 模块可以使这很容易实现,我们只需要把文件的完全路径还有要剔除的扩展名传给它。上面的 path 变量是文件的完全路径,注意文件分隔符是 /,这个字符很特殊,因为它是操作系统的保留字符。这里你不能在文件名里使用系统的分隔符。你应该知道当今流行的操作系统都使用自己独特的文件分隔 符:Unix使用 /,Windows 使用 \,Macintosh 使用 :(顺便说一下,在 Windows 上的 Perl 脚本中,你既可以使用 \也可以使用 /作为文件分隔符,Perl 的解释器能理解你的意思)。File::Basename,当然,能正确在完全路径中找到文件名,不论时在什么系统下。
($uid, $gid) = (getpwnam($username))[2,3] or die "$user not in passwd file"; chown ($uid, $gid, $file) or warn "couldn't chown $file.";
运行结果:无输出 代码讨论:有的时候,你可能知道一个用户名,而你想用这个用户名做些事,比如改变一个文件的所有者。但是不幸的是,Perl 的 chown 命令不能接受用户名作为参数,但是可以接受一对数字:userid 和 groupid。虽然有这些不便之处,Perl 并没有让我们陷入困境,我们可以把用户名作为 getpwnam 函数的参数,获得一个数组,里面包含了用户名对应的 userid 和 groupid,分别对应着数组里的第二和第三个元素。
作者:Luke Melia