Command line parameters and environment variables
│
English (en) │
español (es) │
suomi (fi) │
français (fr) │
русский (ru) │
中文(中国大陆) (zh_CN) │
On most (interactive) operating systems programs can be started via a command line interface (CLI) that allows supplying the program with additional data.
The system (and objPas) units provide basic functions in order to access command-line supplied data.
Overview
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.
Terminology
- Option
- Options are yes/no flags. They usually assume the form
‑‑dryrun/‑‑no‑dryrun(dryrun enabled or disabled respectively). Options are also called “switches”. - Parameter
- Parameters are key-value-tuples. On the command line they look like:
‑‑processors 4. - Argument
- 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.
- Environment variable
- Environment variables refer to a labeled piece of storage in the environment. They are name-value-pairs.
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.
Basics
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.
The function ParamStr returns the n-th argument on the command line.
ParamStr(0) tries returning the name, and possibly full path to the executable program file.
Command line parameters
The basics
A Pascal program can a access command line arguments by using the ParamStr function in conjunction with ParamCount.
ParamCount returns the number of supplied arguments.
program ListArguments(Input, Output, StdErr);
{$mode objfpc}
var
I: Integer;
begin
WriteLn({$ifDef Darwin}
// on Mac OS X return value depends on invocation method
'This program was invoked via: ',
{$else}
// Turbo Pascal-compliant ParamStr(0) returns location
'This program is/was stored at: ',
{$endIf}
ParamStr(0));
for I := 1 to ParamCount() do
begin
WriteLn(i:2, '. argument: ', ParamStr(i));
end;
end.
Running this program (on a non-Mac OS Ⅹ platform) may look like this:
$ ./listArguments foo bar able
This program is/was stored at: /tmp/listArguments
1. argument: foo
2. argument: bar
3. argument: able
The RTL’s system unit provides a ParamStr version that returns ShortStrings.
Due to their implementation ShortStrings are capped at 255 characters.
However the user may supply longer command line arguments.
To access them the ObjPas unit redefines ParamStr that returns AnsiStrings instead which do not have this length limitation.
The ObjPas unit is automatically included in the {$mode objfpc} (line 2) and {$mode Delphi} compiler modes.
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.
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.
ParamStr(0) is useless on Unixoid platforms.The GetOpts unit provides a few other routines for program invocation following a certain style.
User friendly
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 that comes with FPC provides the TCustomApplication class, which provides functions to easily check and read parameters.
Of course you can still access the parameters directly via ParamStr and ParamCount.
Every LCL application uses this automatically. The Application object is a TCustomApplication.
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.
Check for a parameter
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:
if HasOption('h', 'help') then
begin
WriteHelp;
Halt;
end;
application. in front of HasOption.For example:
if Application.HasOption('h', 'help') then
begin
WriteHelp;
Halt;
end;
If you only want to support the short option, use:
if HasOption('h', '') then
If you only want to support the long option, use:
if HasOption('help') then
Read the parameter value
Each parameter can be given a value. For example:
$ project1 -f filename
or in the long form:
$ project1 --file=filename
In order to retrieve filename use:
WriteLn('f=', GetOptionValue('f', 'file'));
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 call.
Checking parameters for validity
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 CheckOptions method for this:
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.
Examples:
ErrorMsg := CheckOptions('hf:', 'help file:');
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.
A Parameter Example:
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;
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.
Note that Application.GetNonOptions() takes the long options as an array but Application.CheckOptions takes the same data but as a string. Bit sad!
Environment variables
The SysUtils unit defines three basic functions in order to retrieve environment variables.
GetEnvironmentVariableCountreturns the total number of environment variablesGetEnvironmentStringreturns an environment variable by its indexGetEnvironmentVariablereturns an environment variable by its key
Example:
program EnvironmentVariablesList(Input, Output, StdErr);
uses
SysUtils;
var
I: Integer;
begin
for I := 0 to GetEnvironmentVariableCount() - 1 do
begin
WriteLn(GetEnvironmentString(I));
end;
end.
It is also possible to load all environment variables to a TStringList object and access to name-value pair easily.
String lists automatically take care of the separator character which is by defaut is an equal sign ('=').
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.
There is also a TCustomApplication method available to get all environment variables as a TStringList at once.
Then the pattern may look like this:
var
EnvironmentVariables: TStringList;
begin
EnvironmentVariables := TStringList.create();
try
Application.GetEnvironmentList(EnvironmentVariables);
…
finally
EnvironmentVariables.Free;
end;
end;