Clicky

Fortran Wiki
Environment Variables

This is a review of the standard Fortran facilities for interacting with environment variables and work-arounds resolving several of the current limitations.

Fortran provides one standard intrinsic for obtaining the value of an environment variable – get_environment_variable.

  • It returns a default character variable.
    • this often needs converted to a numeric or logical type
    • there is no intrinsic to convert a default character variable containing a stream of UTF-8 encoded characters to selected_char_kind (‘ISO_10646’)
  • Currently the returned value is a fixed-length or allocated string of pre-determined length. - to remain generic two calls are required. One to obtain the length so a return value of sufficient length can be ensured, and then a second call to obtain the value
  • In the majority of cases a program proceeds if an environment variable is not set, requiring a default value to be used if the variable is not set.

There is no standard intrinsic for setting an environment variable or returning the full environment table.

Solutions

The module M_get_env defines a function get_env(3) that returns a string of sufficient length to hold the variable. It allows a default value to be specified which will determine the type and kind of the output variable as well.

get_env(3) can return a character type of kind selected_char_kind (‘ISO_10646’) if built with the pre-processor option -DUNICODE (optionally selected via a pre-processor macro because support of ISO_10646 encoding of Unicode characters is optional).

The xxenv example demonstrates setting an environment variable and accessing the entire environment table on POSIX-compliant systems.

Remaining Issues

Look for vendor-supplied supplements

Vendor-specific procedures are available for some platforms that can define environment variables or read or replace the environment table. Intel provides the IFPORT module and the QQSETENV() and QQGETENV() procedures for example.

Parsing multiple values from a string

Reading multiple values from a variable is easily done by reading the string as a NAMELIST group, or reading the value using list-directed input adding a backslash as an input terminator; but otherwise requires parsing the string. One example:

arbitrary_list.f90

program arbitrary_list
! use list-directed I/O to read multiple numeric values from a string
use, intrinsic :: iso_fortran_env, only : output_unit
implicit none
character(len=:),allocatable :: LINE
real,allocatable             :: floats(:)
  ! Reading an arbitrary list of numbers ignoring null values
   line=' 10.11, 20.22,, 30.33 40.44'
   allocate(floats((len(line)+1)/2)) ! maximum size required
   floats(:)=-huge(0.0) ! assume this is an invalid value
   ! tell list-directed I/O an end of input has occurred
   line=line//'/'
   read(line,*) floats
   ! skipping values (even skipped value ,,) not specified
   floats=pack(floats,mask=floats/=-huge(0.0))
   write(*,'(g0)')floats
end program arbitrary_list

Expected Output

10.1099997
20.2199993
30.3299999
40.4399986

Proprietary OS interfaces

There is probably an ISO_C_BINDING-based interface to the proprietary MSWindows equivalents to the POSIX environment table interface. In lieu of that, the environment table can be written to a file using execute_command_line(3) and an appropriate system command. Even on MSWindows platforms there are often POSIX-compliant environments available via CygWin, MSYS2 (often combined with MinGW), and Windows Subsystem for Linux (WSL),

Using platform-specific external commands

If spawning external processes, commands to set variables such as the ULS command env(1) can be used as a substitute for PUTENV(3) using execute_command_line but this will only set the variable in the subprocess, not the current process

    ! set environment variables in subprocess before executing command
    call EXECUTE_COMMAND_LINE('env A=10 B=20 command')