perlvms - Perl 的 VMS 特定文档
下面收集了描述 Perl 5 在 VMS 上行为的详细信息的说明。它们是对常规 Perl 5 文档的补充,因此我们重点关注 Perl 5 在 VMS 下与在 Unix 下不同的功能,以及 Perl 与其他操作系统的交互。我们没有尝试从 Perl 分发版的 [.pod] 子目录中找到的主 Perl 文档中复制 Perl 功能的完整说明。
我们希望这些说明能够在 VMS 上编写 Perl 脚本时避免您感到困惑和失眠。如果您发现我们遗漏了您认为应该出现在此处的某些内容,请随时向 [email protected] 发送电子邮件。
构建和安装 Perl 5 的说明可以在 Perl 发行版主源目录中的文件 README.vms 中找到。
在构建过程中,会生成三个 Perl 映像。Miniperl.Exe 是一个可执行映像,它包含 Perl 的所有基本功能,但无法利用 Perl XS 扩展,并且具有一个用于加载纯 Perl 模块的固定库位置列表。它被广泛用于构建和测试 Perl 及各种扩展,但不会被安装。
大多数完整的 Perl 驻留在可共享映像 PerlShr.Exe 中,它提供了一个核心,Perl 可执行映像和所有 Perl 扩展都链接到该核心。它通常通过逻辑名称 PERLSHR 定位。虽然可以将映像放入 SYS$SHARE 以使其可加载,但并不推荐这样做。虽然你可能希望出于性能原因安装映像,但你不应该使用特权安装它;如果你这样做,结果将不是你所期望的,因为在 Perl 启动期间会禁用映像特权。
最后,Perl.Exe 是一个可执行映像,包含 Perl 的主入口点以及一些初始化代码。它应该被放置在公共目录中,并使世界可执行。为了使用命令行参数运行 Perl,你应该定义一个外来命令来调用此映像。
Perl 扩展是提供 XS 和 Perl 代码以向 Perl 添加新功能的包。(XS 是一种元语言,它简化了编写与 Perl 交互的 C 代码,有关更多详细信息,请参阅 perlxs。)扩展的 Perl 代码被视为任何其他库模块 - 它通过适当的 use
或 require
语句在你的脚本中提供,并且通常定义一个包含扩展的 Perl 包。
XS 代码提供的扩展部分可以通过两种方式连接到 Perl 的其余部分。在静态配置中,扩展的对象代码直接链接到 PerlShr.Exe 中,并在每次调用 Perl 时初始化。在动态配置中,扩展的机器代码被放置到一个单独的可共享映像中,该映像在脚本中use
或require
扩展时由 Perl 的 DynaLoader 映射。这允许您将扩展作为一个单独的实体进行维护,代价是跟踪额外的可共享映像。大多数扩展都可以设置为静态或动态。
扩展的源代码通常驻留在其自己的目录中。通常至少提供三个文件:Extshortname.xs(其中 Extshortname 是扩展名中紧跟最后一个::
的部分),包含 XS 代码,Extshortname.pm,扩展的 Perl 库模块,以及 Makefile.PL,一个 Perl 脚本,它使用 Perl 提供的MakeMaker
库模块为扩展生成一个 Descrip.MMS 文件。
由于静态扩展直接合并到 PerlShr.Exe 中,因此您必须重新构建 Perl 以合并新扩展。您应该编辑用于构建 Perl 的主 Descrip.MMS 或 Makefile,将扩展的名称添加到 ext
宏,并将扩展的对象文件添加到 extobj
宏。您还需要构建扩展的对象文件,方法是向主 Descrip.MMS 添加依赖项,或为扩展使用单独的 Descrip.MMS。然后,重新构建 PerlShr.Exe 以合并新代码。
最后,您需要将扩展的 Perl 库模块复制到 @INC
中某个目录下的 [.Extname] 子目录,其中 Extname 是扩展的名称,所有 ::
都替换为 .
(例如,扩展 Foo::Bar 的库模块将被复制到 [.Foo.Bar] 子目录)。
通常,Perl 扩展的发行套件中包含一个名为 Makefile.PL 的文件,这是一个 Perl 程序,用于创建一个 Descrip.MMS 文件,该文件可用于构建和安装扩展所需的。该套件应解压到不在 Perl 源目录下的目录树中,构建扩展的过程很简单
$ perl Makefile.PL ! Create Descrip.MMS
$ mmk ! Build necessary files
$ mmk test ! Run test code, if supplied
$ mmk install ! Install into public Perl tree
当前 Perl 版本中对 VMS 的支持足以处理大多数扩展。(有关扩展的安装选项的更多详细信息,请参阅 MakeMaker 文档。)
@INC
中某个目录的 [.Lib.Auto.Arch$PVersExtname] 子目录(其中 PVers 是您正在使用的 Perl 版本,如 $]
中提供的,将 '.' 转换为 '_'),或
@INC
中的某个目录,或
扩展的 Perl 库模块在要求其映射可共享映像时传递给 DynaLoader 的目录,或
Sys$Share 或 Sys$Library。
如果可共享映像不在这些位置中的任何一个位置,您需要定义一个逻辑名称Extshortname,其中Extshortname是扩展名在最后一个::
之后的名称部分,它转换为可共享映像的完整文件规范。
我们已尽可能让 Perl 了解 VMS 样式和 Unix 样式的文件规范。您可以在命令行和脚本中使用任一样式或同时使用,但不能在单个文件规范中组合这两种样式。VMS Perl 以与 CRTL 类似的方式解释 Unix 路径名(例如绝对路径的第一个组件被读作 VMS 文件规范的设备名称)。VMS::Filespec
包中提供了一组函数,用于在 VMS 和 Unix 语法之间进行显式转换;其文档提供了更多详细信息。
我们已尽可能减少 Perl 库模块对 Unix 语法的依赖,但您可能会发现其中一些模块以及为 Unix 系统编写的某些脚本需要您使用 Unix 语法,因为它们会假设“/”是目录分隔符,等。如果您在 Perl 发行版本身中发现这种情况,请告知我们,以便我们尝试解决这些问题。
同样,在 VMS 上处理 Perl 程序时,如果您需要特定操作系统格式的语法,那么您需要检查适当的 DECC$ 功能逻辑,或调用转换例程以强制其采用该格式。
功能逻辑名称 DECC$FILENAME_UNIX_REPORT 修改了传统 Perl 行为,将文件规范从 Unix 转换为 VMS 格式,以遵循 CRTL 现在预期的扩展字符处理规则。具体来说,当此功能生效时,Unix 路径中的./.../
现在转换为[.^.^.^.]
,而不是传统的 VMS [...]
。为了与 MakeMaker 的预期兼容,如果 VMS 路径无法转换为 Unix 路径,则它将保持不变,因此unixify("[...]")
将返回[...]
。
在某些模棱两可的情况下,转换例程无法确定输入文件名是 Unix 格式还是 VMS 格式,因为现在 VMS 和 Unix 文件规范中都可能包含字符,这些字符可能会被误认为是另一种类型的语法分隔符。因此,有些路径名根本无法在允许存在任一类型路径名的模式中使用。Perl 倾向于假设模棱两可的文件名采用 Unix 格式。
允许使用“.”作为版本分隔符与确定路径名采用 VMS 格式还是采用具有扩展文件语法的 Unix 格式根本不兼容。在将“perl-5.8.6”传递给 unixify() 或 vmsify() 时,无法知道它是 Unix“perl-5.8.6”还是 VMS“perl-5.8;6”。
DECC$FILENAME_UNIX_REPORT 逻辑名称控制 Perl 解释文件名的方式,因为 Perl 在内部将 CRTL 用于许多目的,并尝试遵循 CRTL 约定来报告文件名。DECC$FILENAME_UNIX_ONLY 特性的不同之处在于,它期望传递给 C 运行时环境的所有文件名都已采用 Unix 格式。Perl 中尚未支持此特性,因为 Perl 在内部和测试环境中使用传统 OpenVMS 文件规范,而且目前尚不清楚此模式是否有用或可用。特性逻辑名称 DECC$POSIX_COMPLIANT_PATHNAMES 是 RMS 符号链接 SDK 中的新增内容,并包含在 OpenVMS v8.3 中,但 Perl 中尚未支持。
Perl 默认启用 DECC$EFS_CASE_PRESERVE 和 DECC$ARGV_PARSE_STYLE。请注意,后者仅在 Perl 运行所在的进程中设置了扩展解析时才会生效。当在环境中明确禁用这些特性或 CRTL 不支持这些特性时,Perl 会遵循 CRTL 的传统行为,即将命令行参数小写并仅返回小写文件规范。
注意:很容易使用处于不同大小写保留处理状态的其他程序、外部实用程序和 Perl 脚本而陷入困境。例如,旧版存档实用程序或 MMK 或 MMS 等构建实用程序创建的文件,即使在 ODS-5 卷上也可能生成全大写文件名。如果稍后 Perl 脚本或模块在保留大小写的环境中检索此文件名,则该大写名称可能与 Perl 代码的混合大小写或小写预期不符。最好的办法是采用全有或全无的大小写保留方法:要么根本不使用,要么确保整个工具链和应用程序环境支持并使用它。
OpenVMS Alpha v7.3-1 及更高版本和所有版本的 OpenVMS I64 都支持大小写敏感性作为进程设置(请参阅 SET PROCESS /CASE_LOOKUP=SENSITIVE
)。Perl 目前不支持 VMS 上的大小写敏感性,但将来可能会支持,因此 Perl 程序应使用 File::Spec->case_tolerant
方法来确定状态,而不是 $^O
变量。
在启用符号链接的 ODS-5 卷上构建时,如果文件系统和 CRTL 中提供了必需的支持(通常为 64 位 OpenVMS v8.3 及更高版本),则 Perl 默认支持符号链接。在 VMS 上使用符号链接时,需要了解一些限制和注意事项。最值得注意的是,有效符号链接的目标必须表示为 Unix 风格路径,并且它必须存在于从 POSIX 根可见的卷上(请参阅 DCL 帮助中的 SHOW ROOT
命令)。有关符号链接功能和要求的更多详细信息,请参阅 OpenVMS v8.3 或更高版本附带的 CRTL 手册的第 12 章。
包含通配符的文件规范在命令行和 Perl glob 中均允许(例如 <*.c>
)。如果通配符文件规范使用 VMS 语法,则结果文件规范将遵循 VMS 语法;如果传入 Unix 风格的文件规范,则将返回 Unix 风格的文件规范。类似于 Unix shell 的通配符 glob 的行为,可以使用双引号 "
转义 Perl 程序命令行参数中的命令行通配符。但是,由于 C 处理 argv 时会去掉 "
字符,因此您需要转义这样的构造(在包含文件 PERL.C、PERL.EXE、PERL.H 和 PERL.OBJ 的目录中)
$ perl -e "print join(' ',@ARGV)" perl.*
perl.c perl.exe perl.h perl.obj
以以下三重引号方式
$ perl -e "print join(' ',@ARGV)" """perl.*"""
perl.*
在未引用的命令行参数或对 glob()
的调用中,都会执行 VMS 通配符展开。(如果您使用 File::Glob::glob
,则可以使用 csh 风格的通配符展开。)如果通配符文件规范包含设备或目录规范,则结果文件规范也将包含设备和目录;否则,将删除设备和目录信息。VMS 风格的结果文件规范将包含完整的设备和目录,而 Unix 风格的结果文件规范将仅包含输入文件规范中存在的目录路径。例如,如果您的默认目录是 Perl_Root:[000000],则展开 [.t]*.*
将产生类似于 "perl_root:[t]base.dir" 的文件规范,而展开 t/*/*
将产生类似于 "t/base.dir" 的文件规范。(这样做是为了匹配 Unix shell 执行的 glob 展开的行为。)
类似地,结果文件规范仅在输入文件规范中存在文件版本时才包含文件版本。
支持输入和输出管道到 Perl 文件句柄;将“文件名”传递给 lib$spawn() 以进行异步执行。您应该小心关闭在 Perl 脚本中打开的任何管道,以免在 Perl 退出时留下任何“孤立”的子进程。
您还可以使用反引号调用 DCL 子进程,其输出用作表达式的返回值。反引号之间的字符串的处理方式与 system
运算符的参数类似(请参见下文)。在这种情况下,Perl 将等待子进程完成,然后再继续。
perl 可以创建的用于与管道通信的邮箱 (MBX) 在 64 位系统上的默认缓冲区大小为 8192,在 VAX 上为 512。只要值介于 128 和 SYSGEN 参数 MAXBUF(含)之间,就可以通过逻辑名 PERL_MBX_SIZE 调整默认缓冲区大小。例如,要将邮箱大小设置为 32767,请使用 $ENV{'PERL_MBX_SIZE'} = 32767;
然后打开并使用管道构造。另一种方法是在运行宽记录管道程序之前发出命令
$ Define PERL_MBX_SIZE 32767
更大的值可能会以牺牲 BYTLM UAF 配额为代价来提高性能。
PERL5LIB 和 PERLLIB 环境元素的工作方式如 perl 中所述,但元素分隔符默认情况下是 '|' 而不是 ':'. 然而,当在 Unix shell 下运行时(由逻辑名 GNV$UNIX_SHELL
确定),分隔符将是 ':',就像在 Unix 系统上一样。目录规范可以使用 VMS 或 Unix 语法。
Perl 分叉调试器将调试器命令和输出放在一个单独的 X-11 终端窗口中,以便不会将来自多个进程的命令和输出混在一起。
当 Perl 在安装了 X11 支持的 VMS 系统上运行时,VMS 上的 Perl 支持分叉调试器的模拟。
要使用分叉调试器,您需要将默认显示器设置为 X-11 服务器,并设置 Unix 期望的一些环境变量。
分叉调试器要求环境变量 TERM
为 xterm
,并且环境变量 DISPLAY
存在。xterm
必须是小写。
$define TERM "xterm"
$define DISPLAY "hostname:0.0"
目前,DISPLAY
的值被忽略。建议将其设置为使用 Unix 表示法的显示器、服务器和屏幕的主机名。将来,Perl 可能会认可 DISPLAY 的值,而不是使用默认显示器。
始终使用分叉调试器可能会有所帮助,以便脚本 I/O 与调试器 I/O 分开。您可以通过将值分配给不是进程标识号的逻辑名 <PERLDB_PIDS> 来强制调试器分叉。
$define PERLDB_PIDS XXXX
如果将 PERL_VMS_EXCEPTION_DEBUG 定义为“ENABLE”,那么在引发未经其他方式处理的致命异常时,将调用 VMS 调试器。其目的是允许调试导致此类条件的 Perl 内部问题。
这允许程序员查看执行堆栈和变量,以找出异常的原因。由于在 Perl 解释器即将执行致命退出时调用调试器,因此通常不切实际地在调试模式下继续执行。
在 VMS 调试器中启动 Perl 可能会改变程序执行配置文件,从而无法重现此类问题。
kill
函数可用于从程序内测试此功能。
按照典型的 VMS 样式,仅以不区分大小写模式检查此逻辑名称的值的第一个字母,如果其值为“T”、“1”或“E”,则认为已启用。
在启动 Perl 之前必须定义此逻辑名称。
VMS 的 Perl 支持使用 Bourne shell 语法的子集在命令行上重定向输入和输出
<file
从 file
读取 stdin,
>file
将 stdout 写入 file
,
>>file
将 stdout 追加到 file
,
2>file
将 stderr 写入 file
,
2>>file
将 stderr 追加到 file
,并且
2>&1
将 stderr 重定向到 stdout。
此外,可以使用字符“|”将输出管道传输到子进程。命令行上此字符之后的任何内容都将传递给子进程以执行;子进程将 Perl 的输出作为其输入。
最后,如果命令行以“&”结尾,则整个命令将作为异步子进程在后台运行。
以下命令行开关在 VMS 中的行为与 perlrun 中描述的不同。另请注意,为了将大写开关传递给 Perl,你需要在命令行上用双引号将它们括起来,因为 CRTL 会将所有未加引号的字符串转换为小写。
在较新的 64 位 OpenVMS 版本上,进程设置现在控制是否需要引用来保留命令行参数的大小写。
如果存在 -i
开关,但未给出备份副本的扩展名,则就地编辑将创建文件的新版本;不会删除现有副本。(注意,如果给出了扩展名,则现有文件将重命名为备份文件,就像在其他操作系统中一样,因此它不会作为原始文件名下的先前版本保留。)
如果存在 "-S"
或 -"S"
开关且脚本名称不包含目录,则 Perl 会将逻辑名称 DCL$PATH 翻译为搜索列表,使用每个翻译作为查找脚本的目录。此外,如果未指定文件类型,Perl 将按此顺序在每个目录中查找与指定名称匹配的文件,该文件具有空白类型、.pl 类型和 .com 类型。
-u
开关会导致在编译 Perl 程序后但在其运行之前调用 VMS 调试器。它不会创建核心转储文件。
截至本文档上次修订时,以下 Perl 函数已在 Perl 的 VMS 端口中实现(下面将更详细地讨论带有 * 标记的函数)
file tests*, abs, alarm, atan, backticks*, binmode*, bless,
caller, chdir, chmod, chown, chomp, chop, chr,
close, closedir, cos, crypt*, defined, delete, die, do, dump*,
each, endgrent, endpwent, eof, eval, exec*, exists, exit, exp,
fileno, flock getc, getgrent*, getgrgid*, getgrnam, getlogin,
getppid, getpwent*, getpwnam*, getpwuid*, glob, gmtime*, goto,
grep, hex, ioctl, import, index, int, join, keys, kill*,
last, lc, lcfirst, lchown*, length, link*, local, localtime, log,
lstat, m//, map, mkdir, my, next, no, oct, open, opendir, ord,
pack, pipe, pop, pos, print, printf, push, q//, qq//, qw//,
qx//*, quotemeta, rand, read, readdir, readlink*, redo, ref,
rename, require, reset, return, reverse, rewinddir, rindex,
rmdir, s///, scalar, seek, seekdir, select(internal),
select (system call)*, setgrent, setpwent, shift, sin, sleep,
socketpair, sort, splice, split, sprintf, sqrt, srand, stat,
study, substr, symlink*, sysread, system*, syswrite, tell,
telldir, tie, time, times*, tr///, uc, ucfirst, umask,
undef, unlink*, unpack, untie, unshift, use, utime*,
values, vec, wait, waitpid*, wantarray, warn, write, y///
以下函数未在 VMS 端口中实现,调用它们会产生致命错误(通常)或未定义的行为(很少,我们希望)
chroot, dbmclose, dbmopen, fork*, getpgrp, getpriority,
msgctl, msgget, msgsend, msgrcv, semctl,
semget, semop, setpgrp, setpriority, shmctl, shmget,
shmread, shmwrite, syscall
以下函数可在使用 Dec C 5.2 或更高版本编译的 Perl 以及运行 VMS 7.0 或更高版本的 Perl 上使用
truncate
以下函数可在构建在 VMS 7.2 或更高版本上的 Perl 上使用
fcntl (without locking)
以下函数可能会或可能不会实现,具体取决于你在 Perl 副本中构建的套接字支持类型
accept, bind, connect, getpeername,
gethostbyname, getnetbyname, getprotobyname,
getservbyname, gethostbyaddr, getnetbyaddr,
getprotobynumber, getservbyport, gethostent,
getnetent, getprotoent, getservent, sethostent,
setnetent, setprotoent, setservent, endhostent,
endnetent, endprotoent, endservent, getsockname,
getsockopt, listen, recv, select(system call)*,
send, setsockopt, shutdown, socket
以下函数可在构建在 64 位 OpenVMS v8.2 上的 Perl 上使用,并且在 ODS-5 格式化构建磁盘上启用了硬链接。从 OpenVMS v7.3-1 原则上可以获得 CRTL 支持,更好的配置支持可以检测到这一点。
link
以下函数可在构建在 64 位 OpenVMS v8.2 及更高版本上的 Perl 上使用。从 OpenVMS v7.3-2 原则上可以获得 CRTL 支持,更好的配置支持可以检测到这一点。
getgrgid, getgrnam, getpwnam, getpwuid,
setgrent, ttyname
以下函数可在构建在 64 位 OpenVMS v8.2 及更高版本上的 Perl 上使用。
statvfs, socketpair
测试 -b
、-B
、-c
、-C
、-d
、-e
、-f
、-o
、-M
、-s
、-S
、-t
、-T
和 -z
按预期工作。-r
、-w
和 -x
的返回值会告知您是否可以实际访问该文件;这可能无法反映基于 UIC 的文件保护。由于在 VMS 下实际和有效的 UIC 不会有所不同,因此 -O
、-R
、-W
和 -X
等效于 -o
、-r
、-w
和 -x
。类似地,包括 -A
、-g
、-k
、-l
、-p
和 -u
在内的其他几个测试在 VMS 下没有特别的意义,这些测试返回的值反映了 CRTL stat()
例程对 st_mode 字段中的等效位执行的任何操作。最后,如果传递给设备规范而没有明确的目录(例如 DUA1:
),以及如果传递给目录,-d
将返回 true。
还有 DECC 特性逻辑名称和 ODS-5 卷属性,它们也控制为日期字段返回哪些值。
注意:一些站点报告在通过 DEC 的 DFS 访问文件时使用文件访问测试(-r
、-w
和 -x
)时遇到问题。具体来说,由于 DFS 目前不提供对远程卷上文件的扩展文件头的访问,因此尝试检查 ACL 会失败,并且文件测试将返回 false,而 $!
指示文件不存在。您可以对这些文件使用 stat
,因为它仅检查基于 UIC 的保护,然后在它返回的模式值中手动检查适当的位(由 C 编译器的 stat.h 定义),如果您需要近似文件保护。
反引号创建一个子进程,并将括起来的字符串传递给它,以便作为 DCL 命令执行。由于子进程是通过 lib$spawn()
直接创建的,因此可以指定任何有效的 DCL 命令字符串。
binmode
运算符将尝试确保不会对输入或输出到此文件句柄的回车控制进行转换。由于这涉及重新打开文件,然后恢复其文件位置指示器,因此如果此函数返回 FALSE,则底层文件句柄可能不再指向打开的文件,或者可能指向与调用 binmode
之前不同的文件位置。
请注意,在使用普通文件句柄时通常不需要 binmode
;提供它是为了让你在必要时可以控制对现有记录结构文件的 I/O。你还可以使用 VMS::Stdio 扩展中的 vmsfopen
函数来更精细地控制对具有不同记录结构的文件和设备的 I/O。
crypt
运算符使用 sys$hash_password
系统服务来生成 PLAINTEXT 的哈希表示。如果 USER 是一个有效的用户名,则算法和盐值将从该用户的 UAF 记录中获取。如果不是,则使用首选算法和盐值 0。四字加密值将作为 8 个字符的字符串返回。
crypt
返回的值可以与 getpw*
函数返回的 UAF 中的加密密码进行比较,以验证用户。如果你要执行此操作,请记住 UAF 中的加密密码是使用大写用户名和密码字符串生成的;你必须将 crypt
的参数大写,以确保获得正确的值
sub validate_passwd {
my($user,$passwd) = @_;
my($pwdhash);
if ( !($pwdhash = (getpwnam($user))[1]) ||
$pwdhash ne crypt("\U$passwd","\U$name") ) {
intruder_alert($name);
}
return 1;
}
如果 $! 或 $? 状态值都不是将本机状态解释为 VMS 为 DCL 错误处理分类的 SEVERE_ERROR 严重性的值,则 die
将强制本机 VMS 退出状态为 SS$_ABORT 代码。
当 PERL_VMS_POSIX_EXIT
处于活动状态(请参见下面的 "$?")时,本机 VMS 退出状态值将包含 $!
或 $?
或 $^E
或 Unix 值 255 中的一个,其编码方式使其他用 C 语言编写的程序(包括 Perl 和 GNV 包)能够解码有效原始值。根据 die
的正常非 VMS 行为,如果 $!
或 $?
任何一个非零,则其中一个值将被编码到本机 VMS 状态值中。如果 Unix 状态值均为 0,并且 $^E
值设置为 ERROR 或 SEVERE_ERROR 严重性之一,则 $^E
值将按原样用作退出代码。如果上述情况均不适用,则 Unix 值 255 将被编码到本机 VMS 退出状态值中。
请注意,PERL_VMS_POSIX_EXIT
模式下 die
行为的一个显著差异在于,它不会在退出时强制执行 VMS SEVERE_ERROR 状态。Unix 退出值 2 到 255 将被编码到严重性级别为 SUCCESS 的 VMS 状态值中。Unix 退出值 1 将被编码到严重性级别为 ERROR 的 VMS 状态值中。这样做是为了与 VMS C 库编码这些值的方式兼容。
PERL_VMS_POSIX_EXIT
模式下 die
设置的最低严重性级别可能会根据测试结果和进一步审查的结果而更改为 ERROR 或更高。
有关将 Unix 值编码为包含它的本机 VMS 状态的说明,请参见 "$?"。
dump
运算符不会导致 Perl 终止并转储核心,而是调用 VMS 调试器。如果您继续在调试器下执行 Perl 程序,则控制权将被转移到指定为 dump
参数的标签,或者如果没有指定标签,则回到程序开头。dump
的所有其他程序状态(例如变量值、打开的文件句柄)不受调用 dump
的影响。
调用 exec
将导致 Perl 退出,并通过 lib$do_command
调用作为 exec
参数给出的命令。如果参数以 '@' 或 '$' 开头(不是作为文件规范的一部分),则将其作为 DCL 命令执行。否则,命令行上的第一个标记将被视为要运行的映像的文件规范,并尝试调用它(使用 .Exe 和进程默认值来扩展文件规范)并将 exec
的其余参数作为参数传递给它。如果标记没有文件类型,并且与空类型文件匹配,则尝试确定该文件是应使用 MCR
调用的可执行映像还是应作为命令过程传递给 DCL 的文本文件。
虽然原则上可以通过 CRTL vfork()
例程(以及与之相同的相当严重的限制)来实现 fork
运算符,并且虽然已经到位了一些内部支持来执行此操作,但该实现从未完成,导致 fork
当前不可用。真正的内核 fork()
预计将在 VMS 的未来版本中出现,而基于解释器线程的伪 fork 可能会在 VMS 的未来版本 Perl 中出现(请参见 perlfork)。在此期间,请使用 system
、反引号或管道文件句柄来创建子进程。
如果您有通过 sys$getuai
检索指定用户的 UAF 信息的权限,则这些操作员会获取 perlfunc 中描述的信息。如果没有,则只返回 $name
、$uid
和 $gid
项。$dir
项包含 VMS 语法中的登录目录,而 $comment
项包含 Unix 语法中的登录目录。$gcos
项包含 UAF 记录中的所有者字段。$quota
项未使用。
如果您有可用的 CRTL gmtime()
例程,或者如果逻辑名称 SYS$TIMEZONE_DIFFERENTIAL 被定义为 UTC 与本地时间之间的秒数,则 gmtime
操作员将正常运行。(如果您运行的是内置 UTC 支持的 VMS 版本,则会自动定义此逻辑名称。)如果这两个条件都不满足,则会打印一条警告消息,并返回 undef
。
在大多数情况下,kill
通过未记录的系统服务 $SIGPRC
实现,该服务具有与 $FORCEX
相同的调用顺序,但在目标进程中引发异常,而不是强制其调用 $EXIT
。一般来说,kill
遵循 CRTL 的 kill()
函数的行为,但与该函数不同,它可以从信号处理程序中调用。此外,与 CRTL 某些版本中的 kill
不同,Perl 的 kill
会检查传入信号的有效性,并返回错误,而不是尝试发送无法识别的信号。
此外,在 VMS 下,负信号值不会执行任何特殊操作;它们只会转换为相应的正值。
请参阅上面关于 backticks
的条目。
如果 Perl 未使用套接字支持构建,则根本无法使用 select
的系统调用版本。如果存在套接字支持,则 select
的系统调用版本仅对附加到套接字的文件描述符起作用。它不会提供有关常规文件或管道的任何信息,因为 CRTL select()
例程不提供此功能。
由于 VMS 按照不同于 Unix 的方案跟踪文件,因此实际上无法在 struct stat
的 st_dev
和 st_ino
字段中表示文件的 ID。不过,Perl 会尽力而为,并且它所使用的值不太可能对两个不同的文件相同。但是,我们无法保证这一点,因此请脚本编写者注意。
system
运算符创建一个子进程,并将它的参数传递给子进程,以便作为 DCL 命令执行。由于子进程是通过 lib$spawn()
直接创建的,因此可以指定任何有效的 DCL 命令字符串。如果字符串以“@”开头,则它将无条件地被视为 DCL 命令。否则,如果第一个标记包含用于文件规范中的分隔符(例如 :
或 ]
),则会尝试使用默认类型 .Exe 和进程默认值对其进行扩展,如果成功,则会通过 MCR
调用结果文件。这允许你通过将文件规范传递给 system
来直接调用映像,这是一种常见的 Unix 惯用语。如果标记没有文件类型,并且与空类型文件匹配,则会尝试确定文件是应该使用 MCR
调用的可执行映像还是应该作为命令过程传递给 DCL 的文本文件。
如果 LIST 包含空字符串,则 system
会生成一个交互式 DCL 子进程,其方式与在 DCL 提示符下键入 SPAWN 相同。
Perl 会等到子进程完成再继续执行当前进程。如 perlfunc 中所述,system
的返回值是一个伪“状态”,它遵循 POSIX 语义,除非启用了 use vmsish 'status'
实用程序;有关更多详细信息,请参阅本文档中的 $?
描述。
time
返回的值是从 1970 年 1 月 1 日 00:00:00 开始的秒偏移量(就像 CRTL 的 times() 例程一样),以便让来自 POSIX/Unix 世界的代码更容易使用。
times
运算符返回的数组按照 CRTL times()
例程的相同规则进行划分。因此,“系统时间”元素始终为 0,因为在 VMS 下“用户时间”和“系统”时间没有区别,并且子进程累积的时间可能出现在“子时间”字段中,也可能不出现,具体取决于 times()
是否单独跟踪子进程。特别注意,VAXCRTL(至少)仅跟踪使用 fork()
和 exec()
生成的子进程;它不会累积通过管道、system()
或反引号生成的子进程的时间。
unlink
仅删除文件的最高版本;要删除所有版本,您需要说
1 while unlink LIST;
您可能需要对为 Unix 系统编写的脚本进行此更改,该脚本期望在调用 unlink
后,传递给 unlink
的名称中不会存在任何文件。(注意:这可以在编译时更改;如果您use Config
并且$Config{'d_unlink_all_versions'}
是define
,那么unlink
将在第一次调用时删除文件的所有版本。)
unlink
将在任何可能的情况下删除文件,即使需要更改文件保护(尽管它不会尝试更改父目录的保护)。您可以通过使用 VMS::Filespec::candelete
运算符来判断是否具有对文件的明确删除访问权限。例如,为了仅删除您有权删除的文件,您可以说类似于
sub safe_unlink {
my($file,$num);
foreach $file (@_) {
next unless VMS::Filespec::candelete($file);
$num += unlink $file;
}
$num;
}
(或者如果您已安装随 Perl 发行的 VMS::Stdio 扩展,则可以使用 VMS::Stdio::remove
)。如果 unlink
必须更改文件保护才能删除文件,并且您在中途中断它,则文件可能保持原样,但 ACL 已更改,允许您删除访问权限。
unlink
的此行为与 POSIX 行为兼容,而不是传统的 VMS 行为。
此运算符仅更改 ODS-2 卷和未启用访问日期的 ODS-5 卷上的文件修改时间(VMS 修订日期)。在启用了访问日期的 ODS-5 卷上,修改真实访问时间。
如果 PID 是通过管道 open()
启动的子进程(请参阅 open),waitpid
将等待该子进程,并在 $?
中返回其最终状态值。如果 PID 是通过其他方式创建的子进程(例如在调用 Perl 之前 SPAWNed),waitpid
将每秒检查一次进程是否已完成,并在完成时返回。(如果 PID 指定的进程不是当前进程的子进程,并且您使用 -w
开关调用了 Perl,则会发出警告。)
成功时返回 PID,错误时返回 -1。在所有情况下,FLAGS 参数都会被忽略。
除了 perlvar 中的一般信息外,以下 VMS 特定的信息适用于指示的“特殊”Perl 变量。如果发生冲突,则此信息优先。
%ENV
数组的操作取决于逻辑名称 PERL_ENV_TABLES 的转换。如果已定义,它应为一个搜索列表,其每个元素都指定 %ENV
元素的位置。如果您告诉 Perl 读取或设置元素 $ENV{
name}
,那么 Perl 将使用 PERL_ENV_TABLES 的转换,如下所示
此字符串告诉 Perl 咨询 CRTL 的内部 environ
键值对数组,并使用 name 作为键。在大多数情况下,它只包含几个键,但如果 Perl 是通过 C exec[lv]e()
函数调用的,就像某些嵌入式 Perl 应用程序或在 GNV bash 等 shell 下运行时一样,environ
数组可能已被调用程序填充。
以 CLISYM_
开头的字符串告诉 Perl 咨询 CLI 的符号表,并使用 name 作为符号的名称。在读取 %ENV
的元素时,首先扫描本地符号表,然后扫描全局符号表。当设置或删除 %ENV
的元素时,CLISYM_
后面的字符很重要:如果完整的字符串是 CLISYM_LOCAL
,则在本地符号表中进行更改;否则,将更改全局符号表。
如果 PERL_ENV_TABLES 的元素转换为任何其他字符串,则该字符串将用作逻辑名称表的名称,并使用 name 作为逻辑名称进行咨询。使用访问模式的正常搜索顺序。
PERL_ENV_TABLES 在 Perl 启动时翻译一次;在 Perl 运行时所做的任何更改都不会影响 %ENV
的行为。如果未定义 PERL_ENV_TABLES,则 Perl 默认首先查询由 LNM$FILE_DEV 指定的逻辑名称表,然后查询 CRTL environ
数组。当逻辑名称 GNV$UNIX_SHELL 被定义时,此默认顺序会颠倒,例如在 GNV bash 下运行时。
对于基于逻辑名称或 DCL 符号的 %ENV 条目的操作,无论在 Perl 表达式中实际指定了什么大小写,都会将键字符串视为全部大写。基于 CRTL 的 environ 数组的 %ENV 中的条目在存储时会保留键字符串的大小写,并且查找区分大小写。
读取 %ENV
的元素时,将按顺序检查 PERL_ENV_TABLES 指向的位置,并返回从首次成功查找中获取的值。如果 %ENV
元素的名称包含分号,则会删除分号及其后的任何字符。在查询 CRTL environ
数组或 CLI 符号表时会忽略这些字符。但是,如果在逻辑名称表中查找名称,则分号后面的后缀会被视为查找要使用的翻译索引。这使您可以查找搜索列表逻辑名称的连续值。例如,如果您说
$ Define STORY once,upon,a,time,there,was
$ perl -e "for ($i = 0; $i <= 6; $i++) " -
_$ -e "{ print $ENV{'story;'.$i},' '}"
Perl 将打印 ONCE UPON A TIME THERE WAS
,当然,前提是 PERL_ENV_TABLES 已被设置为找到逻辑名称 story
,而不是具有相同名称的 CLI 符号或 CRTL environ
元素。
当 %ENV
的元素被设置为已定义字符串时,将在 PERL_ENV_TABLES 的第一个翻译指向的位置进行相应的定义。如果这导致创建逻辑名称,则会在主管模式中定义它。(如果现有的逻辑名称在执行或内核模式中定义,也会执行相同的操作;现有的用户或主管模式逻辑名称将重置为新值。)如果该值为一个空字符串,则逻辑名称的翻译将被定义为一个 NUL
(ASCII \0
)字符,因为逻辑名称不能翻译为零长度字符串。(此限制不适用于 CLI 符号或 CRTL environ
值;它们被设置为一个空字符串。)
当 %ENV
的元素被设置为 undef
时,该元素会被查找,就好像它正在被读取一样,如果找到它,它将被删除。(从 CRTL environ
数组中“删除”的项目将被设置为一个空字符串。)使用 delete
从 %ENV
中删除元素会产生类似的效果,但在元素被删除后,会再次尝试查找该元素,因此内模式逻辑名称或其他位置中的名称将替换刚刚删除的逻辑名称。在任何情况下,只有在搜索 PERL_ENV_TABLES 时找到的第一个值会被更改。目前无法通过 %ENV 定义搜索列表逻辑名称。
元素 $ENV{DEFAULT}
是特殊的:读取时,它返回 Perl 当前的默认设备和目录,设置时,它重置它们,而不管 PERL_ENV_TABLES 的定义如何。它不能被清除或删除;尝试这样做会被静默忽略。
请注意,如果你想将 C-local environ 数组的任何元素传递给未通过 fork/exec 启动或未运行 C 程序的子进程,你可以将它们“提升”为当前进程中的逻辑名称,然后它们将由所有子进程继承,方法如下
foreach my $key (qw[C-local keys you want promoted]) {
my $temp = $ENV{$key}; # read from C-local array
$ENV{$key} = $temp; # and define as logical name
}
(你不能只说 $ENV{$key} = $ENV{$key}
,因为 Perl 优化器足够智能,可以省略该表达式。)
不要尝试通过说 %ENV = ();
来清除 %ENV
,它将抛出一个致命错误。这等同于从 DCL 执行以下操作
DELETE/LOGICAL *
你可以想象,如果例如删除了 SYS$MANAGER 或 SYS$SYSTEM 逻辑名称,情况会变得多么糟糕。
目前,当你第一次使用 keys
或 values
迭代 %ENV 时,由于所有逻辑名称都被读取以完全填充 %ENV,因此你会遇到时间损失。后续迭代不会重新读取逻辑名称,因此它们不会那么慢,但它们也不会反映其他程序对逻辑名称表所做的任何更改。
你需要小心处理表示进程永久文件的逻辑名称,例如 SYS$INPUT
和 SYS$OUTPUT
。这些逻辑名称的转换在前面添加了一个双字节二进制值 (0x1B 0x00),如果你想使用它,需要将其剥离。(在 Perl 的早期版本中,无法获取这些逻辑名称的值,因为空字节充当了字符串结束标记)
$!
的字符串值是由 CRTL 的 strerror() 函数返回的,因此它将包括 VMS 特定错误的 VMS 消息。$!
的数字值是 errno
的值,但如果 errno 是 EVMSERR,则 $!
包含 vaxc$errno 的值。设置 $!
始终将 errno 设置为指定的值。如果此值为 EVMSERR,它还将 vaxc$errno 设置为 4 (NONAME-F-NOMSG),以便 $!
的字符串值不会反映在设置 $!
之前的 VMS 错误消息。
此变量提供对 vaxc$errno 中 VMS 状态值的直接访问,这些值通常比 $!
中的通用 Unix 风格错误消息更具体。它的数字值是 vaxc$errno 的值,它的字符串值是通过 sys$getmsg() 检索到的相应 VMS 消息字符串。设置 $^E
将 vaxc$errno 设置为指定的值。
虽然 Perl 尝试保持 vaxc$errno 值为当前值,但如果 errno 不是 EVMSERR,则它可能不是来自当前操作。
$?
中返回的“状态值”是根据子进程的实际退出状态合成的,其方式近似于 POSIX wait(5) 语义,以便允许 Perl 程序可移植地测试子进程是否成功完成。在 VMS 下,$?
的低阶 8 位始终为 0,因为进程的终止状态可能由异常生成,也可能没有由异常生成。
接下来的 8 位包含程序的终止状态。
如果子进程遵循使用 _POSIX_EXIT 宏编译的 C 程序的约定,则状态值将包含该程序在正常退出时返回的 0 到 255 的实际值。
使用 _POSIX_EXIT 宏时,Unix 退出值 0 表示为 VMS 本机状态 1,Unix 值 2 到 255 由以下等式编码
VMS_status = 0x35a000 + (unix_value * 8) + 1.
在 Unix 值 1 的特殊情况下,编码为
VMS_status = 0x35a000 + 8 + 2 + 0x10000000.
对于其他终止状态,将使用子进程退出状态的严重性部分:如果严重性为成功或信息性,则这些位全部为 0;如果严重性为警告,则它们包含值 1;如果严重性为错误或严重错误,则它们包含实际的严重性位,事实证明错误的值为 2,严重错误的值为 4。致命是严重错误状态的另一个术语。
因此,如果子进程的退出状态指示成功完成,则 $?
始终为零;如果发生警告或错误,或者运行了符合编码 _POSIX_EXIT 值的程序并设置了状态,则为非零。
如何区分 VMS 本机错误状态或编码 Unix 状态导致的非零状态?除非查看 ${^CHILD_ERROR_NATIVE} 值,否则无法区分。${^CHILD_ERROR_NATIVE} 值返回实际的 VMS 状态值并检查严重性位。如果严重性位等于 1,则如果 $?
的数值在 2 到 255 或 0 之间,则 $?
准确地反映了 Unix 应用程序返回的值。如果 $?
为 1,并且严重性位指示 VMS 错误 (2),则 $?
来自 Unix 应用程序退出值。
实际上,调用返回 _POSIX_EXIT 类型状态值的程序的 Perl 脚本将预期这些值,而调用传统 VMS 程序的程序将预期之前的行为或仅检查非零状态。
并且在所有行为中,成功始终为值 0。
当子代的实际 VMS 终止状态为错误时,内部 $!
值将设置为该错误最接近的 Unix errno 值,以便测试错误消息的 Perl 脚本将看到预期的 Unix 样式错误消息,而不是 VMS 消息。
相反,在 END 块中设置 $?
时,会尝试将 POSIX 值转换为在退出 Perl 时操作系统可理解的本机状态。归根结底,将 $?
设置为零会导致通用成功值 SS$_NORMAL,而将 $?
设置为非零值会导致通用失败状态 SS$_ABORT。另请参阅 perlport 中的“exit”。
将 PERL_VMS_POSIX_EXIT
逻辑名称定义为“ENABLE”后,设置 $?
将导致将新值编码到 $^E
中,以便预期 _POSIX_EXIT 行为的 C 程序可以自动恢复原始父代或子代退出状态值 0 到 255。如果父代和子代退出值均为非零,则将假定这实际上是要传递的 VMS 本机状态值。特殊值 0xFFFF 几乎是 NOOP,因为它将导致 C 库中的当前本机 VMS 状态变为当前本机 Perl VMS 状态,并且以这种方式处理,因为它已知不是有效的本机 VMS 状态值。建议仅使用正常 Unix 父代或子代状态数字范围内的值,即 0 到 255。
pragma use vmsish 'status'
使 $?
反映实际 VMS 退出状态,而不是上面描述的 POSIX 状态的默认模拟。此 pragma 还禁用了在 END 块中设置 $?
时将非零值转换为 SS$_ABORT(但零仍将转换为 SS$_NORMAL)。
不要在启用 PERL_VMS_POSIX_EXIT
的情况下使用 pragma use vmsish 'status'
,因为它们有时会请求冲突的操作,并且不遵守此建议的后果将是不确定的,以允许将来改进 POSIX 退出处理。
通常,在启用 PERL_VMS_POSIX_EXIT
的情况下,DCL 脚本或其他本机 VMS 工具的退出状态中将提供更详细的信息,并且将为 Posix 程序提供预期信息。为了保持向后兼容性,它尚未成为默认设置。
注意:设置 DECC$FILENAME_UNIX_REPORT
会隐式启用 PERL_VMS_POSIX_EXIT
。
为 I/O 流设置 $|
会导致数据在每次写入时都刷新到磁盘(即,不仅仅刷新到文件的底层 RMS 缓冲区)。换句话说,它等同于从 C 调用 fflush() 和 fsync()。
SDBM_File 在 VMS 上正常工作。但是,它有一个细微的差别。创建的数据库目录文件具有 .sdbm_dir 扩展名,而不是 .dir 扩展名。.dir 文件是 VMS 文件系统目录文件,将它们用于其他目的可能会导致不可接受的问题。
有关修订历史,请参阅 git 存储库。
Charles Bailey [email protected] Craig Berry [email protected] Dan Sugalski [email protected] John Malmberg [email protected]