目錄

名稱

perlvms - 專為Perl的VMS特定文件

描述

下面收集了有關 Perl 5 在 VMS 上的行為細節的筆記。這些筆記是 Perl 5 常規文檔的補充,因此我們重點關注 Perl 5 在 VMS 下與 Unix 下的不同之處,以及 Perl 與操作系統其餘部分之間的交互作用。我們沒有試圖從主 Perl 文檔中複製 Perl 功能的完整描述,這些功能可以在 Perl 發行版的 [.pod] 子目錄中找到。

我們希望這些筆記能幫助您在 VMS 上編寫 Perl 腳本時避免混淆和失眠。如果您發現我們漏掉了您認為應該出現在這裡的內容,請隨時發送郵件到 vmsperl@perl.org 聯繫我們。

安裝

有關構建和安裝 Perl 5 的指示可以在 Perl 發行版的主源代碼目錄中的文件 README.vms 中找到。

Perl 映像組織

核心映像

在構建過程中,會生成三個 Perl 映像。 Miniperl.Exe 是一個可執行映像,包含 Perl 的所有基本功能,但無法利用 Perl XS 擴展,並且對於加載純 Perl 模塊有一個硬編碼的庫位置列表。它被廣泛用於構建和測試 Perl 及各種擴展,但不會被安裝。

大部分完整的 Perl 存儲在共享映像 PerlShr.Exe 中,它提供了 Perl 可執行映像和所有 Perl 擴展所鏈接的核心。通常可以通過邏輯名稱 PERLSHR 找到它。雖然可以將映像放在 SYS$SHARE 中以便加載,但不推薦這樣做。雖然您可能希望為了性能原因安裝映像,但不應該使用特權來安裝它;如果這樣做,結果將與您期望的不同,因為在 Perl 啟動期間已禁用映像特權。

最後,Perl.Exe 是一個可執行映像,包含 Perl 的主入口點以及一些初始化代碼。它應該放在一個公共目錄中,並且被設置為全局可執行。為了使用命令行參數運行 Perl,您應該定義一個外部命令來調用此映像。

Perl 擴展

Perl 擴展是提供 XS 和 Perl 代碼的包,用於為 perl 添加新功能。(XS 是一種簡化與 Perl 交互的 C 代碼的元語言,有關詳細信息,請參見 perlxs。)擴展的 Perl 代碼被視為任何其他庫模塊 - 通過適當的 userequire 語句,在您的腳本中可用,並且通常定義了包含擴展的 Perl 包。

擴展提供的 XS 代碼部分可以以兩種方式之一連接到 Perl 的其餘部分。在 靜態 配置中,擴展的對象代碼直接鏈接到 PerlShr.Exe 中,並且在調用 Perl 時初始化。在 動態 配置中,擴展的機器代碼放入一個單獨的共享映像中,當在您的腳本中 userequire 該擴展時,Perl 的 DynaLoader 將其映射。這允許您將擴展保持為一個獨立實體,但需要跟踪額外的共享映像。大多數擴展都可以設置為靜態或動態。

擴展的源代碼通常位於自己的目錄中。通常提供至少三個文件:Extshortname.xs(其中Extshortname是擴展名中最後一個::後的部分),包含 XS 代碼的文件,Extshortname.pm,擴展的 Perl 库模塊,以及Makefile.PL,一個 Perl 腳本,它使用 Perl 提供的 MakeMaker 库模塊生成擴展的Descrip.MMS文件。

安裝靜態擴展

由於靜態擴展直接合併到 PerlShr.Exe 中,您需要重新構建 Perl 以合併新的擴展。您應該編輯用於構建 Perl 的主要Descrip.MMSMakefile,將擴展的名稱添加到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 文檔。)

如果可共用的映像檔不在這些位置之一,則需要定義邏輯名稱 Extshortname,其中 Extshortname 是在最後一個 :: 之後的擴展名部分,這將轉換為可共用映像檔的完整文件規格。

文件規格

語法

我們已嘗試使 Perl 能夠識別 VMS-style 和 Unix-style 文件規格,無論何時都可以使用其中一種風格或兩種風格,但不能在單個文件規格中結合兩種風格。VMS Perl 對 Unix 路徑名稱進行解釋的方式與 CRTL(例如,絕對路徑的第一個組件被讀取為 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 格式相互矛盾。無法知曉當將其傳遞給 unixify() 或 vmsify() 時,“perl-5.8.6” 是 Unix 的 “perl-5.8.6” 還是 VMS 的 “perl-5.8;6”。

DECC$FILENAME_UNIX_REPORT 邏輯名稱控制 Perl 解釋文件名的方式,因為 Perl 在許多目的上內部使用 CRTL,並嘗試遵循 CRTL 指定的報告文件名的慣例。DECC$FILENAME_UNIX_ONLY 功能與之不同,它期望所有傳遞給 C 運行時的文件名已經處於 Unix 格式。自從 RMS 符號鏈接 SDK 與 OpenVMS v8.3 一起發布以來,功能邏輯名稱 DECC$POSIX_COMPLIANT_PATHNAMES 是新功能,但尚不受 Perl 支持。

檔案名稱大小寫

Perl 預設啟用 DECC$EFS_CASE_PRESERVE 和 DECC$ARGV_PARSE_STYLE。請注意,後者僅在執行 Perl 的進程中設置了擴展解析時才生效。當這些功能在環境中明確禁用或 CRTL 不支持它們時,Perl 將遵循傳統 CRTL 的行為,將命令行參數轉換為小寫並僅返回小寫的文件規範。

N. B. 在使用其他程序、外部工具和 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 卷上構建時,Perl 預設情況下支援符號連結,前提是文件系統和 CRTL(通常是 64 位 OpenVMS v8.3 及更高版本)提供了必要的支援。在處理 VMS 上的符號連結時,請注意一些限制和注意事項。最值得注意的是,有效符號連結的目標必須表示為 Unix 样式路徑,並且它必須存在於從您的 POSIX 根目錄可見的卷上(請參閱 DCL 說明中的 SHOW ROOT 命令)。有關符號連結功能和要求的進一步詳細信息,請參閱隨 OpenVMS v8.3 或更高版本一起提供的 CRTL 手冊的第 12 章。

通配符擴展

命令行和 Perl 圓括號(例如 <*.c>)中允許包含通配符的文件規範。如果通配符文件規範使用 VMS 語法,則生成的文件規範將遵循 VMS 語法;如果傳遞的是 Unix 样式文件規範,則將返回 Unix 样式文件規範。類似於 Unix shell 的通配符 globbing 的行為,可以使用雙引號 " 將命令行通配符進行轉義,例如在包含文件 PERL.CPERL.EXEPERL.HPERL.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-style 通配符展開。)如果通配符文件規格包含設備或目錄規格,那麼結果文件規格也將包含設備和目錄;否則,設備和目錄信息將被刪除。VMS-style 的結果文件規格將包含完整的設備和目錄,而 Unix-style 的結果文件規格將僅包含與輸入文件規格中存在的目錄路徑相同的部分。例如,如果您的默認目錄是 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。默認的緩衝區大小可以通過邏輯名 PERL_MBX_SIZE 進行調整,前提是該值在 128 和 SYSGEN 參數 MAXBUF 之間(包括 128 和 MAXBUF)。例如,要將郵箱大小設置為 32767,請使用 $ENV{'PERL_MBX_SIZE'} = 32767; 然後打開並使用管道構造。另一種方法是在運行寬記錄管道程序之前發出命令

$ Define PERL_MBX_SIZE 32767

。較大的值可能會在 BYTLM UAF 指配額的代價下改善性能。

PERL5LIB 和 PERLLIB

PERL5LIB 和 PERLLIB 環境變量的工作方式與 perl 中記錄的文檔相同,只是分隔元素的分隔符默認為 '|' 而不是 ': '。但是,當在由邏輯名 GNV$UNIX_SHELL 確定的 Unix shell 下運行時,分隔符將是 Unix 系統上的 ':'。目錄規格可以使用 VMS 或 Unix 語法。

Perl 分叉式除錯器

Perl 分叉式除錯器將除錯器命令和輸出放在一個單獨的 X-11 終端窗口中,以便多個進程的命令和輸出不會混在一起。

當在安裝了 X11 支持的 VMS 系統上運行 Perl 時,Perl 在 VMS 上支持對分叉式除錯器的仿真。

要使用分叉式除錯器,您需要將默認顯示器設置為 X-11 服務器,並設置一些 Unix 預期的環境變量。

分叉式除錯器需要環境變量 TERM 設置為 xterm,並且需要存在環境變量 DISPLAYxterm 必須是小寫。

$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

如果定義了 PERL_VMS_EXCEPTION_DEBUG 為 "ENABLE",則會在引發未處理的致命異常時調用 VMS 調試器。這樣做的目的是允許調試導致此類條件的內部 Perl 問題。

這使程序員可以查看執行堆棧和變量,以找出異常的原因。由於在 Perl 解釋器準備進行致命退出時調用了調試器,因此通常不實用地繼續以調試模式執行。

在 VMS 調試器中啟動 Perl 可能會改變程序執行配置文件,從而無法重現此類問題。

可以使用 kill 函數從程序內部測試此功能。

按照典型的 VMS 樣式,實際上只檢查此邏輯名稱值的第一個字母,並且在不區分大小寫的模式下,如果值為 "T"、"1" 或 "E",則被視為啟用。

此邏輯名稱必須在啟動 Perl 之前定義。

命令行

輸入/輸出重定向與背景執行

Perl for VMS支援在命令行上進行輸入和輸出的重定向,使用Bourne shell語法的子集

此外,可以使用字符'|'將輸出傳送到子進程。命令行上此字符後的任何內容都將傳遞給子進程以執行;子進程將Perl的輸出作為其輸入。

最後,如果命令行以'&'結尾,則整個命令將作為異步子進程在後台運行。

命令行開關

以下命令行開關在VMS下的行為與perlrun中描述的不同。還要注意,在將大寫開關傳遞給Perl時,需要在命令行上用雙引號括起來,因為CRTL會對所有未引用的字符串進行小寫轉換。

在更新的64位OpenVMS版本中,一個進程設置現在控制是否需要引用來保留命令行參數的大小寫。

-i

如果存在-i開關但未給出備份副本的擴展名,則就地編輯將創建文件的新版本;現有副本不會被刪除。(請注意,如果給出了擴展名,則現有文件將重命名為備份文件,就像在其他操作系統下一樣,因此它不會保留在原始文件名下的以前版本。)

-S

如果存在"-S"-"S"開關 並且 腳本名稱不包含目錄,則Perl將DCL$PATH作為搜索列表進行轉換,使用每個轉換作為查找腳本的目錄。此外,如果未指定文件類型,Perl將在每個目錄中查找與指定名稱匹配的文件,其中包含空類型、類型為.pl和類型為.com的文件,依次進行。

-u

-u開關導致在Perl程序編譯完成後但在運行之前調用VMS調試器。它不創建核心轉儲文件。

Perl函數

截至本文最後一次修訂時,VMS版本的Perl實現了以下Perl函數(標有*的函數在下文中進行了更詳細的討論)

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 或更高版本編譯並運行於 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

以下函數可在建立於啟用了硬連結的 ODS-5 格式構建磁盤上的 64 位 OpenVMS v8.2 的 Perl 中使用。從 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,同樣地,如果傳遞了目錄,-d 也返回 true。

還有 DECC 功能邏輯名稱和 ODS-5 卷屬性也可以控制返回日期字段的值。

注意:有些站點報告使用文件訪問測試(-r, -w, 及 -x)在通過 DEC 的 DFS 訪問文件時出現問題。具體來說,由於 DFS 目前不提供對遠程卷上文件的擴展文件頭的訪問,嘗試檢查 ACL 失敗,並且文件測試將返回 false,並且使用 $! 指示文件不存在。您可以對這些文件使用 stat,因為它僅檢查基於 UIC 的保護,然後手動檢查由您的 C 編譯器的 stat.h 中定義的適當位,它返回的模式值,如果您需要文件保護的近似值。

backticks

Backticks 創建一個子進程,並將封閉的字符串傳遞給它作為 DCL 命令執行。由於子進程是直接通過 lib$spawn() 創建的,因此可以指定任何有效的 DCL 命令字符串。

binmode FILEHANDLE

binmode 運算子將嘗試確保對該文件句柄的輸入和輸出不發生輸入輸出的轉換。由於這涉及重新打開文件,然後恢復其文件位置指示器,如果此函數返回 FALSE,則基礎文件句柄可能不再指向已打開的文件,或者可能指向調用 binmode 前的文件中的不同位置。

請注意,在使用普通文件句柄時通常不需要使用 binmode;它是為了讓您在必要時控制對現有記錄結構文件的 I/O 而提供的。您還可以使用 VMS::Stdio 擴展中的 vmsfopen 函數,以更精細地控制對具有不同記錄結構的文件和設備的 I/O。

crypt PLAINTEXT, USER

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;
}
die

die 將強制本地 VMS 退出狀態為 SS$_ABORT 代碼,如果 $! 或 $? 狀態值都不會導致本地狀態被解釋為 DCL 錯誤處理中被 VMS 分類為 SEVERE_ERROR 嚴重性。

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 中止並轉儲核心不同,dump 運算符調用 VMS 調試器。如果您在調試器下繼續執行 Perl 程序,則控制將轉移到作為 dump 參數指定的標籤,或者如果未指定標籤,則返回到程序的開始。調用 dump 不會影響程序的其他狀態(例如變量的值,打開的文件處理程序)。

exec LIST

exec 的調用將導致 Perl 退出,並通過 lib$do_command 調用給定為 exec 參數的命令。如果參數以 '@' 或 '$'(而不是作為文件規格的一部分)開頭,則將其作為 DCL 命令執行。否則,命令行上的第一個標記被視為要運行的映像的文件規格,並嘗試調用它(使用 .Exe 和進程默認值來擴展文件規格),並將 exec 的其餘參數作為參數傳遞給它。如果標記沒有文件類型,並且與具有空類型的文件匹配,則將嘗試確定該文件是否是應使用 MCR 調用的可執行映像,或者是應作為命令程序傳遞給 DCL 的文本文件。

fork

雖然原則上 fork 運算符可以通過(並具有與 CRTL vfork() 函數相同的相當嚴格的限制)實現,並且某些內部支持也已經就位,但該實現從未完成,因此目前無法使用 fork。未來版本的 VMS 預計會提供真正的內核 fork(),並且基於解釋器線程的虛擬 fork 可能會在 VMS 上的 Perl 的未來版本中提供(請參閱 perlfork)。同時,請使用 system、反引號或管道文件處理程序來創建子進程。

getpwent
getpwnam
getpwuid

這些運算子獲取 perlfunc 中描述的信息,如果您有通過 sys$getuai 檢索指定用戶的 UAF (User Authorization File) 信息所需的權限。如果沒有,則只返回 $name$uid$gid 項目。 $dir 項目包含 VMS 語法中的登錄目錄,而 $comment 項目包含 Unix 語法中的登錄目錄。 $gcos 項目包含 UAF 記錄中的所有者字段。 $quota 項目未使用。

gmtime

如果您有一個可工作的 CRTL gmtime() 函數,或者如果邏輯名稱 SYS$TIMEZONE_DIFFERENTIAL 被定義為必須添加到 UTC 才能產生本地時間的秒數 (這個邏輯名稱在具有內置 UTC 支持的 VMS 版本中自動定義)。 如果這兩種情況都不是真的,將打印警告消息,並返回 undef

kill

在大多數情況下,kill 是通過未公開的系統服務 $SIGPRC 實現的,它與 $FORCEX 具有相同的調用序列,但在目標進程中引發異常,而不是強制它調用 $EXIT。 一般而言,kill 遵循 CRTL 的 kill() 函數的行為,但不像該函數可以從信號處理程序中調用。 此外,與某些版本的 CRTL 中的 kill 不同,Perl 的 kill 檢查傳遞的信號的有效性,並返回錯誤,而不是嘗試發送未識別的信號。

此外,負的信號值在 VMS 下並不會做任何特殊處理; 它們只是轉換為相應的正值。

qx//

請參閱上面關於 backticks 的條目。

select (系統調用)

如果 Perl 沒有使用套接字支持構建,則系統調用版本的 select 完全不可用。 如果存在套接字支持,則 select 的系統調用版本僅適用於附加到套接字的文件描述符。 它不會提供關於常規文件或管道的信息,因為 CRTL 的 select() 函數不提供此功能。

stat EXPR

由於 VMS 根據與 Unix 不同的方案跟蹤文件,因此無法真正表示 struct stat 中的 st_devst_ino 字段中的文件 ID。 但 Perl 盡力而為,它使用的值對於兩個不同的文件而言相當不可能相同。 不過,我們無法保證這一點,所以請注意。

system LIST

系統操作符建立一個子進程,並將其參數傳遞給子進程以作為 DCL 命令執行。由於子進程是通過 lib$spawn() 直接創建的,因此可以指定任何有效的 DCL 命令字符串。如果字符串以 '@' 開頭,則無條件地將其視為 DCL 命令。否則,如果第一個標記包含在文件規範中用作分隔符的字符(例如 ':' 或 ']'),則將嘗試使用默認類型 .Exe 和進程默認值對其進行展開,如果成功,則通過 MCR 調用生成的文件。這使您可以通過將文件規範傳遞給 system 來直接調用映像,這是一個常見的 Unix 風格慣用法。如果標記沒有文件類型,並且與空類型的文件匹配,則將嘗試確定該文件是否是應該使用 MCR 調用的可執行映像,或者是應該作為命令程序傳遞給 DCL 的文本文件。

如果 LIST 包含空字符串,system 將以與在 DCL 提示符處鍵入 SPAWN 相同的方式生成一個交互式 DCL 子進程。

Perl 在當前進程中的執行繼續之前等待子進程完成。如 perlfunc 中所述,system 的返回值是一個虛假的 "狀態",其遵循 POSIX 語義,除非啟用了 pragma use vmsish 'status';有關更多細節,請參見本文檔中對 $? 的描述。

time

time 返回的值是從 1970 年 01 月 01 日 00:00:00 起的秒偏移量(就像 CRTL 的 times() 例程),以使從 POSIX/Unix 世界輸入的代碼更容易處理。

times

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 行為。

utime LIST

此運算符僅更改 ODS-2 卷和未啟用訪問日期的 ODS-5 卷上的文件的修改時間(VMS 修訂日期)。在啟用訪問日期的 ODS-5 卷上,修改了真實的訪問時間。

waitpid PID,FLAGS

如果 PID 是由管道 open()(參見 open)啟動的子進程,waitpid 將等待該子進程,並將其最終狀態值返回給 $?。如果 PID 是以其他方式創建的子進程(例如,在調用 Perl 之前由 SPAWN 創建),waitpid 將僅每秒檢查一次進程是否已完成,並在完成時返回。(如果 PID 指定的進程不是當前進程的子進程,並且您使用 -w 選項調用 Perl,將發出警告。)

成功時返回 PID,錯誤時返回 -1。在所有情況下都忽略 FLAGS 參數。

Perl 變量

以下是適用於指定的“特殊”Perl變量的 VMS 特定信息,除了 perlvar 中的一般信息外。如果存在衝突,則此信息優先。

%ENV

%ENV 陣列的操作取決於邏輯名稱 PERL_ENV_TABLES 的翻譯。如果有定義,它應該是一個搜索列表,其中每個元素都指定了 %ENV 元素的位置。如果您告訴 Perl 讀取或設置元素 $ENV{name},則 Perl 使用 PERL_ENV_TABLES 的翻譯如下:

CRTL_ENV

此字符串告訴 Perl 查詢 CRTL 的內部 environ 陣列的鍵值對,使用 name 作為鍵。在大多數情況下,這只包含少量鍵,但如果 Perl 是通過 C exec[lv]e() 函數被調用的,例如某些嵌入式 Perl 應用程序或在像 GNV bash 這樣的 shell 下運行時,environ 陣列可能已由調用程序填充。

CLISYM_[LOCAL]

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_ENV_TABLES已設置,使得在邏輯名稱story被找到,而不是找到同名的CLI符號或CRTL environ元素時,Perl將會輸出ONCE UPON A TIME THERE WAS

%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本地環境數組的任何元素傳遞給不是通過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邏輯名稱被刪除,情況將有多糟糕。

目前,第一次使用keysvalues迭代%ENV時,將產生時間懲罰,因為所有邏輯名稱都會被讀取,以完全填充%ENV。後續迭代將不會重新讀取邏輯名稱,因此它們不會那麼慢,但它們也不會反映其他程序引起的邏輯名稱表的任何更改。

您需要注意表示進程永久文件的邏輯名稱,例如SYS$INPUTSYS$OUTPUT。這些邏輯名稱的翻譯以兩個字節的二進制值(0x1B 0x00)開頭,如果您要使用它,需要將其去除。(在以前的Perl版本中,無法獲取這些邏輯名稱的值,因為空字節充當了字符串結尾標記)

$!

$!的字符串值是由CRTL的strerror()函數返回的,因此它將包含VMS特定錯誤的VMS消息。 $!的數值是errno的值,除非errno是EVMSERR,此時$!包含vaxc$errno的值。設置$!始終將errno設置為指定的值。如果此值為EVMSERR,還會將vaxc$errno設置為4(NONAME-F-NOMSG),以便$!的字符串值不反映在設置$!之前的VMS錯誤消息。

$^E

此變量直接提供對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 結束值表示為 1 的 VMS 本地狀態,而從 2 到 255 的 Unix 值則由以下方程式編碼

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 中,以便 C 程序可以自動恢復原始父進程或子進程的退出狀態值(0 到 255),預期的 _POSIX_EXIT 行為。如果父進程和子進程的退出值都是非零的,那麼將假定這實際上是要通過的 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 程序提供了預期的信息。為了保持向後兼容性,它尚未成為默認值。

N.B. 設置 DECC$FILENAME_UNIX_REPORT 將隱式啟用 PERL_VMS_POSIX_EXIT

$|

對 I/O 流設置 $| 會導致每次寫入時將數據刷新到磁盤(即不僅僅刷新到文件的底層 RMS 緩衝區)。換句話說,這相當於從 C 調用 fflush() 和 fsync()。

具有 VMS 特定差異的標準模塊

SDBM_File

SDBM_File 在 VMS 上能夠正常工作。然而,它有一個小的不同之處。創建的數據庫目錄文件具有 .sdbm_dir 擴展名,而不是 .dir 擴展名。 .dir 文件是 VMS 文件系統目錄文件,將它們用於其他目的可能會導致不可接受的問題。

修訂日期

請參閱 git 儲存庫以查看修訂歷史。

作者

Charles Bailey bailey@cor.newman.upenn.edu Craig Berry craigberry@mac.com Dan Sugalski dan@sidhe.org John Malmberg wb8tyw@qsl.net