Command line parameters and environment variables: 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
| Old | New | Line | |
|---|---|---|---|
| 1 | + | On most (interactive) operating systems [[Program|programs]] can be started via a [[Command-line interface|command line interface]] (CLI) that allows supplying the program with additional data. The [[System unit|system]] (and [[Object Pascal|objPas]]) units provide basic [[Function|functions]] in order to access command-line supplied data. | |
| 2 | + | ||
| 3 | + | ## Overview | |
| 4 | + | ||
| 5 | + | Although this page speaks of *command-line* parameters and environment variables, an actual command line interface (CLI) is not a requirement to use such data, nor to use the routines and terminology described here. However, since a CLI is more tangible the following instructions mainly to refer to a shell’s user experience. | |
| 6 | + | ||
| 7 | + | ### Terminology | |
| 8 | + | ||
| 9 | + | Option | |
| 10 | + | Options are yes/no flags. They usually assume the form `‑‑dryrun`/`‑‑no‑dryrun` (dryrun enabled or disabled respectively). Options are also called “switches”. | |
| 11 | + | ||
| 12 | + | Parameter | |
| 13 | + | Parameters are key-value-tuples. On the command line they look like: `‑‑processors 4`. | |
| 14 | + | ||
| 15 | + | Argument | |
| 16 | + | Arguments are simple words. On the command line they are usually separated by spaces (confer the <abbr title="internal field separator">IFS</abbr> variable in some shells). In a narrow sense *arguments* are all supplied words that are neither options or (part of) parameters. In general, though, arguments are all words on the command line not being interpreted in any way. | |
| 17 | + | ||
| 18 | + | Environment variable | |
| 19 | + | Environment variables refer to a labeled piece of storage in the environment. They are name-value-pairs. | |
| 20 | + | ||
| 21 | + | Note, the CLI/OS provides the program only with arguments it is supposed to get: For example, escaped newline characters, file redirection (pipes or to a file), and assignment of environment variables will not be forwarded to the program. | |
| 22 | + | ||
| 23 | + | ### Basics | |
| 24 | + | ||
| 25 | + | Since the notion of “option” and “parameters” are already kind of high-level, different styles exist (short options vs. long options), and the system unit aims to provide the *basic* means to work with, all command line arguments are simply enumerated starting from zero. No interpretation is done. | |
| 26 | + | ||
| 27 | + | The function [`paramStr`](https://www.freepascal.org/docs-html/rtl/system/paramstr.html) returns the n-th argument on the command line. `ParamStr(0)` tries returning the name, and possibly full path to the executable program file. | |
| 28 | + | ||
| 29 | + | ## Command line parameters | |
| 30 | + | ||
| 31 | + | ### The basics | |
| 32 | + | ||
| 33 | + | A Pascal program can a access command line arguments by using the `paramStr` [[Function|function]] in conjunction with [`paramCount`](https://www.freepascal.org/docs-html/rtl/system/paramcount.html). `ParamCount` returns the number of supplied arguments. | |
| 34 | + | ||
| 35 | + | ``` pascal | |
| 36 | + | program listArguments(input, output, stdErr); | |
| 37 | + | {$mode objFPC} | |
| 38 | + | var | |
| 39 | + | i: integer; | |
| 40 | + | begin | |
| 41 | + | writeLn({$ifDef Darwin} | |
| 42 | + | // on Mac OS X return value depends on invocation method | |
| 43 | + | 'This program was invoked via: ', | |
| 44 | + | {$else} | |
| 45 | + | // Turbo Pascal-compliant paramStr(0) returns location | |
| 46 | + | 'This program is/was stored at: ', | |
| 47 | + | {$endIf} | |
| 48 | + | paramStr(0)); | |
| 49 | + | ||
| 50 | + | for i := 1 to paramCount() do | |
| 51 | + | begin | |
| 52 | + | writeLn(i:2, '. argument: ', paramStr(i)); | |
| 53 | + | end; | |
| 54 | + | end. | |
| 55 | + | ``` | |
| 56 | + | ||
| 57 | + | Running this program (on a non-Mac OS Ⅹ platform) may look like this: | |
| 58 | + | ||
| 59 | + | ```text | |
| 60 | + | $ ./listArguments foo bar able | |
| 61 | + | This program is/was stored at: /tmp/listArguments | |
| 62 | + | 1. argument: foo | |
| 63 | + | 2. argument: bar | |
| 64 | + | 3. argument: able | |
| 65 | + | ``` | |
| 66 | + | ||
| 67 | + | The [[RTL]]’s system unit provides a `paramStr` version that returns [[Shortstring|shortStrings]]. Due to their implementation `shortString`s are capped at 255 characters. However the user may supply longer command line arguments. To access them the [`objPas` unit](https://www.freepascal.org/docs-html/rtl/objpas/index.html) redefines `paramStr` that returns [[Ansistring|ansiStrings]] instead which do not have this length limitation. The `objPas` unit is automatically included in the [[Mode ObjFPC|{$mode objFPC}]] (line 2) and [[Mode Delphi|{$mode Delphi}]] compiler modes. | |
| 68 | + | ||
| 69 | + | The distributed `paramStr` function tries to be [[Turbo Pascal]]-compliant. In TP `paramStr(0)` returns the location of the program. However, the operating system needs to support that. Most notably, on Mac OS Ⅹ the value of `paramStr(0)` depends on the invocation method, how the application is being started. | |
| 70 | + | ||
| 71 | + | > **Note:** Since `paramStr(0)` relies on OS functionality, it is not suitable for [[Multiplatform Programming Guide|cross-platform programs]]. | |
| 72 | + | ||
| 73 | + | > **Note:** On Unixoid platforms it is impossible to accurately define the location of a file merely by a string. There could be multiple hard-links pointing to the same inode, the file could have already been deleted, parts of the file path might have changed, beside other details. For these reasons, `paramStr(0)` is useless on Unixoid platforms. | |
| 74 | + | ||
| 75 | + | The [`getOpts` unit](https://www.freepascal.org/docs-html/rtl/getopts/index.html) provides a few other routines for program invocation following a certain style. | |
| 76 | + | ||
| 77 | + | ### User friendly | |
| 78 | + | ||
| 79 | + | A good program should give a help message when invoked with the wrong parameters and it should follow a common way of giving parameters. The [unit `custApp`](https://www.freepascal.org/docs-html/fcl/custapp/index.html) that comes with FPC provides the [`TCustomApplication` class](https://www.freepascal.org/docs-html/fcl/custapp/tcustomapplication.html), which provides functions to easily check and read parameters. Of course you can still access the parameters directly via `paramStr` and `paramCount`. | |
| 80 | + | ||
| 81 | + | Every [[LCL]] application uses this automatically. The Application object is a TCustomApplication. | |
| 82 | + | ||
| 83 | + | If you want to write a non LCL program, then create in [[Lazarus]] a new project of type “Console Application”. This will create a project1.lpr with some nice goodies, that almost all programs need. Go to the `doRun` method. | |
| 84 | + | ||
| 85 | + | #### Check for a parameter | |
| 86 | + | ||
| 87 | + | With TCustomApplication you can access parameters by name. For example your program should print a help text when the user gave the common help parameter *-h*. The *-h* is a short option. The long form is the *--help*. To test whether the user called the program with *-h* or *--help* you can use: | |
| 88 | + | ||
| 89 | + | ```delphi | |
| 90 | + | if hasOption('h', 'help') then | |
| 91 | + | begin | |
| 92 | + | writeHelp; | |
| 93 | + | halt; | |
| 94 | + | end; | |
| 95 | + | ``` | |
| 96 | + | ||
| 97 | + | > **Note:** In an LCL form you must prepend `application.` in front of `hasOption`. | |
| 98 | + | ||
| 99 | + | For example: | |
| 100 | + | ||
| 101 | + | ```delphi | |
| 102 | + | if application.hasOption('h', 'help') then | |
| 103 | + | begin | |
| 104 | + | writeHelp; | |
| 105 | + | halt; | |
| 106 | + | end; | |
| 107 | + | ``` | |
| 108 | + | ||
| 109 | + | If you only want to support the short option, use: | |
| 110 | + | ||
| 111 | + | ```delphi | |
| 112 | + | if hasOption('h', '') then | |
| 113 | + | ``` | |
| 114 | + | ||
| 115 | + | If you only want to support the long option, use: | |
| 116 | + | ||
| 117 | + | ```delphi | |
| 118 | + | if hasOption('help') then | |
| 119 | + | ``` | |
| 120 | + | ||
| 121 | + | #### Read the parameter value | |
| 122 | + | ||
| 123 | + | Each parameter can be given a value. For example: | |
| 124 | + | ||
| 125 | + | ``` bash | |
| 126 | + | $ project1 -f filename | |
| 127 | + | ``` | |
| 128 | + | ||
| 129 | + | or in the long form: | |
| 130 | + | ||
| 131 | + | ``` bash | |
| 132 | + | $ project1 --file=filename | |
| 133 | + | ``` | |
| 134 | + | ||
| 135 | + | In order to retrieve `filename` use: | |
| 136 | + | ||
| 137 | + | ```delphi | |
| 138 | + | writeLn('f=', getOptionValue('f', 'file')); | |
| 139 | + | ``` | |
| 140 | + | ||
| 141 | + | Note: if you get the error message “`Option at position 1 needs an argument : f.`” then you forgot to add the option in the [`checkOptions`](https://www.freepascal.org/docs-html/fcl/custapp/tcustomapplication.checkoptions.html) call. | |
| 142 | + | ||
| 143 | + | #### Checking parameters for validity | |
| 144 | + | ||
| 145 | + | Command line parameters are free text, so the user can easily type errors. Checking the syntax of the parameters is therefore mandatory. You can use the [[doc:fcl/custapp/tcustomapplication.checkoptions.html|CheckOptions]] method for this: | |
| 146 | + | ||
| 147 | + | You can define, what parameters are allowed, which ones ones need a parameter and in case of a syntax error you can get an error message plus the options that were wrong to print helpful and detailed errors. | |
| 148 | + | ||
| 149 | + | Examples: | |
| 150 | + | ||
| 151 | + | ```delphi | |
| 152 | + | errorMsg := checkOptions('hf:', 'help file:'); | |
| 153 | + | ``` | |
| 154 | + | ||
| 155 | + | This allows passing short options `‑f value` and `‑h`. It allows passing long options `‑‑help` or `‑‑file=filename`. It does not allow `‑‑help` with a value, nor `‑‑file` without a value. | |
| 156 | + | ||
| 157 | + | A Parameter Example: | |
| 158 | + | ||
| 159 | + | ``` pascal | |
| 160 | + | procedure TMainForm.FormShow(Sender: TObject); | |
| 161 | + | var | |
| 162 | + | I: Integer; | |
| 163 | + | Params : TStringList | |
| 164 | + | LongOpts : array [1..2] of string = ('debug-sync', 'config-dir:'); | |
| 165 | + | begin | |
| 166 | + | Params := TStringList.Create; | |
| 167 | + | try | |
| 168 | + | Application.GetNonOptions('hgo:', LongOpts, Params); | |
| 169 | + | for I := 0 to Params.Count -1 do | |
| 170 | + | debugln('Extra Param ' + inttostr(I) + ' is ' + Params[I]); } | |
| 171 | + | finally | |
| 172 | + | FreeAndNil(Params); | |
| 173 | + | end; | |
| 174 | + | end; | |
| 175 | + | ``` | |
| 176 | + | ||
| 177 | + | This example finds parameters and does not mix them with command line switches or options. In this example, the app also accepts the switches --debug-sync and --config-dir=somedir but they are not reported here. | |
| 178 | + | ||
| 179 | + | Note that Application.GetNonOptions() takes the long options as an array but Application.CheckOptions takes the same data but as a string. Bit sad! | |
| 180 | + | ||
| 181 | + | ## Environment variables | |
| 182 | + | ||
| 183 | + | The [[sysutils|sysUtils unit]] defines three basic functions in order to retrieve environment variables. | |
| 184 | + | ||
| 185 | + | - [`getEnvironmentVariableCount`](https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariablecount.html) returns the total number of environment variables | |
| 186 | + | - [`getEnvironmentString`](https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentstring.html) returns an environment variable by its index | |
| 187 | + | - [`getEnvironmentVariable`](https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariable.html) returns an environment variable by its key | |
| 188 | + | ||
| 189 | + | Example: | |
| 190 | + | ||
| 191 | + | ``` pascal | |
| 192 | + | program environmentVariablesList(input, output, stdErr); | |
| 193 | + | uses | |
| 194 | + | sysUtils; | |
| 195 | + | var | |
| 196 | + | i: integer; | |
| 197 | + | begin | |
| 198 | + | for i := 0 to getEnvironmentVariableCount() - 1 do | |
| 199 | + | begin | |
| 200 | + | writeLn(getEnvironmentString(i)); | |
| 201 | + | end; | |
| 202 | + | end. | |
| 203 | + | ``` | |
| 204 | + | ||
| 205 | + | It is also possible to load all environment variables to a [`tStringList` object](https://www.freepascal.org/docs-html/rtl/classes/tstringlist.html) and access to name-value pair easily. String lists automatically take care of the separator character which is by defaut is an [[=|equal sign ('=')]]. | |
| 206 | + | ||
| 207 | + | ```delphi | |
| 208 | + | program environmentVariablesSorted(input, output, stdErr); | |
| 209 | + | {$mode objFPC} | |
| 210 | + | uses | |
| 211 | + | classes, sysUtils; | |
| 212 | + | var | |
| 213 | + | i: integer; | |
| 214 | + | environmentVariables: tStringList; | |
| 215 | + | begin | |
| 216 | + | environmentVariables := tStringList.create(); | |
| 217 | + | ||
| 218 | + | try | |
| 219 | + | // load all variables to string list | |
| 220 | + | for i := 0 to getEnvironmentVariableCount() - 1 do | |
| 221 | + | begin | |
| 222 | + | environmentVariables.append(getEnvironmentString(i)); | |
| 223 | + | end; | |
| 224 | + | ||
| 225 | + | environmentVariables.sort; | |
| 226 | + | for i := 0 to environmentVariables.count - 1 do | |
| 227 | + | begin | |
| 228 | + | writeLn(environmentVariables.names[i]:20, | |
| 229 | + | ' = ', | |
| 230 | + | environmentVariables.valueFromIndex[i]); | |
| 231 | + | end; | |
| 232 | + | finally | |
| 233 | + | environmentVariables.free; | |
| 234 | + | end; | |
| 235 | + | end. | |
| 236 | + | ``` | |
| 237 | + | ||
| 238 | + | There is also a `tCustomApplication` [[Method|method]] available to get all environment variables as a [[TStringList|tStringList]] at once. Then the pattern may look like this: | |
| 239 | + | ||
| 240 | + | ```delphi | |
| 241 | + | var | |
| 242 | + | environmentVariables: tStringList; | |
| 243 | + | begin | |
| 244 | + | environmentVariables := tStringList.create(); | |
| 245 | + | try | |
| 246 | + | application.getEnvironmentList(environmentVariables); | |
| 247 | + | … | |
| 248 | + | finally | |
| 249 | + | environmentVariables.free; | |
| 250 | + | end; | |
| 251 | + | end; | |
| 252 | + | ``` | |
| 253 | + | ||
| 254 | + | --- | |
| 255 | + | *From the Free Pascal wiki, page "Command line parameters and environment variables", as it stood on 2022-05-16 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. 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).* | |
| 256 | + | Category: Code |
