Command line parameters and environment variables: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 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 | 2 | ||
| 3 | 3 | ## Overview | |
| 4 | 4 | ||
| 5 | 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 | 6 | ||
| 7 | 7 | ### Terminology | |
| 8 | 8 | ||
| 9 | 9 | Option | |
| 10 | 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 | 11 | ||
| 12 | 12 | Parameter | |
| 13 | 13 | Parameters are key-value-tuples. On the command line they look like: `‑‑processors 4`. | |
| 14 | 14 | ||
| 15 | 15 | Argument | |
| 16 | - | ||
| 16 | + | Arguments are simple words. On the command line they are usually separated by spaces (confer the IFS 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 | 17 | ||
| 18 | 18 | Environment variable | |
| 19 | 19 | Environment variables refer to a labeled piece of storage in the environment. They are name-value-pairs. | |
| 20 | 20 | ||
| 21 | 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 | 22 | ||
| 23 | 23 | ### Basics | |
| 24 | 24 | ||
| 25 | 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 | 26 | ||
| 27 | 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 | 28 | ||
| 29 | 29 | ## Command line parameters | |
| 30 | 30 | ||
| 31 | 31 | ### The basics | |
| 32 | 32 | ||
| 33 | 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 | 34 | ||
| 35 | 35 | ``` pascal | |
| 36 | 36 | program listArguments(input, output, stdErr); | |
| 37 | 37 | {$mode objFPC} | |
| 38 | 38 | var | |
| 39 | 39 | i: integer; | |
| 40 | 40 | begin | |
| 41 | 41 | writeLn({$ifDef Darwin} | |
| 42 | 42 | // on Mac OS X return value depends on invocation method | |
| 43 | 43 | 'This program was invoked via: ', | |
| 44 | 44 | {$else} | |
| 45 | 45 | // Turbo Pascal-compliant paramStr(0) returns location | |
| 46 | 46 | 'This program is/was stored at: ', | |
| 47 | 47 | {$endIf} | |
| 48 | 48 | paramStr(0)); | |
| 49 | 49 | ||
| 50 | 50 | for i := 1 to paramCount() do | |
| 51 | 51 | begin | |
| 52 | 52 | writeLn(i:2, '. argument: ', paramStr(i)); | |
| 53 | 53 | end; | |
| 54 | 54 | end. | |
| 55 | 55 | ``` | |
| 56 | 56 | ||
| 57 | 57 | Running this program (on a non-Mac OS Ⅹ platform) may look like this: | |
| 58 | 58 | ||
| 59 | 59 | ```text | |
| 60 | 60 | $ ./listArguments foo bar able | |
| 61 | 61 | This program is/was stored at: /tmp/listArguments | |
| 62 | 62 | 1. argument: foo | |
| 63 | 63 | 2. argument: bar | |
| 64 | 64 | 3. argument: able | |
| 65 | 65 | ``` | |
| 66 | 66 | ||
| 67 | 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 | 68 | ||
| 69 | 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 | 70 | ||
| 71 | 71 | > **Note:** Since `paramStr(0)` relies on OS functionality, it is not suitable for [[Multiplatform Programming Guide|cross-platform programs]]. | |
| 72 | 72 | ||
| 73 | 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 | 74 | ||
| 75 | 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 | 76 | ||
| 77 | 77 | ### User friendly | |
| 78 | 78 | ||
| 79 | 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 | 80 | ||
| 81 | 81 | Every [[LCL]] application uses this automatically. The Application object is a TCustomApplication. | |
| 82 | 82 | ||
| 83 | 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 | 84 | ||
| 85 | 85 | #### Check for a parameter | |
| 86 | 86 | ||
| 87 | 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 | 88 | ||
| 89 | 89 | ```delphi | |
| 90 | 90 | if hasOption('h', 'help') then | |
| 91 | 91 | begin | |
| 92 | 92 | writeHelp; | |
| 93 | 93 | halt; | |
| 94 | 94 | end; | |
| 95 | 95 | ``` | |
| 96 | 96 | ||
| 97 | 97 | > **Note:** In an LCL form you must prepend `application.` in front of `hasOption`. | |
| 98 | 98 | ||
| 99 | 99 | For example: | |
| 100 | 100 | ||
| 101 | 101 | ```delphi | |
| 102 | 102 | if application.hasOption('h', 'help') then | |
| 103 | 103 | begin | |
| 104 | 104 | writeHelp; | |
| 105 | 105 | halt; | |
| 106 | 106 | end; | |
| 107 | 107 | ``` | |
| 108 | 108 | ||
| 109 | 109 | If you only want to support the short option, use: | |
| 110 | 110 | ||
| 111 | 111 | ```delphi | |
| 112 | 112 | if hasOption('h', '') then | |
| 113 | 113 | ``` | |
| 114 | 114 | ||
| 115 | 115 | If you only want to support the long option, use: | |
| 116 | 116 | ||
| 117 | 117 | ```delphi | |
| 118 | 118 | if hasOption('help') then | |
| 119 | 119 | ``` | |
| 120 | 120 | ||
| 121 | 121 | #### Read the parameter value | |
| 122 | 122 | ||
| 123 | 123 | Each parameter can be given a value. For example: | |
| 124 | 124 | ||
| 125 | 125 | ``` bash | |
| 126 | 126 | $ project1 -f filename | |
| 127 | 127 | ``` | |
| 128 | 128 | ||
| 129 | 129 | or in the long form: | |
| 130 | 130 | ||
| 131 | 131 | ``` bash | |
| 132 | 132 | $ project1 --file=filename | |
| 133 | 133 | ``` | |
| 134 | 134 | ||
| 135 | 135 | In order to retrieve `filename` use: | |
| 136 | 136 | ||
| 137 | 137 | ```delphi | |
| 138 | 138 | writeLn('f=', getOptionValue('f', 'file')); | |
| 139 | 139 | ``` | |
| 140 | 140 | ||
| 141 | 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 | 142 | ||
| 143 | 143 | #### Checking parameters for validity | |
| 144 | 144 | ||
| 145 | 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 | 146 | ||
| 147 | 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 | 148 | ||
| 149 | 149 | Examples: | |
| 150 | 150 | ||
| 151 | 151 | ```delphi | |
| 152 | 152 | errorMsg := checkOptions('hf:', 'help file:'); | |
| 153 | 153 | ``` | |
| 154 | 154 | ||
| 155 | 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 | 156 | ||
| 157 | 157 | A Parameter Example: | |
| 158 | 158 | ||
| 159 | 159 | ``` pascal | |
| 160 | 160 | procedure TMainForm.FormShow(Sender: TObject); | |
| 161 | 161 | var | |
| 162 | 162 | I: Integer; | |
| 163 | 163 | Params : TStringList | |
| 164 | 164 | LongOpts : array [1..2] of string = ('debug-sync', 'config-dir:'); | |
| 165 | 165 | begin | |
| 166 | 166 | Params := TStringList.Create; | |
| 167 | 167 | try | |
| 168 | 168 | Application.GetNonOptions('hgo:', LongOpts, Params); | |
| 169 | 169 | for I := 0 to Params.Count -1 do | |
| 170 | 170 | debugln('Extra Param ' + inttostr(I) + ' is ' + Params[I]); } | |
| 171 | 171 | finally | |
| 172 | 172 | FreeAndNil(Params); | |
| 173 | 173 | end; | |
| 174 | 174 | end; | |
| 175 | 175 | ``` | |
| 176 | 176 | ||
| 177 | 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 | 178 | ||
| 179 | 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 | 180 | ||
| 181 | 181 | ## Environment variables | |
| 182 | 182 | ||
| 183 | 183 | The [[sysutils|sysUtils unit]] defines three basic functions in order to retrieve environment variables. | |
| 184 | 184 | ||
| 185 | 185 | - [`getEnvironmentVariableCount`](https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariablecount.html) returns the total number of environment variables | |
| 186 | 186 | - [`getEnvironmentString`](https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentstring.html) returns an environment variable by its index | |
| 187 | 187 | - [`getEnvironmentVariable`](https://www.freepascal.org/docs-html/rtl/sysutils/getenvironmentvariable.html) returns an environment variable by its key | |
| 188 | 188 | ||
| 189 | 189 | Example: | |
| 190 | 190 | ||
| 191 | 191 | ``` pascal | |
| 192 | 192 | program environmentVariablesList(input, output, stdErr); | |
| 193 | 193 | uses | |
| 194 | 194 | sysUtils; | |
| 195 | 195 | var | |
| 196 | 196 | i: integer; | |
| 197 | 197 | begin | |
| 198 | 198 | for i := 0 to getEnvironmentVariableCount() - 1 do | |
| 199 | 199 | begin | |
| 200 | 200 | writeLn(getEnvironmentString(i)); | |
| 201 | 201 | end; | |
| 202 | 202 | end. | |
| 203 | 203 | ``` | |
| 204 | 204 | ||
| 205 | 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 | 206 | ||
| 207 | 207 | ```delphi | |
| 208 | 208 | program environmentVariablesSorted(input, output, stdErr); | |
| 209 | 209 | {$mode objFPC} | |
| 210 | 210 | uses | |
| 211 | 211 | classes, sysUtils; | |
| 212 | 212 | var | |
| 213 | 213 | i: integer; | |
| 214 | 214 | environmentVariables: tStringList; | |
| 215 | 215 | begin | |
| 216 | 216 | environmentVariables := tStringList.create(); | |
| 217 | 217 | ||
| 218 | 218 | try | |
| 219 | 219 | // load all variables to string list | |
| 220 | 220 | for i := 0 to getEnvironmentVariableCount() - 1 do | |
| 221 | 221 | begin | |
| 222 | 222 | environmentVariables.append(getEnvironmentString(i)); | |
| 223 | 223 | end; | |
| 224 | 224 | ||
| 225 | 225 | environmentVariables.sort; | |
| 226 | 226 | for i := 0 to environmentVariables.count - 1 do | |
| 227 | 227 | begin | |
| 228 | 228 | writeLn(environmentVariables.names[i]:20, | |
| 229 | 229 | ' = ', | |
| 230 | 230 | environmentVariables.valueFromIndex[i]); | |
| 231 | 231 | end; | |
| 232 | 232 | finally | |
| 233 | 233 | environmentVariables.free; | |
| 234 | 234 | end; | |
| 235 | 235 | end. | |
| 236 | 236 | ``` | |
| 237 | 237 | ||
| 238 | 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 | 239 | ||
| 240 | 240 | ```delphi | |
| 241 | 241 | var | |
| 242 | 242 | environmentVariables: tStringList; | |
| 243 | 243 | begin | |
| 244 | 244 | environmentVariables := tStringList.create(); | |
| 245 | 245 | try | |
| 246 | 246 | application.getEnvironmentList(environmentVariables); | |
| 247 | 247 | … | |
| 248 | 248 | finally | |
| 249 | 249 | environmentVariables.free; | |
| 250 | 250 | end; | |
| 251 | 251 | end; | |
| 252 | 252 | ``` | |
| 253 | 253 | ||
| 254 | 254 | --- | |
| 255 | 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 | 256 | Category: Code |
