Sign in Join
Wiki copytemporary

Command line parameters and environment variables/zh CN: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11在大多数(交互式)操作系统中,可以通过[[Command-line interface|命令行界面]](CLI)启动[[Program|程序]],并允许向程序提供附带的数据。[[System unit|system]]单元(以及[[Object Pascal|`objPas`]])单元提供了一些基本的[[Function|函数]],用于访问这些通过命令行给出的数据。
22
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-
163## 概述
174
185尽管这里介绍的是“命令行”参数和环境变量,但使用传给程序的数据,以及这里提及的例程和术语,都不需要实际用到命令行界面(CLI)。只是命令行界面更为直观,因此以下内容主要考虑 shell 用户的体验。
196
207### 术语
218
22-选项(Option)
23- 选项就是“是/否”。通常假定为如下形式
9+**选项(Option)**
2410
11+选项就是“是/否”。通常假定为如下形式
12+
2513`‑‑dryrun`/`‑‑no‑dryrun` (分别为启用和禁用 dryrun 选项)。 选项也被称为“开关”。
2614
27-参数(Parameter)
28- 参数是键值元组。在命令行中的形式如下:
15+**参数(Parameter)**
2916
17+参数是键值元组。在命令行中的形式如下:
18+
3019`‑‑processors 4`。
3120
32-实参(Argument)
33- 实参就是一些简单的单词。在命令行中通常以空格分隔(某些 shell 中可由 IFS 变量定义)。从狭义上讲, _实参_ 指既非选项也不属于参数的所有单词。而从广义上讲,实参是指命令行中未以任何方式解释的所有单词。
21+**实参(Argument)**
3422
35-环境变量(Environment variable)
36- 环境变量是指运行环境中打了标签的一块存储空间,都是“名称/值”对。
23+实参就是一些简单的单词。在命令行中通常以空格分隔(某些 shell 中可由 IFS 变量定义)。从狭义上讲, _实参_ 指既非选项也不属于参数的所有单词。而从广义上讲,实参是指命令行中未以任何方式解释的所有单词。
24+
25+**环境变量(Environment variable)**
26+
27+环境变量是指运行环境中打了标签的一块存储空间,都是“名称/值”对。
3728
3829请注意,命令行界面操作系统向程序传递的参数会先进行处理:比如转义后的换行符、文件重定向(管道或重定向文件)、对环境变量的赋值,都不会传给程序。
3930
4031### 底层函数
4132
4233“选项”和“参数”的概念抽象程度已经较高,且存在不同的风格(如短选项和长选项),而 system 单元的目标是提供较为 _底层_ 的工作方式,因此只把全部命令行参数视为从零开始的枚举类型,未做任何解释处理。
4334
4435函数 [`paramStr`](<https://www.freepascal.org/docs-html/rtl/system/paramstr.html>) 用于返回第 n 个命令行参数。 `ParamStr(0)` 尝试返回可执行程序文件名,以及完整路径。
4536
4637## 命令行参数
4738
4839### 基本用法
4940
5041Pascal 程序可以将 `paramStr` [[Function|函数]] 和 [`paramCount`](<https://www.freepascal.org/docs-html/rtl/system/paramcount.html>) 结合起来,以便访问命令行参数。
5142
5243`ParamCount` 将返回给出的参数个数。
5344```
5445program listArguments(input, output, stdErr);
5546 {$mode objFPC}
5647 var
5748 i: integer;
5849 begin
5950 writeLn({$ifDef Darwin}
6051 // Mac OS X 中的返回值依赖于调用方法
6152 'This program was invoked via: ',
6253 {$else}
6354 // 兼容 Turbo Pascal 的 paramStr(0) 返回程序所在位置
6455 'This program is/was stored at: ',
6556 {$endIf}
6657 paramStr(0));
6758
6859 for i := 1 to paramCount() do
6960 begin
7061 writeLn(i:2, '. argument: ', paramStr(i));
7162 end;
7263 end.
7364```
7465
7566在非 Mac OS X 系统中,上述程序的运行结果可能如下:
7667```
7768$ ./listArguments foo bar able
7869 This program is/was stored at: /tmp/listArguments
7970 1. argument: foo
8071 2. argument: bar
8172 3. argument: able
8273```
8374
8475RTL 的 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` 单元。
8576
8677提供 `paramStr` 函数是为了与 Turbo Pascal 保持兼容。在 Turbo Pascal 中,`paramStr(0)` 返回程序的所在位置。但这需要操作系统的支持。尤其值得注意的是,在 Mac OS X 系统中,`paramStr(0)` 的返回值取决于调用方法,即应用程序的启动方式。
8778
8879![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Note:** 由于 `paramStr(0)` 依赖于操作系统的支持,所以并不适用于 [[Multiplatform Programming Guide|跨平台应用]]。
8980
9081![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Note:** 在类 Unix 系统中,只用字符串不可能确定文件的位置。可能有多个硬链接指向同一个i节点,文件可能已删除,文件路径可能已有部分改动,以及其他细节问题。
9182
9283因此,在类 Unix 系统中 `paramStr(0)` 无效。
9384
9485[`getOpts` 单元](<https://www.freepascal.org/docs-html/rtl/getopts/index.html>)提供了其他一些函数,在以特定格式调用程序时可用于解析命令行参数。
9586
9687### 用户友好性
9788
9889设计良好的程序在收到错误参数时应能给出帮助信息,并且应该遵循一种通用的参数传递方式。 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` 直接访问也没问题。
9990
10091[[LCL]] 应用程序都会自动使用 TCustomApplication。Application 对象就是 TCustomApplication 的实例。
10192
10293若要编写非 LCL 程序,那么先在 [[Lazarus]] 中新建“控制台应用程序”类型的项目。这会创建一个包含某些实用功能的 project1.lpr,这些功能几乎所有程序都需要用到。接下来,请定位到 `doRun` 方法。
10394
10495#### 检测某个参数
10596
10697TCustomApplication 可以通过名称访问命令行参数。 比如用户给出通用的求助参数 _-h_ 时,程序应该打印出一条帮助文字。 _-h_ 是个短选项。 长选项则为 _\--help_ 。 若要测试用户在调用程序时是否带有 _-h_ 或 _\--help_ 参数,可以使用如下代码:
10798```
10899if hasOption('h', 'help') then
109100 begin
110101 writeHelp;
111102 halt;
112103 end;
113104```
114105
115106![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Note:** 在 LCL 窗体中,必须在 `hasOption` 前面加上 `application.`。
116107
117108例如:
118109```
119110if application.hasOption('h', 'help') then
120111 begin
121112 writeHelp;
122113 halt;
123114 end;
124115```
125116
126117如果只支持短选项,请用以下代码:
127118```
128119if hasOption('h', '') then
129120```
130121
131122若只支持长选项,则用:
132123```
133124if hasOption('help') then
134125```
135126
136127#### 读取参数值
137128
138129每个命令行参数可以给定一个值。 例如:
139130```
140131$ project1 -f filename
141132```
142133
143134或者长格式:
144135```
145136$ project1 --file=filename
146137```
147138
148139读取 `filename` :
149140```
150141writeLn('f=', getOptionValue('f', 'file'));
151142```
152143
153144注意:如果报错“`Option at position 1 needs an argument : f.`”,则是在调用
154145```
155146[checkOptions](<https://www.freepascal.org/docs-html/fcl/custapp/tcustomapplication.checkoptions.html>) 时忘记加入该选项了。
156147```
157148
158149#### 校验参数
159150
160151命令行参数就是一条内容随意的文本,因此用户输入很容易出错。 因此,必须对命令行参数作语法检查。用 [CheckOptions](<http://lazarus-ccr.sourceforge.net/docs/fcl/custapp/tcustomapplication.checkoptions.html> "doc:fcl/custapp/tcustomapplication.checkoptions.html") 方法即可完成检查。
161152
162153可定义允许哪些参数及哪些参数需要有值,在发生语法错误时可获取错误消息和出错的选项,以便打印出帮助信息和错误细节。
163154
164155示例:
165156```
166157errorMsg := checkOptions('hf:', 'help file:');
167158```
168159
169160上述代码允许传递短选项 `‑f value` 和 `‑h`, 还允许 `‑‑help` 或 `‑‑file=filename`。 但不允许 `‑‑help` 带有值,也不允许 `‑‑file` 没有值。
170161
171162参数示例:
172163```
173164procedure TMainForm.FormShow(Sender: TObject);
174165 var
175166 I: Integer;
176167 Params : TStringList
177168 LongOpts : array [1..2] of string = ('debug-sync', 'config-dir:');
178169 begin
179170 Params := TStringList.Create;
180171 try
181172 Application.GetNonOptions('hgo:', LongOpts, Params);
182173 for I := 0 to Params.Count -1 do
183174 debugln('Extra Param ' + inttostr(I) + ' is ' + Params[I]); }
184175 finally
185176 FreeAndNil(Params);
186177 end;
187178 end;
188179```
189180
190181以上例程会查找参数,且不会和命令行开关或选项混淆。 应用程序还接受 --debug-sync 和 --config-dir=somedir 开关,只是没有打印出来。
191182
192183请注意,Application.GetNonOptions() 方法以数组类型的长选项为参数,而 Application.CheckOptions 方法的参数数据相同却只认字符串。 有点遗憾!
193184
194185## 环境变量
195186
196187[[sysutils|`sysUtils`]] 单元定义了3个读取环境变量的基本函数。
197188
198189 * [`getEnvironmentVariableCount`](<https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariablecount.html>) 返回全部环境变量的数量。
199190 * [`getEnvironmentString`](<https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentstring.html>) 按索引编号返回某个环境变量。
200191 * [`getEnvironmentVariable`](<https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariable.html>) 按键值返回某个环境变量。
201192
202193示例:
203194```
204195program environmentVariablesList(input, output, stdErr);
205196 uses
206197 sysUtils;
207198 var
208199 i: integer;
209200 begin
210201 for i := 0 to getEnvironmentVariableCount() - 1 do
211202 begin
212203 writeLn(getEnvironmentString(i));
213204 end;
214205 end.
215206```
216207
217208另外还有一种可能的方式,就是将所有环境变量载入 [`tStringList`](<https://www.freepascal.org/docs-html/rtl/classes/tstringlist.html>)对象,以便访问键值对。 字符串列表会自动处理分隔符,默认为[[=|等号 (`'='`)]]。
218209```
219210program environmentVariablesSorted(input, output, stdErr);
220211 {$mode objFPC}
221212 uses
222213 classes, sysUtils;
223214 var
224215 i: integer;
225216 environmentVariables: tStringList;
226217 begin
227218 environmentVariables := tStringList.create();
228219
229220 try
230221 // load all variables to string list
231222 for i := 0 to getEnvironmentVariableCount() - 1 do
232223 begin
233224 environmentVariables.append(getEnvironmentString(i));
234225 end;
235226
236227 environmentVariables.sort;
237228 for i := 0 to environmentVariables.count - 1 do
238229 begin
239230 writeLn(environmentVariables.names[i]:20,
240231 ' = ',
241232 environmentVariables.valueFromIndex[i]);
242233 end;
243234 finally
244235 environmentVariables.free;
245236 end;
246237 end.
247238```
248239
249240还可以采用 `tCustomApplication` 的 [[Method|方法]],可一次性将全部环境变量读取为一个[[TStringList|`tStringList`]]。大致如下所示:
250241```
251242var
252243 environmentVariables: tStringList;
253244 begin
254245 environmentVariables := tStringList.create();
255246 try
256247 application.getEnvironmentList(environmentVariables);
257248 …
258249 finally
259250 environmentVariables.free;
260251 end;
261252 end;
262253```
263254 *[IFS]: internal field separator
264255
265256---
266257*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](/wiki-contact).*

Back to history