perlport - 编写可移植的 Perl
Perl 在许多操作系统上运行。虽然大多数操作系统有很多共同点,但也各自拥有独特的特性。
本文档旨在帮助您了解什么是可移植的 Perl 代码。这样,一旦您决定编写可移植的代码,您就知道界限在哪里,并且可以保持在这些界限之内。
在充分利用特定类型的计算机和充分利用各种计算机之间存在权衡。自然地,随着您扩展范围并变得更加多样化,共同因素会减少,您能用来完成特定任务的共同点会越来越少。因此,当您开始解决问题时,重要的是要考虑您想在权衡曲线的哪个部分进行操作。具体来说,您必须决定您正在编写的代码是否需要具有可移植性的通用性,或者是否只是现在就完成工作。这是最难的选择。其余部分很简单,因为 Perl 提供了许多选择,无论您想如何解决问题。
换句话说,编写可移植代码通常意味着有意限制您的可用选择。自然地,这样做需要纪律和牺牲。可移植性和便利性的乘积可能是一个常数。您已经被警告了。
请注意以下两个重要要点
没有理由阻止你将 Perl 用作将 Unix 工具粘合在一起的语言,或用于创建 Macintosh 应用程序的原型,或用于管理 Windows 注册表。如果在给定程序中出于某种原因无法实现可移植性,那么就不要费心了。
不要被误认为创建可移植的 Perl 代码很难。其实并不难。Perl 尽其所能弥合不同平台之间可用功能的差距,以及使用这些功能的所有可用方法。因此,几乎所有 Perl 代码都可以在任何机器上运行而无需修改。但是,在编写可移植代码时,存在一些重大问题,本文档完全是关于这些问题的。
一般规则如下:当你处理一项通常使用各种平台完成的任务时,请考虑编写可移植代码。这样,你不会牺牲太多可用的实现选择,同时也可以为你的用户提供很多平台选择。另一方面,当你必须利用某个特定平台的某些独特功能时,就像系统编程(无论是 Unix、Windows、VMS 等)经常发生的那样,请考虑编写特定于平台的代码。
当代码仅在两个或三个操作系统上运行时,你可能只需要考虑这些特定系统的差异。重要的是要决定代码将在哪里运行,并在你的决定中保持一致。
以下内容分为三个主要部分:可移植性的主要问题 ("ISSUES"),特定于平台的问题 ("PLATFORMS"),以及在不同端口上行为不同的内置 Perl 函数 ("FUNCTION IMPLEMENTATIONS")。
这些信息不应被视为完整信息;它可能包含有关某些端口特性的瞬态信息,这些特性几乎都处于不断发展状态。因此,这些材料应被视为一项持续进行的工作 (<IMG SRC="yellow_sign.gif" ALT="Under Construction">
)。
在大多数操作系统中,文件中的行以换行符结尾。具体使用什么作为换行符可能因操作系统而异。Unix 传统上使用 \012
,一种 DOSish I/O 使用 \015\012
,Mac OS 使用 \015
,而 z/OS 使用 \025
。
Perl 使用 \n
来表示“逻辑”换行符,其中逻辑可能取决于所使用的平台。在 MacPerl 中,\n
始终表示 \015
。在 EBCDIC 平台上,\n
可以是 \025
或 \045
。在 DOSish Perl 中,\n
通常表示 \012
,但在“文本”模式下访问文件时,Perl 使用 :crlf
层将其转换为(或从)\015\012
,具体取决于您是读取还是写入。Unix 在规范模式下的 tty 上执行相同的操作。\015\012
通常被称为 CRLF。
要从文本行中修剪尾随换行符,请使用 chomp
。使用默认设置,该函数查找尾随的 \n
字符,因此以可移植的方式进行修剪。
在处理二进制文件(或以二进制模式打开的文本文件)时,请确保在使用 chomp
之前将 $/
显式设置为适合您的文件格式的值。
由于“文本”模式转换,DOSish Perl 在以“文本”模式访问的文件上使用 seek
和 tell
时存在限制。坚持使用从 tell
获取的位置进行 seek
(以及其他位置),通常可以在“文本”模式下自由使用 seek
和 tell
。使用 seek
或 tell
或其他文件操作可能不可移植。但是,如果您对文件使用 binmode
,通常可以使用 seek
和 tell
安全地使用任意值。
套接字编程中一个常见的误解是 \n eq \012
始终成立。当使用像常见的互联网协议这样的协议时,\012
和 \015
是明确要求的,而逻辑 \n
和 \r
(回车)的值不可靠。
print $socket "Hi there, client!\r\n"; # WRONG
print $socket "Hi there, client!\015\012"; # RIGHT
然而,使用 \015\012
(或 \cM\cJ
,或 \x0D\x0A
)可能很繁琐且难看,而且会让维护代码的人感到困惑。因此,Socket
模块为那些需要的人提供了正确的方法。
use Socket qw(:DEFAULT :crlf);
print $socket "Hi there, client!$CRLF" # RIGHT
从套接字读取数据时,请记住默认的输入记录分隔符 $/
是 \n
,但健壮的套接字代码会将 \012
或 \015\012
都识别为行尾。
while (<$socket>) { # NOT ADVISABLE!
# ...
}
由于 CRLF 和 LF 都以 LF 结尾,因此可以将输入记录分隔符设置为 LF,并在之后剥离任何 CR。最好写成
use Socket qw(:DEFAULT :crlf);
local($/) = LF; # not needed if $/ is already \012
while (<$socket>) {
s/$CR?$LF/\n/; # not sure if socket uses LF or CRLF, OK
# s/\015?\012/\n/; # same thing
}
这个例子比上一个例子更可取——即使是在 Unix 平台上——因为现在任何 \015
(\cM
)都被剥离了(并且人们欢欣鼓舞)。
类似地,返回文本数据的函数——例如获取网页的函数——有时应该在返回数据之前转换换行符,如果它们还没有被转换为本地换行符表示。通常一行代码就足够了
$data =~ s/\015?\012/\n/g;
return $data;
其中一些内容可能令人困惑。以下是对 ASCII CR 和 LF 字符的简便参考。你可以打印出来并放在钱包里。
LF eq \012 eq \x0A eq \cJ eq chr(10) eq ASCII 10
CR eq \015 eq \x0D eq \cM eq chr(13) eq ASCII 13
| Unix | DOS | Mac |
---------------------------
\n | LF | LF | CR |
\r | CR | CR | LF |
\n * | LF | CRLF | CR |
\r * | CR | CR | LF |
---------------------------
* text-mode STDIO
Unix 列假设你没有以规范模式访问串行线(如 tty)。如果你访问了,那么输入的 CR 将变为 "\n",而输出的 "\n" 将变为 CRLF。
这些只是 Perl 中 \n
和 \r
最常见的定义。可能还有其他定义。例如,在 z/OS(OS/390)或 OS/400(使用 ILE,PASE 是基于 ASCII 的)这样的 EBCDIC 实现上,上述内容类似于 "Unix",但代码编号会发生变化
LF eq \025 eq \x15 eq \cU eq chr(21) eq CP-1047 21
LF eq \045 eq \x25 eq chr(37) eq CP-0037 37
CR eq \015 eq \x0D eq \cM eq chr(13) eq CP-1047 13
CR eq \015 eq \x0D eq \cM eq chr(13) eq CP-0037 13
| z/OS | OS/400 |
----------------------
\n | LF | LF |
\r | CR | CR |
\n * | LF | LF |
\r * | CR | CR |
----------------------
* text-mode STDIO
不同的 CPU 以不同的顺序(称为大小端)和宽度(如今最常见的是 32 位和 64 位)存储整数和浮点数。当程序尝试以二进制格式将数字从一种 CPU 架构传输到另一种 CPU 架构时,这会影响程序,通常是通过网络连接“实时”传输,或者将数字存储到辅助存储器(如磁盘文件或磁带)。
冲突的存储顺序会使数字变得一团糟。如果一个小端主机(Intel,VAX)存储 0x12345678(十进制为 305419896),则一个大端主机(Motorola,Sparc,PA)会将其读取为 0x78563412(十进制为 2018915346)。Alpha 和 MIPS 可以是任一:Digital/Compaq 在小端模式下使用它们;SGI/Cray 在大端模式下使用它们。为了避免网络(套接字)连接中的此问题,请使用 pack
和 unpack
格式 n
和 N
,即“网络”顺序。这些格式保证是可移植的。
从 Perl 5.10.0 开始,您还可以使用 >
和 <
修饰符来强制使用大端或小端字节顺序。如果您想存储有符号整数或 64 位整数,这将非常有用。
您可以通过解压缩以本机格式打包的数据结构来探索平台的字节序,例如
print unpack("h*", pack("s2", 1, 2)), "\n";
# '10002000' on e.g. Intel x86 or Alpha 21064 in little-endian mode
# '00100020' on e.g. Motorola 68040
如果您需要区分字节序架构,您可以使用以下任一变量集:
$is_big_endian = unpack("h*", pack("s", 1)) =~ /01/;
$is_little_endian = unpack("h*", pack("s", 1)) =~ /^1/;
即使在字节序相同的平台之间,不同的宽度也会导致截断。宽度较短的平台会丢失数字的上部。除了避免传输或存储原始二进制数字之外,没有好的解决方案来解决此问题。
可以通过两种方式规避这两个问题。要么始终以文本格式而不是原始二进制格式传输和存储数字,要么考虑使用 Data::Dumper
和 Storable
(从 Perl 5.8 开始包含)之类的模块。将所有数据保留为文本可以大大简化问题。
如今,大多数平台都以分层方式构建文件。因此,可以合理地假设所有平台都支持“路径”的概念,以唯一标识系统上的文件。但是,路径的实际写入方式却大不相同。
尽管类似,但文件路径规范在 Unix、Windows、Mac OS、OS/2、VMS、VOS、RISC OS 以及其他操作系统之间有所不同。例如,Unix 是少数几个具有单一根目录这一优雅理念的操作系统之一。
DOS、OS/2、VMS、VOS 和 Windows 可以像 Unix 一样使用 /
作为路径分隔符,也可以使用它们自己的特殊方式(例如,拥有多个根目录和各种“无根”设备文件,如 NIL: 和 LPT:)。
Mac OS 9 及更早版本使用 :
作为路径分隔符,而不是 /
。
文件系统可能不支持硬链接(link
)或符号链接(symlink
、readlink
、lstat
)。
文件系统可能不支持访问时间戳或修改时间戳(这意味着唯一可移植的时间戳是修改时间戳),或者任何时间戳的精度为一秒(例如,FAT 文件系统将时间精度限制为两秒)。
“inode 修改时间戳”(-C
文件测试)实际上可能是“创建时间戳”(在 Unix 中并非如此)。
VOS perl 可以使用 /
作为路径分隔符来模拟 Unix 文件名。大于号、小于号、井号和百分号等原生路径名字符始终被接受。
RISC OS perl 可以使用 /
作为路径分隔符来模拟 Unix 文件名,也可以使用原生方式,使用 .
作为路径分隔符,使用 :
表示文件系统和磁盘名称。
不要假设 Unix 文件系统访问语义:读取、写入和执行是所有权限,即使它们存在,它们的语义(例如,r
、w
和 x
在目录上的含义)也是 Unix 的语义。各种 Unix/POSIX 兼容层通常会尝试使像 chmod
这样的接口工作,但有时根本没有好的映射。
File::Spec
模块提供方法来操作路径规范,并为每个平台返回原生格式的结果。这通常是不必要的,因为 Perl 在所有支持的平台上都理解 Unix 风格的路径,但如果您需要为不理解 Unix 语法的原生实用程序生成原生路径,或者如果您正在操作未知(因此可能是原生)语法的路径或路径组件,File::Spec
是您的朋友。以下是一些简短的示例
use File::Spec::Functions;
chdir(updir()); # go up one directory
# Concatenate a path from its components
my $file = catfile(updir(), 'temp', 'file.txt');
# on Unix: '../temp/file.txt'
# on Win32: '..\temp\file.txt'
# on VMS: '[-.temp]file.txt'
一般来说,生产代码不应该硬编码文件路径。将它们设置为用户提供或从配置文件中读取更好,同时要记住文件路径语法在不同的机器上有所不同。
这在像 Makefile 和测试套件这样的脚本中尤其明显,这些脚本通常假设/
作为子目录的路径分隔符。
同样有用的是标准发行版中的File::Basename
,它将路径名拆分为多个部分(基本文件名、目录的完整路径和文件后缀)。
即使在一个平台上(如果你能将 Unix 称为一个平台),也不要依赖特定系统文件或目录的存在或内容,例如/etc/passwd、/etc/sendmail.conf、/etc/resolv.conf,甚至/tmp/。例如,/etc/passwd可能存在,但不包含加密密码,因为系统正在使用某种形式的增强安全性。或者它可能不包含所有帐户,因为系统正在使用 NIS。如果代码确实需要依赖这样的文件,请在代码的文档中包含对文件及其格式的描述,然后让用户可以轻松地覆盖文件的默认位置。
不要假设文本文件以换行符结尾。它们应该,但人们会忘记。
不要有两个同名但大小写不同的文件或目录,例如test.pl和Test.pl,因为许多平台具有不区分大小写(或至少大小写宽容)的文件名。此外,尽量不要在名称中使用非单词字符(除了.
),并保持 8.3 约定,以获得最大的可移植性,尽管这可能显得繁琐。
同样,在使用AutoSplit
模块时,请尝试将函数保持在 8.3 命名和不区分大小写的约定中;或者,至少,确保生成的具有唯一(不区分大小写)的前 8 个字符。
大多数系统允许文件名中包含空格,但并非所有系统都允许,即使在允许的系统上,某些实用程序也可能对空格感到困惑。
许多系统(DOS、VMS ODS-2)的文件名中不能包含多个.
。
不要假设>
不会成为文件名的第一个字符。始终使用open
的三参数版本。
open my $fh, '<', $existing_file) or die $!;
两参数open
是神奇的,它可以转换文件名中的>
、<
和|
等字符,这通常是错误的做法。sysopen
和三参数open
没有这个问题。
不要在文件名中使用:
,因为许多系统使用它来表示自己的语义(例如,Mac OS Classic 使用它来分隔路径名组件,许多网络方案和实用程序使用它来分隔节点名和路径名,等等)。出于同样的原因,请避免使用@
、;
和|
。
不要假设在路径名中可以将两个前导斜杠//
合并为一个:一些网络和集群文件系统对此有特殊的语义。让操作系统来处理它。
ANSI C 定义的可移植文件名字符 是
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
. _ -
并且-
不应该作为第一个字符。如果你想更严格,请保持不区分大小写,并遵循 8.3 命名约定(所有文件和目录在同一个目录中必须是唯一的,如果它们的名称被转换为小写并截断为 .
之前的八个字符,如果有的话,以及 .
之后的三个字符,如果有的话)。(并且不要在目录名中使用 .
。)
并非所有平台都提供命令行。这些通常是主要依靠图形用户界面 (GUI) 进行用户交互的平台。需要命令行界面的程序可能无法在所有地方运行。这可能是程序用户需要处理的问题,所以不要为此熬夜担心。
一些平台无法删除或重命名由系统打开的文件,此限制也可能适用于更改文件系统元信息,例如文件权限或所有者。请记住在完成操作后close
文件。不要unlink
或rename
打开的文件。不要tie
或open
已经绑定或打开的文件;请先untie
或close
它。
不要同时多次打开同一个文件进行写入,因为某些操作系统会对这些文件进行强制锁定。
不要假设对目录的写入/修改权限意味着有权在该目录中添加或删除文件/目录。这是特定于文件系统的:在某些文件系统中,你还需要(甚至只是)在文件/目录本身中具有写入/修改权限。在某些文件系统(AFS、DFS)中,添加/删除目录项的权限是完全独立的权限。
不要假设单个 unlink
可以完全删除文件:某些文件系统(最值得注意的是 VMS 中的文件系统)具有版本化的文件系统,而 unlink
只删除最新的版本(它不会删除所有版本,因为默认情况下,这些平台上的原生工具也只删除最新的版本)。删除文件所有版本的便携式习惯用法是
1 while unlink "file";
如果文件由于某种原因(受保护、不存在等)无法删除,这将终止。
不要指望 %ENV
中存在特定的环境变量。不要指望 %ENV
条目区分大小写,甚至保持大小写。不要尝试通过 %ENV = ();
来清除 %ENV
,或者,如果你真的必须这样做,请将其设置为 $^O ne 'VMS'
的条件,因为在 VMS 中,%ENV
表格远不止一个进程内键值字符串表格。
在 VMS 上,%ENV
哈希表中的一些条目是在读取时动态创建的,前提是它们的键之前不存在。$ENV{HOME}
、$ENV{TERM}
、$ENV{PATH}
和 $ENV{USER}
的值已知是动态生成的。动态生成的具体名称可能会因 VMS 上 C 库的版本而异,并且可能存在比文档中记录的更多名称。
在 VMS 上,默认情况下,对 %ENV
哈希表的更改在 perl 退出后会保留。随后在同一进程中调用 perl 会无意中继承原本打算临时使用的环境设置。
不要指望信号或 %SIG
的任何功能。
不要指望文件名通配符。请改用 opendir
、readdir
和 closedir
。
不要指望每个程序的环境变量或每个程序的当前目录。
不要指望 $!
的特定值,无论是数字值还是字符串值。用户可能会切换其语言环境,导致错误消息被翻译成他们的语言。如果你可以信任 POSIXish 环境,则可以便携式地使用 Errno
模块定义的符号,例如 ENOENT
。并且不要信任 $!
的值,除非是在失败的系统调用之后立即。
不要假设用于调用命令或程序的名称(使用 system
或 exec
)也可以用于测试包含该命令或程序可执行代码的文件是否存在。首先,许多系统具有“内部”命令,这些命令内置于 shell 或操作系统中,虽然可以调用这些命令,但没有相应的文件。其次,一些操作系统(例如,Cygwin、OS/2 和 VOS)要求可执行文件具有后缀;这些后缀通常允许在命令名称中使用,但不是必需的。因此,像 perl
这样的命令可能存在于名为 perl、perl.exe 或 perl.pm 的文件中,具体取决于操作系统。变量 $Config{_exe}
(位于 Config
模块中)保存可执行文件后缀(如果有)。第三,VMS 端口仔细设置了 $^X
和 $Config{perlpath}
,因此不需要进一步处理。这很好,因为下面使用的匹配正则表达式将不得不处理 VMS 文件名中可能出现的尾随版本号。
要将 $^X
转换为文件路径名,并考虑各种操作系统可能性的要求,请说
use Config;
my $thisperl = $^X;
if ($^O ne 'VMS') {
$thisperl .= $Config{_exe}
unless $thisperl =~ m/\Q$Config{_exe}\E$/i;
}
要将 $Config{perlpath}
转换为文件路径名,请说
use Config;
my $thisperl = $Config{perlpath};
if ($^O ne 'VMS') {
$thisperl .= $Config{_exe}
unless $thisperl =~ m/\Q$Config{_exe}\E$/i;
}
不要假设您可以访问公共互联网。
不要假设只有一种方法可以穿过防火墙访问公共互联网。
不要假设您可以通过除 80 端口或某些 Web 代理之外的任何其他端口访问外部世界。许多防火墙阻止了 ftp。
不要假设您可以通过连接到本地 SMTP 端口发送电子邮件。
不要假设您可以通过名称“localhost”访问自己或任何节点。对于“127.0.0.1”也是如此。您需要尝试两者。
不要假设主机只有一个网卡,或者它不能绑定到多个虚拟 IP 地址。
不要假设特定的网络设备名称。
不要假设特定的一组 ioctl
将起作用。
不要假设您可以 ping 主机并获得回复。
不要假设任何特定端口(服务)都会响应。
不要假设 Sys::Hostname
(或任何其他 API 或命令)返回完全限定的域名或非限定的域名:这完全取决于系统的配置方式。还要记住,对于 DHCP 和 NAT 之类的东西,您获得的主机名可能不太有用。
以上所有“不要”可能看起来很吓人,而且确实如此,但关键是如果无法访问所需的特定网络服务,则要优雅地降级。崩溃或挂起看起来很不专业。
一般来说,不要在旨在可移植的代码中直接访问系统。这意味着,不要使用 system
、exec
、fork
、pipe
、``
或 qx//
、open
带有 |
,也不要使用任何其他让成为 Perl 黑客值得成为的东西。
启动外部进程的命令通常在大多数平台上受支持(尽管其中许多不支持任何类型的分叉)。使用它们的问题在于你对它们调用什么。外部工具在不同的平台上通常有不同的名称,可能在同一个位置不可用,可能接受不同的参数,可能表现不同,并且通常以平台相关的方式呈现其结果。因此,你应该很少依赖它们来产生一致的结果。(再说一次,如果你正在调用 netstat -a
,你可能不希望它在 Unix 和 CP/M 上运行。)
一段特别常见的 Perl 代码是打开一个管道到 sendmail
open(my $mail, '|-', '/usr/lib/sendmail -t')
or die "cannot fork sendmail: $!";
这对于系统编程来说很好,因为已知 sendmail 可用。但对于许多非 Unix 系统来说,这不好,甚至一些可能没有安装 sendmail 的 Unix 系统也不好。如果需要可移植的解决方案,请参阅 CPAN 上处理它的各种发行版。 Mail::Mailer
和 Mail::Send
在 MailTools
发行版中被普遍使用,并提供多种邮件方法,包括 mail
、sendmail
和直接 SMTP(通过 Net::SMTP
),如果邮件传输代理不可用。 Mail::Sendmail
是一个独立的模块,它提供简单、平台无关的邮件发送。
Unix System V IPC (msg*(), sem*(), shm*()
) 甚至在所有 Unix 平台上都不可用。
不要使用 pack("N", 10, 20, 30, 40)
的裸结果或裸 v 字符串(例如 v10.20.30.40
)来表示 IPv4 地址:这两种形式只是将四个字节打包成网络顺序。这将等于 C 语言 in_addr
结构体(这是套接字代码内部使用的)并不保证。要实现可移植性,请使用 Socket
模块的例程,例如 inet_aton
、inet_ntoa
和 sockaddr_in
.
可移植代码的经验法则是:全部使用可移植的 Perl,或者使用一个模块(它可能在内部使用平台特定的代码实现它,但公开一个通用接口)。
XS 代码通常可以与任何平台一起使用,但依赖的库、头文件等可能不容易获得或可移植,或者 XS 代码本身可能是平台特定的,就像 Perl 代码一样。如果库和头文件是可移植的,那么通常可以确保 XS 代码也是可移植的。
编写 XS 代码时会遇到另一种可移植性问题:最终用户系统上是否可用 C 编译器。C 本身也存在可移植性问题,编写 XS 代码会让你接触到其中一些问题。纯粹用 Perl 编写代码是实现可移植性的更简单方法。
一般来说,标准模块可以在跨平台使用。值得注意的例外是 CPAN
模块(目前它连接到可能不可用的外部程序)、特定于平台的模块(如 ExtUtils::MM_VMS
)和 DBM 模块。
并非所有平台都提供一个 DBM 模块。 SDBM_File
和其他模块通常在所有 Unix 和 DOSish 端口上可用,但在 MacPerl 中不可用,MacPerl 中只有 NDBM_File
和 DB_File
可用。
好消息是至少应该提供一些 DBM 模块,并且 AnyDBM_File
将使用它能找到的任何模块。当然,代码需要相当严格,降到最大公约数(例如,每个记录不超过 1K),以便它能与任何 DBM 模块一起使用。有关更多详细信息,请参阅 AnyDBM_File。
系统对时间和日期的理解方式差异很大。不要假设时区存储在 $ENV{TZ}
中,即使它在那里,也不要假设你可以通过该变量控制时区。不要假设任何关于三个字母的时区缩写(例如 MST 是山区标准时间,它也可能代表莫斯科标准时间)。如果你需要使用时区,请以某种明确的格式表示它们,例如与 UTC 的精确分钟偏移量,或 POSIX 时区格式。
不要假设纪元从 1970 年 1 月 1 日 00:00:00 开始,因为这是特定于操作系统和实现的。最好以明确的表示形式存储日期。ISO 8601 标准将 YYYY-MM-DD 定义为日期格式,或 YYYY-MM-DDTHH:MM:SS(这是一个字面上的“T”,将日期与时间分开)。请使用 ISO 8601,而不是让我们猜测 02/03/04 可能是什么日期。ISO 8601 甚至可以按原样排序。可以使用 Time::Piece
(参见 "Time::Piece 中的日期解析")或 Date::Parse
等模块将文本表示形式(如“1987-12-18”)轻松转换为特定于操作系统的值。可以使用 Time::Local
将 localtime
返回的值数组转换为特定于操作系统的表示形式。
在计算特定时间时,例如时间或日期模块中的测试,可能需要计算一个纪元偏移量。
use Time::Local qw(timegm);
my $offset = timegm(0, 0, 0, 1, 0, 1970);
在 Unix 中,$offset
的值为 0
,但在 Mac OS Classic 中则是一个很大的数字。$offset
可以加到 Unix 时间值上,以获得任何系统上的正确值。
对字符集的假设要非常少。
不要对字符的数值(ord
,chr
)做任何假设。不要使用显式的代码点范围(如 \xHH-\xHH)
。但是,从 Perl v5.22 开始,正则表达式模式中用方括号括起来的字符类范围,如 qr/[\N{U+HH}-\N{U+HH}]/
,是可移植的,从 Perl v5.24 开始,相同的范围在 tr///
中也是可移植的。您可以使用可移植的符号字符类,如 [:print:]
。
不要假设字母字符是连续编码的(从数值意义上讲)。可能存在间隙。但是,Perl 中的特殊编码保证了 qr/[A-Z]/
、qr/[a-z]/
和 qr/[0-9]/
的所有子集的行为都符合预期。tr///
对这些范围的行为相同。在模式中,使用 \N{...}
符号指定端点的任何范围都确保了字符集的可移植性,但在 Perl v5.22 中,tr///
不支持这种可移植性是一个 bug,在 v5.24 中已修复。
不要对字符的顺序做任何假设。小写字母可能出现在大写字母之前或之后;小写字母和大写字母可能交织在一起,使得 "a" 和 "A" 都出现在 "b" 之前;带重音符的字符和其他国际字符可能交织在一起,使得 ä 出现在 "b" 之前。可以使用 Unicode::Collate 对所有这些进行排序。
如果您假设 POSIX(这是一个相当大的假设),您可以从 perllocale 中了解更多关于 POSIX 本地化系统的信息。本地化系统至少尝试使事物更具可移植性,或者至少对非英语用户更方便和更友好。该系统影响字符集和编码,以及日期和时间格式——以及其他一些内容。
如果你真的想国际化,你应该考虑使用 Unicode。查看 perluniintro 和 perlunicode 获取更多信息。
默认情况下,Perl 假设你的源代码是用 8 位 ASCII 超集编写的。要在字符串和正则表达式中嵌入 Unicode 字符,可以使用 \x{HH}
或(更便携的)\N{U+HH}
符号。你也可以使用 utf8
编译指示,并用 UTF-8 编写你的代码,这样你就可以直接使用 Unicode 字符(不仅在引号构造中,而且在标识符中)。
如果你的代码注定要运行在虚拟内存系统非常有限(或缺失!)的系统上,那么你尤其要注意避免使用浪费的结构,例如
my @lines = <$very_large_file>; # bad
while (<$fh>) {$file .= $_} # sometimes bad
my $file = join('', <$fh>); # better
最后两个结构可能对大多数人来说看起来不直观。第一个反复增长一个字符串,而第二个一次性分配一大块内存。在某些系统上,第二个比第一个更有效。
大多数多用户平台提供基本的安全级别,通常在文件系统级别实现。然而,不幸的是,有些平台没有提供。因此,用户 ID、"home" 目录,甚至登录状态的概念在许多平台上可能是无法识别的。如果你编写的是安全意识强的程序,最好了解你将在什么类型的系统上运行,以便你可以为该平台(或平台类别)明确地编写代码。
不要假设 Unix 文件系统访问语义:操作系统或文件系统可能正在使用一些 ACL 系统,这些系统比通常的 rwx
更丰富。即使 rwx
存在,它们的语义也可能不同。
(从安全的角度来看,在尝试执行某项操作之前测试权限本身就很愚蠢:如果有人尝试这样做,就会有竞争条件的风险。有人或某些东西可能会在权限检查和实际操作之间更改权限。只需尝试操作即可。)
不要假设 Unix 用户和组语义:特别是,不要期望 $<
和 $>
(或 $(
和 $)
)用于切换身份(或成员资格)。
不要假设 set-uid 和 set-gid 语义。(即使你这样做了,也要三思而后行:set-uid 和 set-gid 是一个众所周知的安全漏洞。)
对于需要平台特定代码的情况,请考虑将平台特定代码放在一个地方,以便于移植到其他平台。使用 Config
模块和特殊变量 $^O
来区分平台,如 "PLATFORMS" 中所述。
注意“else综合征”
if ($^O eq 'MSWin32') {
# code that assumes Windows
} else {
# code that assumes Linux
}
else
分支应该用于真正最终的回退,而不是用于特定于某个平台的代码。
在为模块或程序提供的测试中要小心。模块代码可能是完全可移植的,但其测试可能不可移植。这通常发生在测试生成其他进程或调用外部程序以帮助测试时,或者(如上所述)测试假设了关于文件系统和路径的某些内容时。注意不要依赖于错误的特定输出样式,例如在失败的系统调用后检查 $!
时。使用 $!
用于除将其显示为输出之外的任何其他目的都是可疑的(尽管请参阅 Errno
模块以测试对错误值的合理可移植性)。某些平台期望特定的输出格式,并且这些平台上的 Perl 可能已相应调整。最重要的是,在测试错误值时不要锚定正则表达式。
上传到 CPAN 的模块由不同平台上的各种志愿者进行测试。这些 CPAN 测试人员会通过邮件收到每个新上传的通知,并回复列表,内容为 PASS、FAIL、NA(不适用于此平台)或 UNKNOWN(未知),以及任何相关的注释。
测试的目的是双重的:一是帮助开发人员修复由于缺乏对其他平台的测试而导致的代码中的任何问题;二是为用户提供有关给定模块是否在给定平台上工作的信息。
另请参阅
Perl 是使用 $^O
变量构建的,该变量指示其构建的操作系统。这是为了帮助加快代码速度而实现的,否则代码将不得不 use Config
并使用 $Config{osname}
的值。当然,要获取有关系统的更多详细信息,建议查看 %Config
。
但是,%Config
并不总是可靠的,因为它是在编译时构建的。如果 perl 在一个地方构建,然后转移到另一个地方,则某些值可能不正确。这些值甚至可能在事后被编辑过。
Perl 在各种各样的 Unix 和类 Unix 平台上运行(例如,源代码包中的 hints/ 目录中的大多数文件)。在大多数这些系统上,$^O
(因此也是 $Config{osname}
)的值是通过将 shell 提示符下输入的 uname -a
(或类似命令)返回的字符串的第一个字段转换为小写并去除标点符号来确定的,或者通过测试文件系统中是否存在唯一命名的文件(例如内核或头文件)来确定的。例如,以下是一些比较流行的 Unix 版本
uname $^O $Config{archname}
--------------------------------------------
AIX aix aix
BSD/OS bsdos i386-bsdos
Darwin darwin darwin
DYNIX/ptx dynixptx i386-dynixptx
FreeBSD freebsd freebsd-i386
Haiku haiku BePC-haiku
Linux linux arm-linux
Linux linux armv5tel-linux
Linux linux i386-linux
Linux linux i586-linux
Linux linux ppc-linux
HP-UX hpux PA-RISC1.1
IRIX irix irix
Mac OS X darwin darwin
NeXT 3 next next-fat
NeXT 4 next OPENSTEP-Mach
openbsd openbsd i386-openbsd
OSF1 dec_osf alpha-dec_osf
reliantunix-n svr4 RM400-svr4
SCO_SV sco_sv i386-sco_sv
SINIX-N svr4 RM400-svr4
sn4609 unicos CRAY_C90-unicos
sn6521 unicosmk t3e-unicosmk
sn9617 unicos CRAY_J90-unicos
SunOS solaris sun4-solaris
SunOS solaris i86pc-solaris
SunOS4 sunos sun4-sunos
由于 $Config{archname}
的值可能取决于硬件架构,因此它的变化可能比 $^O
的值更大。
Perl 长期以来一直移植到运行在 PC-DOS、MS-DOS、OS/2 和大多数你能想到的 Windows 平台(除了 Windows CE,如果你算上它)上的 Intel 风格的微型计算机上。熟悉 COMMAND.COM 或 CMD.EXE 风格 shell 的用户应该注意,这些文件规范中的每一个都可能存在细微的差异
my $filespec0 = "c:/foo/bar/file.txt";
my $filespec1 = "c:\\foo\\bar\\file.txt";
my $filespec2 = 'c:\foo\bar\file.txt';
my $filespec3 = 'c:\\foo\\bar\\file.txt';
系统调用接受 /
或 \
作为路径分隔符。但是,许多 DOS 时代的命令行实用程序将 /
视为选项前缀,因此可能会对包含 /
的文件名感到困惑。除了调用任何外部程序之外,/
可以正常工作,甚至可能更好,因为它与流行用法更加一致,并且避免了记住哪些需要反斜杠转义,哪些不需要的问题。
DOS FAT 文件系统只能容纳 "8.3" 风格的文件名。在 "不区分大小写,但保留大小写" 的 HPFS(OS/2)和 NTFS(NT)文件系统下,你可能需要小心使用 readdir
等函数返回的大小写,或者使用 open
或 opendir
等函数。
DOS 还将一些文件名视为特殊文件名,例如 AUX、PRN、NUL、CON、COM1、LPT1、LPT2 等。不幸的是,有时即使你包含显式的目录前缀,这些文件名也无法正常工作。如果你希望你的代码能够移植到 DOS 及其衍生系统,最好避免使用这些文件名。不幸的是,很难知道它们都是什么。
这些操作系统的用户可能还想使用 pl2bat.bat 等脚本为你的脚本添加包装器。
换行符 (\n
) 在 I/O 系统从文件读取和写入文件时被转换为 \015\012
(参见 "换行符")。binmode($filehandle)
将使 \n
对于该文件句柄保持转换为 \012
。对于处理二进制数据的代码,应始终使用 binmode
。假设你事先知道你的数据是二进制的。通用程序通常不应该假设其数据的任何信息。
各种 DOSish Perl 的 $^O
变量和 $Config{archname}
值如下
OS $^O $Config{archname} ID Version
---------------------------------------------------------
MS-DOS dos ?
PC-DOS dos ?
OS/2 os2 ?
Windows 3.1 ? ? 0 3 01
Windows 95 MSWin32 MSWin32-x86 1 4 00
Windows 98 MSWin32 MSWin32-x86 1 4 10
Windows ME MSWin32 MSWin32-x86 1 ?
Windows NT MSWin32 MSWin32-x86 2 4 xx
Windows NT MSWin32 MSWin32-ALPHA 2 4 xx
Windows NT MSWin32 MSWin32-ppc 2 4 xx
Windows 2000 MSWin32 MSWin32-x86 2 5 00
Windows XP MSWin32 MSWin32-x86 2 5 01
Windows 2003 MSWin32 MSWin32-x86 2 5 02
Windows Vista MSWin32 MSWin32-x86 2 6 00
Windows 7 MSWin32 MSWin32-x86 2 6 01
Windows 7 MSWin32 MSWin32-x64 2 6 01
Windows 2008 MSWin32 MSWin32-x86 2 6 01
Windows 2008 MSWin32 MSWin32-x64 2 6 01
Windows CE MSWin32 ? 3
Cygwin cygwin cygwin
各种 MSWin32 Perl 可以通过 Win32::GetOSVersion()
返回的列表的第五个元素的值来区分它们运行的操作系统。例如
if ($^O eq 'MSWin32') {
my @os_version_info = Win32::GetOSVersion();
print +('3.1','95','NT')[$os_version_info[4]],"\n";
}
还有 Win32::IsWinNT()|Win32/Win32::IsWinNT()
、Win32::IsWin95()|Win32/Win32::IsWin95()
和 Win32::GetOSName()
;尝试 perldoc Win32
。非常便携的 POSIX::uname()
也能工作
c:\> perl -MPOSIX -we "print join '|', uname"
Windows NT|moonru|5.0|Build 2195 (Service Pack 2)|x86
Winsock 函数设置的错误现在直接放入 $^E
中,相关的 WSAE*
错误代码现在从 Errno 和 POSIX 模块导出,用于对此进行测试。
将错误(自 Perl 5.20.0 起转换为 POSIX 风格的 E*
错误代码)放入 $!
的先前行为存在错误,因为 Winsock 和 POSIX 错误常量的同名常量不完全相同,自 Perl 5.8.0 起,这种关系以某种方式建立起来。
新行为为在便携式软件中检查 Winsock 错误提供了一个更健壮的解决方案,而不会意外地匹配为其他操作系统设计的 POSIX 测试,这些测试可能对 Winsock 具有不同的含义。
为了向后兼容,目前保留了旧行为,包括所有缺点,但鼓励用户将任何测试 $!
与 E*
常量(用于 Winsock 错误)的代码更改为改为测试 $^E
与 WSAE*
常量。在适当的弃用期后(从 Perl 5.24 开始),旧行为可能会被删除,在 Winsock 函数调用后 $!
保持不变,以避免对要检查哪个错误变量产生任何可能的混淆。
另请参阅
用于 DOS、OS/2 等的 EMX 环境 [email protected],ftp://hobbes.nmsu.edu/pub/os2/dev/emx/ 还有 perlos2。
Win32 的构建说明在 perlwin32 中,或在 Cygnus 环境下在 perlcygwin 中。
Win32 中的 Win32::*
模块。
ActiveState 页面,https://www.activestate.com/
用于 Win32 的 Cygwin 环境;README.cygwin(安装为 perlcygwin),https://www.cygwin.com/
OS/2 的构建说明,perlos2
Perl 在 VMS 上的讨论在 Perl 发行版中的 perlvms 中。
截至本文撰写之时,VMS 的官方名称为 OpenVMS。
从 Digital Command Language (DCL) shell 与 Perl 交互通常需要与 Unix shell 不同的引号集。例如
$ perl -e "print ""Hello, world.\n"""
Hello, world.
如果您愿意,有几种方法可以将您的 Perl 脚本包装在 DCL .COM 文件中。例如
$ write sys$output "Hello from DCL!"
$ if p1 .eqs. ""
$ then perl -x 'f$environment("PROCEDURE")
$ else perl -x - 'p1 'p2 'p3 'p4 'p5 'p6 'p7 'p8
$ deck/dollars="__END__"
#!/usr/bin/perl
print "Hello from Perl!\n";
__END__
$ endif
如果您的 Perl-in-DCL 脚本期望执行诸如 $read = <STDIN>;
之类的事情,请注意 $ ASSIGN/nolog/user SYS$COMMAND: SYS$INPUT
。
VMS 操作系统有两个文件系统,由它们在磁盘上的结构 (ODS) 级别指定:ODS-2 及其后继者 ODS-5。Perl 对 VMS 的初始移植早于 ODS-5,但所有当前测试和开发都假设 ODS-5 及其功能,包括大小写保留、文件规范中的扩展字符以及最长 8192 字节的名称。
VMS 上的 Perl 可以接受 VMS 或 Unix 风格的文件规范,例如以下两种:
$ perl -ne "print if /perl_setup/i" SYS$LOGIN:LOGIN.COM
$ perl -ne "print if /perl_setup/i" /sys$login/login.com
但不能混合使用,例如:
$ perl -ne "print if /perl_setup/i" sys$login:/login.com
Can't open sys$login:/login.com: file specification syntax error
一般来说,除非需要由本机命令或实用程序处理,否则始终以 Unix 格式指定文件名是最容易移植的方法。由于后者的考虑,File::Spec 模块默认情况下会返回本机格式规范,无论输入格式如何。可以通过在环境中指定 DECC$FILENAME_UNIX_REPORT
特性逻辑名称来反转此默认设置,以便始终以 Unix 格式报告文件名。
文件类型或扩展名始终存在于 VMS 格式的文件规范中,即使它为零长度。这意味着,默认情况下,readdir
会在没有扩展名的文件上返回一个尾随点,因此在 Unix 上你会看到 "a"
,而在 VMS 上你会看到 "a."
。但是,可以通过在环境中启用 DECC$READDIR_DROPDOTNOTYPE
特性来抑制尾随点(请参阅 CRTL 文档中的特性逻辑名称)。
\n
代表什么取决于打开的文件类型。它通常代表 \012
,但它也可能是 \015
、\012
、\015\012
、\000
、\040
或根据文件组织和记录格式的不同而不同。该 VMS::Stdio
模块提供对 VMS 上具有不寻常属性的文件的特殊 fopen()
要求的访问。
OpenVMS 上 $^O
的值为 "VMS"。要确定您正在运行的架构,请参考 $Config{archname}
。
在 VMS 上,perl 从 SYS$TIMEZONE_DIFFERENTIAL
逻辑名称确定 UTC 偏移量。虽然 VMS 纪元始于 17-NOV-1858 00:00:00.00,但对 localtime
的调用会调整为从 01-JAN-1970 00:00:00.00 开始计算偏移量,就像 Unix 一样。
另请参阅
README.vms(安装为 README_vms),perlvms
vmsperl 列表,[email protected]
网络上的 vmsperl,http://www.sidhe.org/vmsperl/index.html
VMS Software Inc. 网站,http://www.vmssoftware.com
Perl 在 VOS(也称为 OpenVOS)上的讨论在 Perl 发行版中的 README.vos 中(安装为 perlvos)。Perl 在 VOS 上可以接受 VOS 或 Unix 风格的文件规范,例如以下两种:
$ perl -ne "print if /perl_setup/i" >system>notices
$ perl -ne "print if /perl_setup/i" /system/notices
甚至可以混合使用,例如:
$ perl -ne "print if /perl_setup/i" >system/notices
即使 VOS 允许在对象名称中出现斜杠字符,但由于 Perl 的 VOS 端口将其解释为路径名分隔符,因此无法处理名称包含斜杠字符的 VOS 文件、目录或链接。此类文件必须在被 Perl 处理之前重命名。
旧版本的 VOS(OpenVOS Release 17.0 之前)将文件名限制为 32 个字符或更少,禁止文件名以 -
字符开头,并禁止文件名包含
(空格)或来自集合 !#%&'()*;<=>?
的任何字符。
VOS 的较新版本(OpenVOS 17.0 或更高版本)支持称为扩展名称的功能。在这些版本中,文件名最多可以包含 255 个字符,禁止以 -
字符开头,并且禁止使用的字符集减少为 #%*<>?
。空格和撇号存在限制:这些字符不能位于名称的开头或结尾,也不能紧接在句点之前或之后。此外,空格不能紧接在另一个空格或连字符之前。具体来说,以下字符组合是被禁止的:空格-空格、空格-连字符、句点-空格、空格-句点、句点-撇号、撇号-句点、开头或结尾的空格以及开头或结尾的撇号。虽然扩展文件名限制为 255 个字符,但路径名仍然限制为 256 个字符。
VOS 上 $^O
的值为 "vos"。要确定您正在运行的架构,请参考 $Config{archname}
。
另请参阅
README.vos(安装为 perlvos)
VOS 邮件列表。
VOS 上没有专门针对 Perl 的邮件列表。您可以联系您所在地区的 Stratus Technologies 客户协助中心 (CAC),或者使用 Stratus 匿名 FTP 站点上的分发文件中的联系信息。
Stratus Technologies 网站:http://www.stratus.com
VOS 开源软件网站:http://ftp.stratus.com/pub/vos/vos.html
v5.22 核心 Perl 在 z/OS(以前称为 OS/390)上运行。理论上它也可以在 AS/400 小型机的 OS/400 后继者以及 VM/ESA 和 S/390 大型机的 BS2000 上运行。这些计算机在内部使用 EBCDIC 字符集(通常是 OS/400 的字符代码集 ID 0037,以及 S/390 系统的 1047 或 POSIX-BC)。
本节的其余部分可能需要更新,但我们不知道应该写什么。请将评论提交到 https://github.com/Perl/perl5/issues。
在大型机上,Perl 目前在“OS/390 的 Unix 系统服务”(以前称为 OpenEdition)、VM/ESA OpenEdition 或 BS200 POSIX-BC 系统(BS2000 在 Perl 5.6 及更高版本中受支持)下运行。有关详细信息,请参阅 perlos390。请注意,对于 OS/400,Perl 5.8.1/5.10.0 或更高版本也移植到了基于 ASCII 的 PASE(与基于 EBCDIC 的 ILE 相反),请参阅 perlos400。
从 OS/390 的 USS R2.5 和 VM/ESA 的 2.3 版本开始,这些 Unix 子系统不支持用于脚本调用的 #!
shebang 技巧。因此,在 OS/390 和 VM/ESA 上,Perl 脚本可以使用类似于以下简单脚本的标头执行
: # use perl
eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}'
if 0;
#!/usr/local/bin/perl # just a comment really
print "Hello from perl!\n";
OS/390 将在 2.8 及更高版本中支持 #!
shebang 技巧。对 system
和反引号的调用可以在所有 S/390 系统上使用 POSIX shell 语法。
在 AS/400 上,如果您的库列表中包含 PERL5,您可能需要将 Perl 脚本包装在 CL 过程中,以便像这样调用它们
BEGIN
CALL PGM(PERL5/PERL) PARM('/QOpenSys/hello.pl')
ENDPGM
这将在 QOpenSys 文件系统的根目录中调用 Perl 脚本 hello.pl。在 AS/400 上调用 system
或反引号必须使用 CL 语法。
在这些平台上,请记住 EBCDIC 字符集可能会影响某些 Perl 函数的行为(例如 chr
、pack
、print
、printf
、ord
、sort
、sprintf
、unpack
),以及使用 ^
、&
和 |
等运算符对 ASCII 常量进行位操作,更不用说处理与 ASCII 计算机的套接字接口了(参见 "换行符")。
幸运的是,大多数大型机上的 Web 服务器会将以下语句中的 \n
正确转换为其 ASCII 等效项(\r
在 Unix 和 z/OS 下都是一样的)
print "Content-type: text/html\r\n\r\n";
以下是一些平台上 $^O
的值
uname $^O $Config{archname}
--------------------------------------------
OS/390 os390 os390
OS400 os400 os400
POSIX-BC posix-bc BS2000-posix-bc
以下是一些简单的技巧,可以用来确定您是否在 EBCDIC 平台上运行(可能全部)
if ("\t" eq "\005") { print "EBCDIC may be spoken here!\n"; }
if (ord('A') == 193) { print "EBCDIC may be spoken here!\n"; }
if (chr(169) eq 'z') { print "EBCDIC may be spoken here!\n"; }
您可能不想依赖的一件事是标点符号的 EBCDIC 编码,因为这些编码可能因代码页而异(一旦您的模块或脚本被传言可以与 EBCDIC 一起使用,人们就会希望它可以与所有 EBCDIC 字符集一起使用)。
另请参阅
[email protected] 列表用于讨论移植问题以及所有 EBCDIC Perl 的一般使用问题。向 [email protected] 发送包含“subscribe perl-mvs”的消息正文。
AS/400 Perl 信息可在 http://as400.rochester.ibm.com/ 以及 CPAN 的 ports/ 目录中找到。
由于 Acorn 使用 ASCII,文本文件中的换行符 (\n
) 为 \012
,就像 Unix 一样,并且由于 Unix 文件名模拟默认情况下处于打开状态,因此大多数简单的脚本可能“开箱即用”。原生文件系统是模块化的,各个文件系统可以自由地区分大小写或不区分大小写,并且通常保留大小写。一些原生文件系统对文件名长度有限制,文件名和目录名会被静默截断以适应限制。脚本应该意识到,标准文件系统目前对文件名长度的限制为 **10** 个字符,目录中最多可以有 77 个项目,但其他文件系统可能不会施加此类限制。
原生文件名格式如下
Filesystem#Special_Field::DiskName.$.Directory.Directory.File
其中
Special_Field is not usually present, but may contain . and $ .
Filesystem =~ m|[A-Za-z0-9_]|
DsicName =~ m|[A-Za-z0-9_/]|
$ represents the root directory
. is the path separator
@ is the current directory (per filesystem but machine global)
^ is the parent directory
Directory and File =~ m|[^\0- "\.\$\%\&:\@\\^\|\177]+|
默认的文件名转换大约是 tr|/.|./|
,交换点和斜杠。
请注意 "ADFS::HardDisk.$.File" ne 'ADFS::HardDisk.$.File'
,并且正则表达式中 $
插值的第二阶段如果脚本不谨慎,将会遇到 $.
变量的问题。
系统变量指定的逻辑路径,包含逗号分隔的搜索列表,也是允许的;因此 System:Modules
是一个有效的文件名,文件系统会将 Modules
与 System$Path
的每个部分前缀,直到找到一个指向磁盘上对象的名称。只有当 System$Path
包含一个单项列表时,才能写入新文件 System:Modules
。文件系统还会在文件名中扩展系统变量,如果它们包含在尖括号中,那么 <System$Dir>.Modules
将查找文件 $ENV{'System$Dir'} . 'Modules'
。这显然意味着 **完全限定的文件名可以以 <>
开头**,并且 open
的三参数形式应该始终使用。
由于 .
被用作目录分隔符,并且文件名在 10 个字符后不能假定为唯一,Acorn 实现的 C 编译器会从源代码中指定的文件名中剥离尾部的 .c
.h
.s
和 .o
后缀,并将相应的文件存储在以后缀命名的子目录中。因此,文件被翻译为
foo.h h.foo
C:foo.h C:h.foo (logical path variable)
sys/os.h sys.h.os (C compiler groks Unix-speak)
10charname.c c.10charname
10charname.o o.10charname
11charname_.c c.11charname (assuming filesystem truncates at 10)
Unix 模拟库将文件名转换为本地的翻译假设需要这种类型的转换,并且它允许用户定义已知后缀的列表,它将以这种方式进行转置。这看起来很透明,但请考虑,根据这些规则,foo/bar/baz.h 和 foo/bar/h/baz 都映射到 foo.bar.h.baz,并且 readdir
和 glob
不能也不尝试模拟反向映射。文件名中的其他 .
将被转换为 /
。
如上所述,通过 %ENV
访问的环境是全局的,约定是程序特定的环境变量采用 Program$Name
的形式。每个文件系统都维护一个当前目录,当前文件系统的当前目录是全局当前目录。因此,友好的程序不会更改当前目录,而是依赖于完整路径名,并且程序(和 Makefile)不能假设它们可以生成一个子进程,该子进程可以更改当前目录而不会影响其父进程(以及其他所有人)。
由于本机操作系统文件句柄是全局的,并且当前从 255 向下分配,其中 0 是保留值,因此 Unix 模拟库模拟了 Unix 文件句柄。因此,您不能依赖于将 STDIN
、STDOUT
或 STDERR
传递给您的子进程。
用户希望在命令行上表达 <Foo$Dir>.Bar
形式的文件名,但未加引号会导致问题:``
命令输出捕获必须执行猜测游戏。它假设字符串 <[^<>]+\$[^<>]>
是对环境变量的引用,而任何其他涉及 <
或 >
的内容都是重定向,并且通常能够做到 99% 正确。当然,问题仍然存在,即脚本不能依赖于任何 Unix 工具可用,或者任何找到的工具具有类似 Unix 的命令行参数。
理论上,任何人都可以使用免费工具构建扩展和 XS。实际上,许多人没有这样做,因为 Acorn 平台的用户习惯于二进制分发。MakeMaker 确实运行,但目前没有可用的 make 能够处理 MakeMaker 的 makefile;即使并且当这应该得到修复时,缺少类似 Unix 的 shell 也会导致 makefile 规则出现问题,尤其是 cd sdbm && make all
形式的行,以及任何使用引号的内容。
"RISC OS" 是操作系统的正式名称,但在 $^O
中的值是 "riscos"(因为我们不喜欢喊叫)。
Perl 已移植到许多平台,这些平台不属于上述任何类别。一些平台,如 AmigaOS、QNX、Plan 9 和 VOS,已很好地集成到标准 Perl 源代码包中。您可能需要查看 CPAN 上的 ports/ 目录以获取信息,以及可能针对以下平台的二进制文件:aos、Atari ST、lynxos、riscos、Novell Netware、Tandem Guardian 等。(是的,我们知道其中一些操作系统可能属于 Unix 类别,但我们不是标准机构。)
一些近似操作系统名称及其在“其他”类别中的 $^O
值包括
OS $^O $Config{archname}
------------------------------------------
Amiga DOS amigaos m68k-amigos
另请参阅
Amiga,README.amiga(安装为 perlamiga)。
Plan 9,README.plan9
下面列出了在各种平台上完全未实现或实现方式不同的函数。每个描述之前将用括号列出该描述适用的平台。
此列表可能不完整,甚至在某些地方可能不正确。如有疑问,请查阅 Perl 源代码分发中的特定于平台的 README 文件,以及任何其他与特定端口相关的文档资源。
此外,请注意,即使在类 Unix 系统中也存在差异。
对于许多函数,您还可以查询 %Config
,它默认情况下由 Config
模块导出。例如,要检查平台是否具有 lstat
调用,请检查 $Config{d_lstat}
。有关可用变量的完整描述,请参阅 Config。
(Win32) -w
仅检查只读文件属性 (FILE_ATTRIBUTE_READONLY),该属性决定是否可以删除目录,而不是是否可以写入目录。目录始终具有读写访问权限,除非被自由访问控制列表 (DACL) 拒绝。
(VMS) -r
、-w
、-x
和 -o
指示文件是否可访问,这可能不反映基于 UIC 的文件保护。
(RISC OS) 对打开文件的 -s
按名称将返回磁盘上保留的空间,而不是当前范围。对打开的文件句柄的 -s
返回当前大小。
(Win32、VMS、RISC OS) -R
、-W
、-X
、-O
与 -r
、-w
、-x
、-o
无区别。
(Win32, VMS, RISC OS) -g
、-k
、-l
、-u
、-A
这些选项没有特别的意义。
(Win32) -l
对符号链接和目录连接都返回真值。
(VMS, RISC OS) -p
没有特别的意义。
(VMS) 如果传递一个没有显式目录的设备规范,则 -d
为真。
(Win32) -x
(或 -X
)判断文件是否以可执行文件后缀结尾。-S
没有意义。
(RISC OS) -x
(或 -X
)判断文件是否具有可执行文件类型。
(Win32) 使用计时器模拟,这些计时器必须在 Perl 想要分发“安全信号”时显式轮询,因此无法中断阻塞系统调用。
(Tru64, HP-UX 10.20) 由于各种 CPU、数学库、编译器和标准存在问题,atan2
的结果可能会因上述任何组合而异。Perl 尝试符合 Open Group/IEEE 标准,以返回 atan2
的结果,但如果运行 Perl 的系统不允许,则无法强制执行。
atan2
标准的当前版本可在 http://www.opengroup.org/onlinepubs/009695399/functions/atan2.html 获取。
(RISC OS) 没有意义。
(VMS) 重新打开文件并恢复指针;如果函数失败,底层文件句柄可能被关闭,或者指针可能处于不同的位置。
(Win32) 调用后,tell
返回的值可能会受到影响,并且文件句柄可能会被刷新。
(Win32) 系统报告的当前目录可能包含为 chdir() 指定的任何符号链接。
(Win32) 仅适用于更改“所有者”读写权限;“组”和“其他”位没有意义。
(RISC OS) 仅适用于更改“所有者”和“其他”读写权限。
(VOS) 访问权限映射到 VOS 访问控制列表更改。
(Cygwin) 实际设置的权限取决于 SYSTEM 环境设置中 CYGWIN
变量的值。
(Android) 在某些位置(通常是 /sdcard)设置执行位将返回真值,但实际上不会设置该位。
(VMS) 模式参数为零时,将权限设置为用户的默认权限掩码,而不是禁用所有权限。
(Plan 9, RISC OS) 未实现。
(Win32) 不执行任何操作,但不会失败。
(VOS) 有点奇怪,因为 VOS 对所有权的概念有点奇怪。
(Win32, VMS, Plan 9, RISC OS, VOS) 未实现。
(Win32) 如果在构建 Perl 时未提供库或源代码,则可能不可用。
(Android) 未实现。
(VMS, Plan 9, VOS) 未实现。
(VMS, Plan 9, VOS) 未实现。
(RISC OS) 无用。
(Cygwin, Win32) 不支持。
(VMS) 调用 VMS 调试器。
(Win32) exec LIST
不使用间接对象语法 (exec PROGRAM LIST
) 可能会在第一次 spawn()
失败时回退到尝试使用 shell。
请注意,exec() 的列表形式是模拟的,因为 Win32 API CreateProcess() 接受一个简单的字符串而不是一个命令行参数数组。这可能会对您的代码造成安全隐患。
(SunOS, Solaris, HP-UX) 在某些平台上不会自动刷新输出句柄。
(VMS) 通过将 1
映射到 SS$_ABORT
(44
) 来模拟 Unix exit
(它认为 exit 1
表示错误)。此行为可以通过 pragma use vmsish 'exit'
覆盖。与 CRTL 的 exit()
函数一样,exit 0
也映射到 SS$_NORMAL
(1
) 的退出状态;此映射不可覆盖。exit
的任何其他参数都将直接用作 Perl 的退出状态。在 VMS 上,除非启用了未来的 POSIX_EXIT 模式,否则退出代码应始终是有效的 VMS 退出代码,而不是通用数字。当启用 POSIX_EXIT 模式时,通用数字将以与 C 库 _POSIX_EXIT 宏兼容的方式进行编码,以便其他程序(尤其是用 C 编写的程序,如 GNV 包)可以对其进行解码。
(Solaris) exit
重置文件指针,这在从子进程(由 fork
创建)在 BEGIN
中调用时会出现问题。解决方法是使用 POSIX::_exit
.
exit unless $Config{archname} =~ /\bsolaris\b/;
require POSIX;
POSIX::_exit(0);
(Win32) 未实现。
(VMS) 一些函数根据 VMS 版本可用。
(VMS, RISC OS, VOS) 未实现。
(AmigaOS, RISC OS, VMS) 未实现。
(Win32) 使用多个解释器模拟。参见 perlfork.
(SunOS, Solaris, HP-UX) 在某些平台上不会自动刷新输出句柄。
(RISC OS) 未实现。
(Win32, VMS, RISC OS) 未实现。
(Win32, RISC OS) 未实现。
(Win32, VMS, RISC OS, VOS) 未实现。
(Win32) 未实现。
(RISC OS) 无用。
(Win32, VMS, RISC OS) 未实现。
(Android, Win32, Plan 9) 未实现。
(Win32) 未实现。
(RISC OS) 无用。
(Win32, VMS, RISC OS) 未实现。
(Android, Win32, Plan 9) 未实现。
(Android) 未实现。
(Android, Win32) 未实现。
(Android, Win32, VMS) 未实现。
(Irix 5) gethostbyname('localhost')
在某些情况下无法正常工作:您可能需要使用 gethostbyname('127.0.0.1')
。
(Win32) 未实现。
(Android, Win32, Plan 9) 未实现。
(Android, Win32, Plan 9) 未实现。
(Win32, Plan 9) 未实现。
(Android) 未实现。
(Android, Win32, Plan 9, RISC OS) 未实现。
(Win32, Plan 9, RISC OS) 未实现。
(Android, Win32, Plan 9, RISC OS) 未实现。
(Plan 9, Win32, RISC OS) 未实现。
(Win32) 未实现。
(Android) 未实现或为空操作。
(Android, RISC OS, VMS, Win32) 未实现。
(Android, Win32) 未实现。
(Android, Win32, Plan 9) 未实现。
(Android, Win32, Plan 9) 未实现。
(Plan 9, Win32) 未实现。
(Plan 9) 未实现。
此操作符在大多数平台上通过 File::Glob
扩展实现。有关可移植性信息,请参阅 File::Glob。
理论上,gmtime
在 -2**63 到 2**63-1 之间是可靠的。但是,由于实现中的解决方法使用浮点数,因此随着时间的推移,它会变得不准确。这是一个错误,将在未来修复。
(VOS) 时间值是 32 位量。
(VMS) 未实现。
(Win32) 仅适用于套接字句柄,它执行 Winsock API 中的 ioctlsocket()
调用所执行的操作。
(RISC OS) 仅适用于套接字句柄。
(RISC OS) 未实现,因此对污染检查没有用。
(Win32) kill
不会像在 Unix 平台上那样向标识的进程发送信号。相反,kill($sig, $pid)
会终止由 $pid
标识的进程,并使其立即退出,退出状态为 $sig
。与 Unix 一样,如果 $sig
为 0 且指定的进程存在,它将返回 true 而不实际终止它。
(Win32) kill(-9, $pid)
将终止由 $pid
指定的进程及其拥有的所有子进程。这与 Unix 语义不同,在 Unix 语义中,信号将传递给与由 $pid
指定的进程位于同一进程组中的所有进程。
(VMS) 目前不支持指示系统上所有进程的 -1 pid。
(RISC OS, VOS) 未实现。
(AmigaOS) 链接计数未更新,因为硬链接并不完全是硬链接(它们有点介于硬链接和软链接之间)。
(Win32) 硬链接在 Win32 上仅在 NTFS 下实现。它们在 Windows 2000 及更高版本中得到原生支持。在 Windows NT 上,它们是使用 Windows POSIX 子系统支持实现的,Perl 进程将需要管理员或备份操作员权限才能创建硬链接。
(VMS) 在 64 位 OpenVMS 8.2 及更高版本上可用。
localtime
的范围与 "gmtime" 相同,但由于时区规则会发生变化,因此它对历史时间和未来时间的准确性可能会下降,但通常不会超过一个小时。
(RISC OS) 未实现。
(Win32) 将目录连接视为符号链接。
(Android, Win32, VMS, Plan 9, RISC OS, VOS) 未实现。
(RISC OS) 不支持打开模式 |-
和 -|
。
(SunOS, Solaris, HP-UX) 在某些平台上,打开进程不会自动刷新输出句柄。
(Win32) |-
和 -|
两种模式都支持,但列表形式是模拟的,因为 Win32 API CreateProcess() 接受一个简单的字符串而不是一个参数数组。这可能会对您的代码造成安全隐患。
(VMS, RISC OS) 未实现。
(Win32) 在目录连接上调用 readlink() 会返回对象名称,而不是简单的路径。
(Win32) 无法在不同的逻辑卷上将目录从一个目录移动到另一个目录。
(Win32) 不会导致 readdir
重新读取目录流。在 rewinddir
调用之前已读取的条目将从缓存缓冲区中再次返回。
(Win32, VMS) 仅在套接字上实现。
(RISC OS) 仅在套接字上可靠。
请注意,select FILEHANDLE
形式通常是可移植的。
(Android, Win32, VMS, RISC OS) 未实现。
(Android, VMS, Win32, RISC OS) 未实现。
(Win32, VMS, RISC OS, VOS) 未实现。
(Win32, VMS, RISC OS, VOS) 未实现。
(Android, Win32, RISC OS) 未实现。
(Plan 9) 未实现。
(Android, Win32, VMS, RISC OS) 未实现。
(Win32) 使用同步函数模拟,因此可以被 alarm
中断,并且限制为最大 4294967 秒,大约 49 天。
(RISC OS) 未实现。
(VMS) 在 64 位 OpenVMS 8.2 及更高版本上可用。
没有 rdev
、blksize
或 blocks
的平台将返回这些字段为 ''
,因此对这些字段进行数值比较或操作可能会导致“非数值”警告。
(Mac OS X) ctime
在 UFS 上不支持。
(Win32) ctime
是创建时间,而不是 inode 修改时间。
(VMS) dev
和 ino
不一定可靠。
(RISC OS) mtime
、atime
和 ctime
都返回最后修改时间。dev
和 ino
不一定可靠。
(OS/2) dev
、rdev
、blksize
和 blocks
不可用。ino
没有意义,并且在同一个文件的 stat 调用之间会不同。
(Cygwin) 一些版本的 cygwin 在执行 stat("foo")
并且没有找到它时,可能会尝试执行 stat("foo.exe")
。
(RISC OS) 未实现。
(Win32) 需要提升的权限或开发者模式以及足够新的 Windows 10 版本。可以使用 Win32::IsSymlinkCreationAllowed() 函数检查当前进程是否具有所需的权限。
由于 Windows 在创建链接时需要知道目标是目录还是文件,因此目标 Perl 只有在目标存在并且是目录时才会创建链接作为目录链接。
Windows 不识别符号链接中的正斜杠作为路径分隔符。因此,在 Windows 上,symlink() 的 OLDFILE 参数中的任何 /
都将转换为 \
。这反映在 readlink() 返回的结果中,结果中的 \
不会转换回 /
。
(VMS) 在 64 位 VMS 8.3 上实现。如果符号链接旨在解析为有效路径,则 VMS 要求符号链接采用 Unix 语法。
(Win32, VMS, RISC OS, VOS) 未实现。
(Mac OS, OS/390) 传统的 0
、1
和 2
MODES 在某些系统上使用不同的数值实现。由 Fcntl
(O_RDONLY
、O_WRONLY
、O_RDWR
)导出的标志应该在所有地方都能正常工作。
(Win32) 作为优化,可能不会调用 $ENV{PERL5SHELL}
中指定的命令 shell。system(1, @args)
生成一个外部进程并立即返回其进程标识符,而不会等待它终止。返回值可以在后续的 wait
或 waitpid
中使用。无法 spawn()
子进程将通过将 $?
设置为 255 << 8
来指示。$?
的设置方式与 Unix 兼容(即,子进程的退出状态通过 $? >> 8
获得,如文档中所述)。
请注意,system() 的列表形式是模拟的,因为 Win32 API CreateProcess() 接受一个简单的字符串而不是一个命令行参数数组。这可能会对您的代码造成安全隐患。
(RISC OS) 没有 shell 来处理元字符,并且本机标准是将以“\n” “\r” 或 “\0” 结尾的命令行传递给生成的程序。重定向(如果有的话)由生成的程序的运行时库执行。system LIST
将调用 Unix 模拟库的 exec
模拟,该模拟尝试提供对父进程中有效的 stdin、stdout、stderr 的模拟,前提是子程序使用兼容版本的模拟库。system SCALAR
将直接调用本机命令行,并且不会发生这种对子 Unix 程序的模拟。效果会有所不同。
(Win32) system LIST
在不使用间接对象语法(system PROGRAM LIST
)的情况下,如果第一次 spawn()
失败,可能会回退到尝试使用 shell。
(SunOS, Solaris, HP-UX) 在某些平台上不会自动刷新输出句柄。
(VMS) 与 Win32 一样,system(1, @args)
生成一个外部进程并立即返回其进程标识符,而不会等待进程终止。在这种情况下,返回值可以在后续的 wait
或 waitpid
中使用。否则,返回值是 POSIX 样式的(向上移位 8 位),这只会为从本机 32 位条件代码的严重性位派生的虚构值留出空间(除非被 use vmsish 'status'
覆盖)。如果本机条件代码是编码了 POSIX 值的代码,则将解码 POSIX 值以提取预期的退出值。有关更多详细信息,请参阅 "$?" in perlvms。
(Android) 未实现。
(Win32) "累计" 时间将是错误的。在除 Windows NT 或 Windows 2000 之外的任何系统上,"系统" 时间将是错误的,而 "用户" 时间实际上是 C 运行时库中 clock()
函数返回的时间。
(RISC OS) 无用。
(旧版本的 VMS) 未实现。
(VOS) 只能截断为相同或更短的长度。
(Win32) 如果提供 FILEHANDLE,它必须是可写的,并且以追加模式打开(即,使用 open(my $fh, '>>', 'filename')
或 sysopen(my $fh, ..., O_APPEND|O_RDWR)
)。如果提供文件名,则它不应在其他地方保持打开状态。
在不可用时返回 undef
。
(AmigaOS) umask
有效,但只有在文件最终关闭时才会设置正确的权限。
(VMS, RISC OS) 仅更新修改时间。
(Win32) 可能无法按预期工作。行为取决于 C 运行时库对 utime()
的实现,以及所使用的文件系统。FAT 文件系统通常不支持 "访问时间" 字段,并且它可能将时间戳限制为两秒的粒度。
(Win32) 只能应用于为使用 system(1, ...)
生成的进程或使用 fork
创建的伪进程返回的进程句柄。
(RISC OS) 无用。
以下平台已知可以从 http://www.cpan.org/src 提供的标准源代码分发版构建 Perl 5.12(截至 2010 年 4 月,其发布日期)。
已知某些测试会失败
ext/XS-APItest/t/call_checker.t - 请参阅 https://github.com/Perl/perl5/issues/10750
dist/I18N-Collate/t/I18N-Collate.t
ext/Win32CORE/t/win32core.t - 最近的 cygwin 安装可能会失败。
注意事项
Perl 现在可以使用 FreeMiNT/Atari 构建。它会失败一些测试,需要一些调查。
FreeMiNT 端口使用 GNU dld 来实现可加载模块功能。因此,请确保在构建 perl 时已安装该库。
以下平台在 Perl 的先前版本中受支持,但从 5.37.1 开始已从 Perl 的源代码中正式删除。
以下平台在 Perl 的先前版本中受支持,但从 5.36 开始已从 Perl 的源代码中正式删除。
以下平台在 Perl 的先前版本中受支持,但自 5.20 起已从 Perl 的源代码中正式删除。
以下平台在 5.10 之前受支持。它们可能在 5.12 中仍然有效,但支持代码已在 5.14 中删除。
以下平台在 Perl 的先前版本中受支持,但自 5.12 起已从 Perl 的源代码中正式删除。
截至 2002 年 7 月(Perl 版本 5.8.0),以下平台能够从 http://www.cpan.org/src/ 提供的标准源代码发行版构建 Perl。
AIX
BeOS
BSD/OS (BSDi)
Cygwin
DG/UX
DOS DJGPP 1)
DYNIX/ptx
EPOC R5
FreeBSD
HI-UXMPP (Hitachi) (5.8.0 worked but we didn't know it)
HP-UX
IRIX
Linux
Mac OS Classic
Mac OS X (Darwin)
MPE/iX
NetBSD
NetWare
NonStop-UX
ReliantUNIX (formerly SINIX)
OpenBSD
OpenVMS (formerly VMS)
Open UNIX (Unixware) (since Perl 5.8.1/5.9.0)
OS/2
OS/400 (using the PASE) (since Perl 5.8.1/5.9.0)
POSIX-BC (formerly BS2000)
QNX
Solaris
SunOS 4
SUPER-UX (NEC)
Tru64 UNIX (formerly DEC OSF/1, Digital UNIX)
UNICOS
UNICOS/mk
UTS
VOS / OpenVOS
Win95/98/ME/2K/XP 2)
WinCE
z/OS (formerly OS/390)
VM/ESA
1) in DOS mode either the DOS or OS/2 ports can be used
2) compilers: Borland, MinGW (GCC), VC6
以下平台在之前的版本 (5.6 和 5.7) 中有效,但我们没有在 5.8.0 版本发布之前及时修复或测试这些平台。这些平台中的许多很可能在 5.8.0 中正常工作。
BSD/OS
DomainOS
Hurd
LynxOS
MachTen
PowerMAX
SCO SV
SVR4
Unixware
Windows 3.1
已知在 5.8.0 中存在问题(但可以使用 5.6.1 和 5.7.2)
AmigaOS 3
以下平台过去 (5.005_03 及更早版本) 已知可以从源代码构建 Perl,但我们无法验证它们在当前版本中的状态,原因是硬件/软件平台稀有,或者我们没有这些平台的活跃维护者,或者两者兼而有之。不过,它们过去是有效的,所以请尝试编译它们,并将任何问题告知 https://github.com/Perl/perl5/issues。
3b1
A/UX
ConvexOS
CX/UX
DC/OSx
DDE SMES
DOS EMX
Dynix
EP/IX
ESIX
FPS
GENIX
Greenhills
ISC
MachTen 68k
MPC
NEWS-OS
NextSTEP
OpenSTEP
Opus
Plan 9
RISC/os
SCO ODT/OSR
Stellar
SVR2
TI1500
TitanOS
Unisys Dynix
以下平台有自己的源代码发行版和二进制文件,可通过 http://www.cpan.org/ports/ 获取。
Perl release
OS/400 (ILE) 5.005_02
Tandem Guardian 5.004
以下平台仅提供通过 http://www.cpan.org/ports/index.html 获取的二进制文件。
Perl release
Acorn RISCOS 5.005_02
AOS 5.002
LynxOS 5.004_02
虽然我们建议您始终从源代码构建自己的 Perl,以获得最大的可配置性和安全性,但如果您时间紧迫,可以查看 http://www.cpan.org/ports/index.html 以获取二进制发行版。
perlaix、perlamiga、perlbs2000、perlcygwin、perlebcdic、perlfreebsd、perlhurd、perlhpux、perlirix、perlmacosx、perlos2、perlos390、perlos400、perlplan9、perlqnx、perlsolaris、perltru64、perlunicode、perlvms、perlvos、perlwin32 和 Win32。
Abigail <[email protected]>、Charles Bailey <[email protected]>、Graham Barr <[email protected]>、Tom Christiansen <[email protected]>、Nicholas Clark <[email protected]>、Thomas Dorner <[email protected]>、Andy Dougherty <[email protected]>、Dominic Dunlop <[email protected]>、Neale Ferguson <[email protected]>、David J. Fiander <[email protected]>、Paul Green <[email protected]>、M.J.T. Guy <[email protected]>、Jarkko Hietaniemi <[email protected]>、Luther Huffman <[email protected]>、Nick Ing-Simmons <[email protected]>、Andreas J. König <[email protected]>、Markus Laker <[email protected]>、Andrew M. Langmead <[email protected]>、Lukas Mai <[email protected]>、Larry Moore <[email protected]>、Paul Moore <[email protected]>、Chris Nandor <[email protected]>、Matthias Neeracher <[email protected]>、Philip Newton <[email protected]>、Gary Ng <[email protected]>、Tom Phoenix <[email protected]>、André Pirard <[email protected]>、Peter Prymmer <[email protected]>、Hugo van der Sanden <[email protected]>、Gurusamy Sarathy <[email protected]>、Paul J. Schinder <[email protected]>、Michael G Schwern <[email protected]>、Dan Sugalski <[email protected]>、Nathan Torkington <[email protected]>、John Malmberg <[email protected]>