Command line parameters and environment variables/zh CN: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Command line parameters and environment variables/zh CN
| Old | New | Line | |
|---|---|---|---|
| 1 | + | 在大多数(交互式)操作系统中,可以通过[[Command-line interface|命令行界面]](CLI)启动[[Program|程序]],并允许向程序提供附带的数据。[[System unit|system]]单元(以及[[Object Pascal|`objPas`]])单元提供了一些基本的[[Function|函数]],用于访问这些通过命令行给出的数据。 | |
| 2 | + | ||
| 3 | + | ## Contents | |
| 4 | + | ||
| 5 | + | * 1 概述 | |
| 6 | + | * 1.1 术语 | |
| 7 | + | * 1.2 底层函数 | |
| 8 | + | * 2 命令行参数 | |
| 9 | + | * 2.1 基本用法 | |
| 10 | + | * 2.2 用户友好性 | |
| 11 | + | * 2.2.1 检测某个参数 | |
| 12 | + | * 2.2.2 读取参数值 | |
| 13 | + | * 2.2.3 校验参数 | |
| 14 | + | * 3 环境变量 | |
| 15 | + | ||
| 16 | + | ## 概述 | |
| 17 | + | ||
| 18 | + | 尽管这里介绍的是“命令行”参数和环境变量,但使用传给程序的数据,以及这里提及的例程和术语,都不需要实际用到命令行界面(CLI)。只是命令行界面更为直观,因此以下内容主要考虑 shell 用户的体验。 | |
| 19 | + | ||
| 20 | + | ### 术语 | |
| 21 | + | ||
| 22 | + | 选项(Option) | |
| 23 | + | 选项就是“是/否”。通常假定为如下形式 | |
| 24 | + | ||
| 25 | + | `‑‑dryrun`/`‑‑no‑dryrun` (分别为启用和禁用 dryrun 选项)。 选项也被称为“开关”。 | |
| 26 | + | ||
| 27 | + | 参数(Parameter) | |
| 28 | + | 参数是键值元组。在命令行中的形式如下: | |
| 29 | + | ||
| 30 | + | `‑‑processors 4`。 | |
| 31 | + | ||
| 32 | + | 实参(Argument) | |
| 33 | + | 实参就是一些简单的单词。在命令行中通常以空格分隔(某些 shell 中可由 IFS 变量定义)。从狭义上讲, _实参_ 指既非选项也不属于参数的所有单词。而从广义上讲,实参是指命令行中未以任何方式解释的所有单词。 | |
| 34 | + | ||
| 35 | + | 环境变量(Environment variable) | |
| 36 | + | 环境变量是指运行环境中打了标签的一块存储空间,都是“名称/值”对。 | |
| 37 | + | ||
| 38 | + | 请注意,命令行界面操作系统向程序传递的参数会先进行处理:比如转义后的换行符、文件重定向(管道或重定向文件)、对环境变量的赋值,都不会传给程序。 | |
| 39 | + | ||
| 40 | + | ### 底层函数 | |
| 41 | + | ||
| 42 | + | “选项”和“参数”的概念抽象程度已经较高,且存在不同的风格(如短选项和长选项),而 system 单元的目标是提供较为 _底层_ 的工作方式,因此只把全部命令行参数视为从零开始的枚举类型,未做任何解释处理。 | |
| 43 | + | ||
| 44 | + | 函数 [`paramStr`](<https://www.freepascal.org/docs-html/rtl/system/paramstr.html>) 用于返回第 n 个命令行参数。 `ParamStr(0)` 尝试返回可执行程序文件名,以及完整路径。 | |
| 45 | + | ||
| 46 | + | ## 命令行参数 | |
| 47 | + | ||
| 48 | + | ### 基本用法 | |
| 49 | + | ||
| 50 | + | Pascal 程序可以将 `paramStr` [[Function|函数]] 和 [`paramCount`](<https://www.freepascal.org/docs-html/rtl/system/paramcount.html>) 结合起来,以便访问命令行参数。 | |
| 51 | + | ||
| 52 | + | `ParamCount` 将返回给出的参数个数。 | |
| 53 | + | ``` | |
| 54 | + | program listArguments(input, output, stdErr); | |
| 55 | + | {$mode objFPC} | |
| 56 | + | var | |
| 57 | + | i: integer; | |
| 58 | + | begin | |
| 59 | + | writeLn({$ifDef Darwin} | |
| 60 | + | // Mac OS X 中的返回值依赖于调用方法 | |
| 61 | + | 'This program was invoked via: ', | |
| 62 | + | {$else} | |
| 63 | + | // 兼容 Turbo Pascal 的 paramStr(0) 返回程序所在位置 | |
| 64 | + | 'This program is/was stored at: ', | |
| 65 | + | {$endIf} | |
| 66 | + | paramStr(0)); | |
| 67 | + | ||
| 68 | + | for i := 1 to paramCount() do | |
| 69 | + | begin | |
| 70 | + | writeLn(i:2, '. argument: ', paramStr(i)); | |
| 71 | + | end; | |
| 72 | + | end. | |
| 73 | + | ``` | |
| 74 | + | ||
| 75 | + | 在非 Mac OS X 系统中,上述程序的运行结果可能如下: | |
| 76 | + | ``` | |
| 77 | + | $ ./listArguments foo bar able | |
| 78 | + | This program is/was stored at: /tmp/listArguments | |
| 79 | + | 1. argument: foo | |
| 80 | + | 2. argument: bar | |
| 81 | + | 3. argument: able | |
| 82 | + | ``` | |
| 83 | + | ||
| 84 | + | RTL 的 system 单元提供了一个 `paramStr`,返回的是[[Shortstring|`shortString`]] 类型的字符串。`shortString` 类型的实现方式限制了其长度在255个字符以内。 但是用户可能会给出长度超限的命令行参数。 为了访问全部命令行参数,[`objPas` 单元](<https://www.freepascal.org/docs-html/rtl/objpas/index.html>) 重新定义了 `paramStr`,返回类型换成了 [[Ansistring|`ansiString`]],这样长度就不受限了。在 [[Mode ObjFPC|`{$mode objFPC}`]](第2行)和 [[Mode Delphi|`{$mode Delphi}`]] 编译模式下,会自动包含 `objPas` 单元。 | |
| 85 | + | ||
| 86 | + | 提供 `paramStr` 函数是为了与 Turbo Pascal 保持兼容。在 Turbo Pascal 中,`paramStr(0)` 返回程序的所在位置。但这需要操作系统的支持。尤其值得注意的是,在 Mac OS X 系统中,`paramStr(0)` 的返回值取决于调用方法,即应用程序的启动方式。 | |
| 87 | + | ||
| 88 | + |  **Note:** 由于 `paramStr(0)` 依赖于操作系统的支持,所以并不适用于 [[Multiplatform Programming Guide|跨平台应用]]。 | |
| 89 | + | ||
| 90 | + |  **Note:** 在类 Unix 系统中,只用字符串不可能确定文件的位置。可能有多个硬链接指向同一个i节点,文件可能已删除,文件路径可能已有部分改动,以及其他细节问题。 | |
| 91 | + | ||
| 92 | + | 因此,在类 Unix 系统中 `paramStr(0)` 无效。 | |
| 93 | + | ||
| 94 | + | [`getOpts` 单元](<https://www.freepascal.org/docs-html/rtl/getopts/index.html>)提供了其他一些函数,在以特定格式调用程序时可用于解析命令行参数。 | |
| 95 | + | ||
| 96 | + | ### 用户友好性 | |
| 97 | + | ||
| 98 | + | 设计良好的程序在收到错误参数时应能给出帮助信息,并且应该遵循一种通用的参数传递方式。 FPC 的 [unit `custApp`](<https://www.freepascal.org/docs-html/fcl/custapp/index.html>) 单元提供了一个 [`TCustomApplication`](<https://www.freepascal.org/docs-html/fcl/custapp/tcustomapplication.html>) 类,可用于轻松检查和读取命令行参数。当然,用 `paramStr` 和 `paramCount` 直接访问也没问题。 | |
| 99 | + | ||
| 100 | + | [[LCL]] 应用程序都会自动使用 TCustomApplication。Application 对象就是 TCustomApplication 的实例。 | |
| 101 | + | ||
| 102 | + | 若要编写非 LCL 程序,那么先在 [[Lazarus]] 中新建“控制台应用程序”类型的项目。这会创建一个包含某些实用功能的 project1.lpr,这些功能几乎所有程序都需要用到。接下来,请定位到 `doRun` 方法。 | |
| 103 | + | ||
| 104 | + | #### 检测某个参数 | |
| 105 | + | ||
| 106 | + | TCustomApplication 可以通过名称访问命令行参数。 比如用户给出通用的求助参数 _-h_ 时,程序应该打印出一条帮助文字。 _-h_ 是个短选项。 长选项则为 _\--help_ 。 若要测试用户在调用程序时是否带有 _-h_ 或 _\--help_ 参数,可以使用如下代码: | |
| 107 | + | ``` | |
| 108 | + | if hasOption('h', 'help') then | |
| 109 | + | begin | |
| 110 | + | writeHelp; | |
| 111 | + | halt; | |
| 112 | + | end; | |
| 113 | + | ``` | |
| 114 | + | ||
| 115 | + |  **Note:** 在 LCL 窗体中,必须在 `hasOption` 前面加上 `application.`。 | |
| 116 | + | ||
| 117 | + | 例如: | |
| 118 | + | ``` | |
| 119 | + | if application.hasOption('h', 'help') then | |
| 120 | + | begin | |
| 121 | + | writeHelp; | |
| 122 | + | halt; | |
| 123 | + | end; | |
| 124 | + | ``` | |
| 125 | + | ||
| 126 | + | 如果只支持短选项,请用以下代码: | |
| 127 | + | ``` | |
| 128 | + | if hasOption('h', '') then | |
| 129 | + | ``` | |
| 130 | + | ||
| 131 | + | 若只支持长选项,则用: | |
| 132 | + | ``` | |
| 133 | + | if hasOption('help') then | |
| 134 | + | ``` | |
| 135 | + | ||
| 136 | + | #### 读取参数值 | |
| 137 | + | ||
| 138 | + | 每个命令行参数可以给定一个值。 例如: | |
| 139 | + | ``` | |
| 140 | + | $ project1 -f filename | |
| 141 | + | ``` | |
| 142 | + | ||
| 143 | + | 或者长格式: | |
| 144 | + | ``` | |
| 145 | + | $ project1 --file=filename | |
| 146 | + | ``` | |
| 147 | + | ||
| 148 | + | 读取 `filename` : | |
| 149 | + | ``` | |
| 150 | + | writeLn('f=', getOptionValue('f', 'file')); | |
| 151 | + | ``` | |
| 152 | + | ||
| 153 | + | 注意:如果报错“`Option at position 1 needs an argument : f.`”,则是在调用 | |
| 154 | + | ``` | |
| 155 | + | [checkOptions](<https://www.freepascal.org/docs-html/fcl/custapp/tcustomapplication.checkoptions.html>) 时忘记加入该选项了。 | |
| 156 | + | ``` | |
| 157 | + | ||
| 158 | + | #### 校验参数 | |
| 159 | + | ||
| 160 | + | 命令行参数就是一条内容随意的文本,因此用户输入很容易出错。 因此,必须对命令行参数作语法检查。用 [CheckOptions](<http://lazarus-ccr.sourceforge.net/docs/fcl/custapp/tcustomapplication.checkoptions.html> "doc:fcl/custapp/tcustomapplication.checkoptions.html") 方法即可完成检查。 | |
| 161 | + | ||
| 162 | + | 可定义允许哪些参数及哪些参数需要有值,在发生语法错误时可获取错误消息和出错的选项,以便打印出帮助信息和错误细节。 | |
| 163 | + | ||
| 164 | + | 示例: | |
| 165 | + | ``` | |
| 166 | + | errorMsg := checkOptions('hf:', 'help file:'); | |
| 167 | + | ``` | |
| 168 | + | ||
| 169 | + | 上述代码允许传递短选项 `‑f value` 和 `‑h`, 还允许 `‑‑help` 或 `‑‑file=filename`。 但不允许 `‑‑help` 带有值,也不允许 `‑‑file` 没有值。 | |
| 170 | + | ||
| 171 | + | 参数示例: | |
| 172 | + | ``` | |
| 173 | + | procedure TMainForm.FormShow(Sender: TObject); | |
| 174 | + | var | |
| 175 | + | I: Integer; | |
| 176 | + | Params : TStringList | |
| 177 | + | LongOpts : array [1..2] of string = ('debug-sync', 'config-dir:'); | |
| 178 | + | begin | |
| 179 | + | Params := TStringList.Create; | |
| 180 | + | try | |
| 181 | + | Application.GetNonOptions('hgo:', LongOpts, Params); | |
| 182 | + | for I := 0 to Params.Count -1 do | |
| 183 | + | debugln('Extra Param ' + inttostr(I) + ' is ' + Params[I]); } | |
| 184 | + | finally | |
| 185 | + | FreeAndNil(Params); | |
| 186 | + | end; | |
| 187 | + | end; | |
| 188 | + | ``` | |
| 189 | + | ||
| 190 | + | 以上例程会查找参数,且不会和命令行开关或选项混淆。 应用程序还接受 --debug-sync 和 --config-dir=somedir 开关,只是没有打印出来。 | |
| 191 | + | ||
| 192 | + | 请注意,Application.GetNonOptions() 方法以数组类型的长选项为参数,而 Application.CheckOptions 方法的参数数据相同却只认字符串。 有点遗憾! | |
| 193 | + | ||
| 194 | + | ## 环境变量 | |
| 195 | + | ||
| 196 | + | [[sysutils|`sysUtils`]] 单元定义了3个读取环境变量的基本函数。 | |
| 197 | + | ||
| 198 | + | * [`getEnvironmentVariableCount`](<https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariablecount.html>) 返回全部环境变量的数量。 | |
| 199 | + | * [`getEnvironmentString`](<https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentstring.html>) 按索引编号返回某个环境变量。 | |
| 200 | + | * [`getEnvironmentVariable`](<https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariable.html>) 按键值返回某个环境变量。 | |
| 201 | + | ||
| 202 | + | 示例: | |
| 203 | + | ``` | |
| 204 | + | program environmentVariablesList(input, output, stdErr); | |
| 205 | + | uses | |
| 206 | + | sysUtils; | |
| 207 | + | var | |
| 208 | + | i: integer; | |
| 209 | + | begin | |
| 210 | + | for i := 0 to getEnvironmentVariableCount() - 1 do | |
| 211 | + | begin | |
| 212 | + | writeLn(getEnvironmentString(i)); | |
| 213 | + | end; | |
| 214 | + | end. | |
| 215 | + | ``` | |
| 216 | + | ||
| 217 | + | 另外还有一种可能的方式,就是将所有环境变量载入 [`tStringList`](<https://www.freepascal.org/docs-html/rtl/classes/tstringlist.html>)对象,以便访问键值对。 字符串列表会自动处理分隔符,默认为[[=|等号 (`'='`)]]。 | |
| 218 | + | ``` | |
| 219 | + | program environmentVariablesSorted(input, output, stdErr); | |
| 220 | + | {$mode objFPC} | |
| 221 | + | uses | |
| 222 | + | classes, sysUtils; | |
| 223 | + | var | |
| 224 | + | i: integer; | |
| 225 | + | environmentVariables: tStringList; | |
| 226 | + | begin | |
| 227 | + | environmentVariables := tStringList.create(); | |
| 228 | + | ||
| 229 | + | try | |
| 230 | + | // load all variables to string list | |
| 231 | + | for i := 0 to getEnvironmentVariableCount() - 1 do | |
| 232 | + | begin | |
| 233 | + | environmentVariables.append(getEnvironmentString(i)); | |
| 234 | + | end; | |
| 235 | + | ||
| 236 | + | environmentVariables.sort; | |
| 237 | + | for i := 0 to environmentVariables.count - 1 do | |
| 238 | + | begin | |
| 239 | + | writeLn(environmentVariables.names[i]:20, | |
| 240 | + | ' = ', | |
| 241 | + | environmentVariables.valueFromIndex[i]); | |
| 242 | + | end; | |
| 243 | + | finally | |
| 244 | + | environmentVariables.free; | |
| 245 | + | end; | |
| 246 | + | end. | |
| 247 | + | ``` | |
| 248 | + | ||
| 249 | + | 还可以采用 `tCustomApplication` 的 [[Method|方法]],可一次性将全部环境变量读取为一个[[TStringList|`tStringList`]]。大致如下所示: | |
| 250 | + | ``` | |
| 251 | + | var | |
| 252 | + | environmentVariables: tStringList; | |
| 253 | + | begin | |
| 254 | + | environmentVariables := tStringList.create(); | |
| 255 | + | try | |
| 256 | + | application.getEnvironmentList(environmentVariables); | |
| 257 | + | … | |
| 258 | + | finally | |
| 259 | + | environmentVariables.free; | |
| 260 | + | end; | |
| 261 | + | end; | |
| 262 | + | ``` | |
| 263 | + | *[IFS]: internal field separator | |
| 264 | + | ||
| 265 | + | --- | |
| 266 | + | *From the Free Pascal wiki, page "Command line parameters and environment variables/zh CN", as the Internet Archive kept it on 2025-01-13. 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](/wiki-contact).* |
