由Perl编程浅谈Windows和Linux识别文件的方式

feilniu 2010-08-02 04:06:11
以前写的一篇小文,发来与大家分享。这部分内容已经理解的朋友请自动忽略哈。


文件格式识别是个大话题。这里只是在学习Perl过程中想到的一点问题。

计算机文件最基本的分类:文本文件二进制文件。事实上,所有文件在计算机内部都是以二进制形式处理的。区别在于,文本文件依赖于特定字符编码,并且会解析为人可以阅读的字符集;二进制文件则是纯粹的字节流,数据的含义依赖于文件格式的约定和处理程序的解析机制。关于二者之间的优劣和选择,参看纯文本的威力
常见的文本文件有txt文本文件、htm/html网页文件、各种编程语言的源文件或脚本文件;常见的二进制文件有exe可执行文件(Windows平台)、bmp/jpg/gif/png图片文件、wav/mp3/wma/wmv/rmvb/avi音视频文件、doc/xls/ppt等Office文件、zip/rar/tar/gz归档压缩文件。

Windows根据扩展名(文件名最后一个点后面的部分)来识别文件类型。比如Helloworld.txt是一个文本文件,Helloworld.c是一个C源文件,Helloworld.exe是一个二进制可执行文件,等等。
Windows系统记录了可以识别的文件扩展名及打开对应文件的程序,可在指定文件的文件属性对话框的打开方式中看到和修改,或是在控制面板->文件夹选项的文件类型标签页查看全部已知文件类型,assoc和ftype命令也可以用来查看和修改已知文件类型的对应程序。这些信息实际上是记录在注册表的HKEY_CLASSES_ROOT之下。
用扩展名识别文件类型在性能上占优,但倘若扩展名与文件格式不相符便可能带来错误。比如,把一个jpg或doc文件的扩展名改为txt,文件将会默认使用记事本打开,通常会看到一堆乱码(二进制文件的字节流通常不能完全对应到某种字符编码的可阅读字符);把一个文本文件的扩展名改为xls或mp3,文件在由EXCEL或mp3播放器打开时会因为文件格式不符而报错。
因而Windows系统通常默认不显示文件扩展名(在控制面板->文件夹选项的查看标签页设置)。这样做带来的弊端是,一个名字形如“我不是病毒.txt.exe”的可执行文件倘若配上一个记事本的图标,因为最后的.exe扩展名被隐藏,则可能诱使一些大意的用户误点而执行。

Linux采用一种叫做魔数(Magic Number)的机制来识别文件类型,通过解析文件内容开头的若干字节来判断相应文件格式。这种方案较扩展名方案更为准确安全,代价是要承担解析文件的性能开销。
除识别文件类型外,Linux系统通过文件的模式位(file mode bits)判断用户是否拥有某一文件的读(r)、写(w)和执行(x)的权限。

以Perl编程为例。(通常,Perl指这种语言,perl指该语言的解释器程序名。)
Perl是一种解释型语言,运行Perl脚本首先需要系统安装有Perl解释器。运行命令perl filename即会执行名为filename的Perl脚本。

在Windows平台,最好指定Perl脚本的扩展名(通常为.pl)。安装Perl的过程中,通常默认把.pl文件关联到了Perl解释器,这样双击Helloworld.pl或直接运行命令Helloworld.pl即可执行该脚本。
如下命令可以查看.pl文件的关联设置(>指Windows命令提示符的引导字符):
>assoc .pl
.pl=Perl.File
>ftype Perl.File
Perl.File="C:\strawberry\perl\bin\perl.exe" "%1" %*
倘若安装程序没有进行如上默认关联,运行如下命令即可:
assoc .pl=Perl.File
ftype Perl.File="你的Perl安装路径\perl.exe" "%1" %*
这样,当双击或运行一个.pl文件时,该文件的文件名会填充到如上设置中的%1,则会用指定的Perl解释器运行该文件。%*是指命令行参数。双引号的作用是为了防止路径中包含空格导致无法找到文件。
如果实在想要让命令不包含扩展名,可以把.pl加入环境变量PATHEXT的最后。这样,运行命令Helloworld则可以自动执行Helloworld.pl(如果按照PATH和PATHEXT的查找顺序找到的是Helloworld.pl文件)。

在Linux平台,不需要指定Perl脚本的扩展名。只需要在脚本的第一行加上Sha-Bang(即#!),指定Perl解释器的路径即可。脚本完成后,用chmod命令给文件加上可执行权限,即可作为命令来执行。

比如如下Perl脚本:

#!/usr/bin/perl
print "Hello, world!\n";

其中,#以后直到行尾的内容为注释,解释器将之忽略;但#!作为文件最开始的两个字节,Linux系统会根据之后的内容来判断解释程序。(因而Linux家族的脚本语言通常都将#作为注释,以支持#!。)
在Windows平台,将以上代码保存为Helloworld.pl。运行perl Helloworld.pl或Helloworld.pl执行该脚本。
在Linux平台,将以上代码保存为Helloworld,运行命令chmod a+x Helloworld给该文件加上可执行权限(对所有用户)。运行perl Helloworld或./Helloworld执行该脚本。
其中,perl filename是通用的脚本运行方式——指定Perl解释器来运行指定的脚本文件,采用这种方式,不需要关联.pl扩展名(Windows平台),不需要增加可执行权限(Linux平台)。
直接运行脚本命令时,在Linux平台需要在命令前加上./,是为了指明该可执行文件位于当前目录。与Windows不同,Linux在查找可执行文件时只从PATH环境变量指定的路径查找,不查找当前目录。

最后说明一下脚本不包含扩展名的好处。
如果编写的脚本将被作为一个在多处使用的命令,文件名不包含扩展名则降低了命令与其实现方式的耦合性。比如对于Helloworld命令,调用命令的用户不必关心它是用Perl、Python、Bash还是C编译实现的,如果使用Helloworld.pl作为命令名,倘若将来需要将之改为C编译的命令,命令名的更改将会是一件很讨厌的事情。
...全文
1106 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
gz_jason 2010-08-15
  • 打赏
  • 举报
回复
[Quote=引用自Wikipedia的File format词条:]
One artifact of this approach is that the system can easily be tricked into treating a file as a different format simply by renaming it—an HTML file can, for instance, be easily treated as plain text by renaming it from filename.html to filename.txt. Although this strategy was useful to expert users who could easily understand and manipulate this information, it was frequently confusing to less technical users, who might accidentally make a file unusable (or 'lose' it) by renaming it incorrectly.

This led more recent operating system shells, such as Windows 95 and Mac OS X, to hide the extension when displaying lists of recognized files. This separates the user from the complete filename, preventing the accidental changing of a file type, while allowing expert users to still retain the original functionality through enabling the displaying of file extensions.
[/Quote]

以上是对隐藏(已知文件类型的)扩展名的策略的正面评价
wuming2003 2010-08-08
  • 打赏
  • 举报
回复
谢谢楼主的好文章。下面是个人的一些看法:
1. Linux下的shell(包括GNOME以及KDE等图形化的shell)都是通过扩展名来识别文件,显示不同的图标或者颜色,运行程序时直接调用内核exec系列的API,由内核处理#!xxx,选择合适的可执行程序。
2.. Windows内核(CreateProcess)只识别PE文件(不是通过扩展名)、bat文件以及cmd文件,其他的文件无法识别。shell运行程序时先取得文件绑定的程序,然后运行绑定的程序。

这两种设计各有好处,从最终用户看,windows的设计可能更加安全一点,特别是在windows下文件缺乏可执行权限的控制的情况下。
yzs0128 2010-08-06
  • 打赏
  • 举报
回复
学习了
fibbery 2010-08-05
  • 打赏
  • 举报
回复
我现在对文件类型已经没什么感觉了,但,的确在接触Unix的最初,受windows的影响,总想知道文件类型是什么,为什么Unix下没有文件扩展名之类的。但是现在,我已经漠然了。
tianluys 2010-08-05
  • 打赏
  • 举报
回复
学习~~~
feilniu 2010-08-05
  • 打赏
  • 举报
回复
Windows之所以被大众以为易染病毒,很大程度上是拜这条“默认策略”所赐。而且直到NT6.1,Windows也没有“悔改”的迹象,杯具啊。
====
Windows主要的安全问题是用户默认以Administrator权限登录,从而导致病毒的危害容易扩大,幸而在Vista和Win7已经改进了这一点。同样,使用Linux也一定养成普通用户+sudo的使用习惯,而不是一直用root登录。
扩展名识别的问题并不总是负面的。例如,我经常喜欢在一个cmd脚本的最后加上ren %~nx0 %~nx0o,执行之后自动把脚本扩展名改为cmdo,防止误点击导致重复执行。


另外,我刚才在自己的Ubuntu 10.04上试了一下pdf和jpg两种格式:
====
GUI下应该是采用了扩展名关联来提高图标和缩略图显示性能。
Shell下的file命令是使用Magic Number来识别文件的。


是不是说,假如我用perl perl_source的方式来执行脚本的话,连文件头的这句#!/usr/bin/perl都可以省了?
====
是的。
但在Linux下,带上这一行是好习惯。一方面,便于file命令识别(#!即是脚本文件的MagicNumber),另一方面,如果需要把一个脚本发布为可执行命令,必须有这一行才能以filename的方式执行。
gz_jason 2010-08-05
  • 打赏
  • 举报
回复
兄台好文!
我最近正处于Windows过渡到Linux的艰难时期,很多地方模模糊糊,似懂非懂,这篇文章为我揭开了一些迷雾,非常感谢!

[Quote=引用楼主 feilniu 的回复:]
因而Windows系统通常默认不显示文件扩展名(在控制面板->文件夹选项的查看标签页设置)。这样做带来的弊端是,一个名字形如“我不是病毒.txt.exe”的可执行文件倘若配上一个记事本的图标,因为最后的.exe扩展名被隐藏,则可能诱使一些大意的用户误点而执行。[/Quote]
Windows之所以被大众以为易染病毒,很大程度上是拜这条“默认策略”所赐。而且直到NT6.1,Windows也没有“悔改”的迹象,杯具啊。

[Quote=引用楼主 feilniu 的回复:]
Linux采用一种叫做魔数(Magic Number)的机制来识别文件类型,通过解析文件内容开头的若干字节来判断相应文件格式。这种方案较扩展名方案更为准确安全,代价是要承担解析文件的性能开销。[/Quote]
这个性能开销在非GUI时代还好,只是打开文件的时候分析一下。而GUI时代的浏览器则经常要展示一个文件夹内所有的文件,就需要根据不同的文件类型展示不同的文件图标,也就是需要经常性地分析一大批文件的“魔数”,这个开销就大了。

另外,我刚才在自己的Ubuntu 10.04上试了一下pdf和jpg两种格式:
1. 直接双击打开,有相应的doc viewer和pic viewer
2. 修改扩展名,无法打开。看来Ubuntu也有和Windows类似的文件格式关联的机制
3. 把扩展名删除。jpg仍可显示缩略图,并可双击打开,而pdf能被识别为pdf,缩略图变成了pdf通用图标,双击无法打开,具体原因暂不明

[Quote=引用楼主 feilniu 的回复:]
其中,perl filename是通用的脚本运行方式——指定Perl解释器来运行指定的脚本文件,采用这种方式,不需要关联.pl扩展名(Windows平台),不需要增加可执行权限(Linux平台)。[/Quote]
是不是说,假如我用perl perl_source的方式来执行脚本的话,连文件头的这句#!/usr/bin/perl都可以省了?
wxgiter 2010-08-04
  • 打赏
  • 举报
回复
学习...
iambic 2010-08-02
  • 打赏
  • 举报
回复
Linux采用一种叫做魔数(Magic Number)的机制来识别文件类型,通过解析文件内容开头的若干字节来判断相应文件格式。这种方案较扩展名方案更为准确安全,代价是要承担解析文件的性能开销。

这种方式一点也不准确,也谈不上安全。至少比起用扩展名来识别,是不如的。而且这种方式很难被称作是Linux风格,只是Linux平台提供了这样一种方式而已。大多数情况,对于数据文件,类型信息还是要靠用户提供比较好。扩展名就是一种用户提供的方式,而且Linux上也是使用的。

37,740

社区成员

发帖
与我相关
我的任务
社区描述
JavaScript,VBScript,AngleScript,ActionScript,Shell,Perl,Ruby,Lua,Tcl,Scala,MaxScript 等脚本语言交流。
社区管理员
  • 脚本语言(Perl/Python)社区
  • WuKongSecurity@BOB
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧