维基副本镜像

这是 pascal.chat 维基的镜像。编辑、历史记录和登录都在那里。

Command line parameters and environment variables/zh CN

English · Español · Français · Suomi · Русский · 中文(中国)

在大多数(交互式)操作系统中,可以通过命令行界面(CLI)启动程序,并允许向程序提供附带的数据。system单元(以及objPas)单元提供了一些基本的函数,用于访问这些通过命令行给出的数据。

概述

尽管这里介绍的是“命令行”参数和环境变量,但使用传给程序的数据,以及这里提及的例程和术语,都不需要实际用到命令行界面(CLI)。只是命令行界面更为直观,因此以下内容主要考虑 shell 用户的体验。

术语

选项(Option)

选项就是“是/否”。通常假定为如下形式

‑‑dryrun/‑‑no‑dryrun (分别为启用和禁用 dryrun 选项)。 选项也被称为“开关”。

参数(Parameter)

参数是键值元组。在命令行中的形式如下:

‑‑processors 4。

实参(Argument)

实参就是一些简单的单词。在命令行中通常以空格分隔(某些 shell 中可由 IFS 变量定义)。从狭义上讲, 实参 指既非选项也不属于参数的所有单词。而从广义上讲,实参是指命令行中未以任何方式解释的所有单词。

环境变量(Environment variable)

环境变量是指运行环境中打了标签的一块存储空间,都是“名称/值”对。

请注意,命令行界面操作系统向程序传递的参数会先进行处理:比如转义后的换行符、文件重定向(管道或重定向文件)、对环境变量的赋值,都不会传给程序。

底层函数

“选项”和“参数”的概念抽象程度已经较高,且存在不同的风格(如短选项和长选项),而 system 单元的目标是提供较为 底层 的工作方式,因此只把全部命令行参数视为从零开始的枚举类型,未做任何解释处理。

函数 paramStr 用于返回第 n 个命令行参数。 ParamStr(0) 尝试返回可执行程序文件名,以及完整路径。

命令行参数

基本用法

Pascal 程序可以将 paramStr 函数 和 paramCount 结合起来,以便访问命令行参数。

ParamCount 将返回给出的参数个数。

pascal
program listArguments(input, output, stdErr);
    {$mode objFPC}
    var
        i: integer;
    begin
        writeLn({$ifDef Darwin}
                // Mac OS X 中的返回值依赖于调用方法
                'This program was invoked via: ',
            {$else}
                // 兼容 Turbo Pascal 的 paramStr(0) 返回程序所在位置
                'This program is/was stored at: ',
            {$endIf}
            paramStr(0));
        
        for i := 1 to paramCount() do
        begin
            writeLn(i:2, '. argument: ', paramStr(i));
        end;
    end.

在非 Mac OS X 系统中,上述程序的运行结果可能如下:

pascal
$ ./listArguments foo bar able
    This program is/was stored at: /tmp/listArguments
     1. argument: foo
     2. argument: bar
     3. argument: able

RTL 的 system 单元提供了一个 paramStr,返回的是shortString 类型的字符串。shortString 类型的实现方式限制了其长度在255个字符以内。 但是用户可能会给出长度超限的命令行参数。 为了访问全部命令行参数,objPas 单元 重新定义了 paramStr,返回类型换成了 ansiString,这样长度就不受限了。在 {$mode objFPC}(第2行)和 {$mode Delphi} 编译模式下,会自动包含 objPas 单元。

提供 paramStr 函数是为了与 Turbo Pascal 保持兼容。在 Turbo Pascal 中,paramStr(0) 返回程序的所在位置。但这需要操作系统的支持。尤其值得注意的是,在 Mac OS X 系统中,paramStr(0) 的返回值取决于调用方法,即应用程序的启动方式。

https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png Note: 由于 paramStr(0) 依赖于操作系统的支持,所以并不适用于 跨平台应用。

https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png Note: 在类 Unix 系统中,只用字符串不可能确定文件的位置。可能有多个硬链接指向同一个i节点,文件可能已删除,文件路径可能已有部分改动,以及其他细节问题。

因此,在类 Unix 系统中 paramStr(0) 无效。

getOpts 单元提供了其他一些函数,在以特定格式调用程序时可用于解析命令行参数。

用户友好性

设计良好的程序在收到错误参数时应能给出帮助信息,并且应该遵循一种通用的参数传递方式。 FPC 的 unit custApp 单元提供了一个 TCustomApplication 类,可用于轻松检查和读取命令行参数。当然,用 paramStr 和 paramCount 直接访问也没问题。

LCL 应用程序都会自动使用 TCustomApplication。Application 对象就是 TCustomApplication 的实例。

若要编写非 LCL 程序,那么先在 Lazarus 中新建“控制台应用程序”类型的项目。这会创建一个包含某些实用功能的 project1.lpr,这些功能几乎所有程序都需要用到。接下来,请定位到 doRun 方法。

检测某个参数

TCustomApplication 可以通过名称访问命令行参数。 比如用户给出通用的求助参数 -h 时,程序应该打印出一条帮助文字。 -h 是个短选项。 长选项则为 --help 。 若要测试用户在调用程序时是否带有 -h 或 --help 参数,可以使用如下代码:

pascal
if hasOption('h', 'help') then
    begin
        writeHelp;
        halt;
    end;

https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png Note: 在 LCL 窗体中,必须在 hasOption 前面加上 application.。

例如:

pascal
if application.hasOption('h', 'help') then
    begin
        writeHelp;
        halt;
    end;

如果只支持短选项,请用以下代码:

pascal
if hasOption('h', '') then

若只支持长选项,则用:

pascal
if hasOption('help') then

读取参数值

每个命令行参数可以给定一个值。 例如:

pascal
$ project1 -f filename

或者长格式:

pascal
$ project1 --file=filename

读取 filename :

pascal
writeLn('f=', getOptionValue('f', 'file'));

注意:如果报错“Option at position 1 needs an argument : f.”,则是在调用

pascal
[checkOptions](<https://www.freepascal.org/docs-html/fcl/custapp/tcustomapplication.checkoptions.html>) 时忘记加入该选项了。

校验参数

命令行参数就是一条内容随意的文本,因此用户输入很容易出错。 因此,必须对命令行参数作语法检查。用 CheckOptions 方法即可完成检查。

可定义允许哪些参数及哪些参数需要有值,在发生语法错误时可获取错误消息和出错的选项,以便打印出帮助信息和错误细节。

示例:

pascal
errorMsg := checkOptions('hf:', 'help file:');

上述代码允许传递短选项 ‑f value 和 ‑h, 还允许 ‑‑help 或 ‑‑file=filename。 但不允许 ‑‑help 带有值,也不允许 ‑‑file 没有值。

参数示例:

pascal
procedure TMainForm.FormShow(Sender: TObject);
    var
        I: Integer;
        Params : TStringList
        LongOpts : array [1..2] of string = ('debug-sync', 'config-dir:');
    begin
        Params := TStringList.Create;
        try
            Application.GetNonOptions('hgo:', LongOpts, Params);
            for I := 0 to Params.Count -1 do
                debugln('Extra Param ' + inttostr(I) + ' is ' + Params[I]);  }
        finally
            FreeAndNil(Params);
        end;
    end;

以上例程会查找参数,且不会和命令行开关或选项混淆。 应用程序还接受 --debug-sync 和 --config-dir=somedir 开关,只是没有打印出来。

请注意,Application.GetNonOptions() 方法以数组类型的长选项为参数,而 Application.CheckOptions 方法的参数数据相同却只认字符串。 有点遗憾!

环境变量

sysUtils 单元定义了3个读取环境变量的基本函数。

示例:

pascal
program environmentVariablesList(input, output, stdErr);
    uses
        sysUtils;
    var
        i: integer;
    begin
        for i := 0 to getEnvironmentVariableCount() - 1 do
        begin
            writeLn(getEnvironmentString(i));
        end;
    end.

另外还有一种可能的方式,就是将所有环境变量载入 tStringList对象,以便访问键值对。 字符串列表会自动处理分隔符,默认为等号 ('=')。

pascal
program environmentVariablesSorted(input, output, stdErr);
    {$mode objFPC}
    uses
        classes, sysUtils;
    var
        i: integer;
        environmentVariables: tStringList;
    begin
        environmentVariables := tStringList.create();
        
        try
            // load all variables to string list
            for i := 0 to getEnvironmentVariableCount() - 1 do
            begin
                environmentVariables.append(getEnvironmentString(i));
            end;
            
            environmentVariables.sort;
            for i := 0 to environmentVariables.count - 1 do
            begin
                writeLn(environmentVariables.names[i]:20,
                    ' = ',
                    environmentVariables.valueFromIndex[i]);
            end;
        finally
            environmentVariables.free;
        end;
    end.

还可以采用 tCustomApplication 的 方法,可一次性将全部环境变量读取为一个tStringList。大致如下所示:

pascal
var
        environmentVariables: tStringList;
    begin
        environmentVariables := tStringList.create();
        try
            application.getEnvironmentList(environmentVariables);
            ???
        finally 
            environmentVariables.free;
        end;
    end;

*[IFS]: internal field separator


From the Free Pascal wiki, page "Command line parameters and environment variables/zh CN", as the Internet Archive kept it on 2025-01-13, last changed on 2024-12-09. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

版本3,最后由WikiArchive于修改。