3
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Fortranの標準手続きを用いてコマンドライン引数を取得する

Fortranのプログラム中で,コマンドライン引数を取得する方法をまとめます.
ネット上にいくつか記事が見られますが,まだ非標準の手続きを使っている場合もあります.本記事では,Fortran 2003から追加された標準手続きを利用します.Fortran2018から少し変わっている部分もあるので,それについても言及します.

以前に記事の付録としてまとめていますが,改めて一つの記事として切り出しています.

概要

  • Fortranでコマンドライン引数を取得するには,サブルーチンget_command_argumentを利用する.
  • 定石として,get_command_argument(number,length)でn番目の引数の長さを取得して文字列を割付けた後,get_command_argument(number,value)で引数の文字列を取得する.
  • サブルーチン実行の成否は整数(0なら成功,それ以外なら失敗)として取得できるが,Fortran2018からはエラーメッセージも取得できるようになっている.

環境

  • gfortran 8.1.0 (Windows, Linux)
  • intel fortran 2021.1 (Windows, Linux)
  • nvfortran 20.11-0 (Linux)

Fortran標準の手続によるコマンドライン引数の取得

Fortran 2003から,コマンドライン引数に関する手続き(サブルーチンまたは関数)が追加されました.
コマンドライン引数全体を取得するサブルーチンget_command(),引数の個数を取得するcommand_argument_count()関数,各引数を取得するサブルーチンget_command_argument()です.

関数 機能
get_command([command = コマンドライン引数, length = コマンドライン引数の長さ, status=取得状態]) コマンドライン引数全体を取得する
command引数が指定された場合,実行コマンドも含む全てのコマンドライン引数を文字列として取得する
length引数が指定された場合,コマンドライン引数の文字列の長さを取得する
statusは,コマンドライン引数を正常に取得できれば0,文字列の変数がコマンドライン引数よりも短ければ-1,それ以外で正常に取得できなかった場合は正の値
command_argument_count() 実行コマンドを含まない引数の個数を標準種別の整数型で返す
get_command_argumet(number=引数番号, [value = 引数の値(文字列), length = 引数の長さ, status=取得状態]) 指定された番号(0$\le$number$\le$command_argument_count())の引数を取得する(0は実行コマンド)
value引数が指定された場合,指定された番号の引数を文字列として取得する
length引数が指定された場合,指定された番号の引数の文字列長さを取得する
statusは,引数を正常に取得できれば0,文字列の変数が引数よりも短ければ-1,それ以外で正常に取得できなかった場合は正の値

get_commandget_command_argumentの引数command, length, valueは省略可能ですが,現実的な使い方としてすべてを省略することはないでしょう.

個別の引数は,下記の手順で取得すると無駄がありません.使う変数名などは異なりますが,他の記事でも同様の手順で引数が取得されており,定石といってよいでしょう.

  1. command_argumet_count()での個数を取得
  2. doループの中でget_command_argument()によって各引数の長さを取得
  3. 取得した長さの文字列を動的に割り付けてget_command_argument()で引数を取得
program main
    use, intrinsic :: iso_fortran_env
    implicit none

    type :: arguments
        character(:), allocatable :: v
    end type

    integer(int32) :: argc
    type(arguments), allocatable :: arg(:)

    argc = command_argument_count() ! 実行コマンドを含まない引数の個数
    allocate (arg(0:argc))          ! 実行コマンド+引数を格納するための文字列配列

    get_arguments: block
        integer(int32) :: n, length_nth_arg
        do n = 0, argc
            ! 第n引数の長さを取得
            call get_command_argument(number=n, length=length_nth_arg)

            ! 第n引数の長さと同じ長さの文字列を割付
            allocate (character(length_nth_arg) :: arg(n)%v)

            ! 第n引数の値を文字列で取得
            call get_command_argument(number=n, value=arg(n)%v)

            print *, n, arg(n)%v
        end do
    end block get_arguments
end program main

上記のプログラムでは,C言語の流儀に従って,コマンドライン引数の個数をargc,引数の文字列をargvと表現するようにしています.引数の文字列の格納には,自動割付文字列を成分に持つ構造体arguments型を作成し,そのarguments型の配列を利用しています.

上記のプログラムをコンパイルして実行すると,実行コマンドを含む引数の番号とその文字列が出力されます.

 $ ./a.out --help --version
           0 ./a.out
           1 --help
           2 --version

エラー処理

サブルーチンで引数を正しく取得できているか否かを確認するには,status引数として整数型変数を渡します.サブルーチン実行後にその値を確認し,その値に応じて処理を切り分けます.

program main
    use, intrinsic :: iso_fortran_env
    implicit none

! 中略

    integer(int32) :: stat

! 中略

            call get_command_argument(number=n, value=arg(n)%v, status=stat)
            if (stat /= 0) then
                write (error_unit, '(*(g0))') "error in get_command_argument: &
                                              &unable to get correctly the string of argument : ", n
                stop
            end if
! 中略
end program main

statusの値を確認することで,実行の成否が確認できます.したがって,statusはエラーコードと見ることも可能ですが,エラーコードと生じているエラーの対応関係は,規格では規定されていないようです.
Intel Fortranの実行時エラーについては,一覧が存在しています.

Fortran 2018での拡張

上述したように,サブルーチンの引数statusでエラーコードを取得できますが,エラーコードと生じているエラーの対応を調べるのも一苦労です.

Fortran 2018からは,get_commandget_command_argumentに新たな引数errmsgが追加されました.この引数を利用すると,引数として渡した文字列にエラーメッセージが書き込まれます.statusと併せて利用することで,status0でない場合にエラーの内容を表示できます.

program main
    use, intrinsic :: iso_fortran_env
    implicit none

    type :: arguments
        character(:), allocatable :: v
    end type

    integer(int32) :: argc, stat
    type(arguments), allocatable :: arg(:)

    integer(int32), parameter :: len_buffer = 255
    character(len_buffer) :: error_message

    argc = command_argument_count() ! 実行コマンドを含まない引数の個数
    allocate (arg(0:argc))          ! 実行コマンド+引数を格納するための文字列配列

    get_arguments: block
        integer(int32) :: n, length_nth_arg
        do n = 0, argc
            ! 第n引数の長さを取得
            call get_command_argument(number=n, length=length_nth_arg, status=stat, errmsg=error_message)
            if (stat /= 0) then
                write (error_unit, '(*(g0))') trim(error_message)
                stop
            end if

            ! 第n引数の長さと同じ長さの文字列を割付
            allocate (character(length_nth_arg) :: arg(n)%v)

            ! 第n引数の値を文字列で取得
            call get_command_argument(number=n, value=arg(n)%v, status=stat, errmsg=error_message)
            if (stat /= 0) then
                write (error_unit, '(*(g0))') trim(error_message)
                stop
            end if

            print *, n, arg(n)%v
        end do
    end block get_arguments
end program main

例えば,存在しない引数の番号を指定してしまった場合には

Empty arg in retrieved command

というエラーメッセージが表示されます.

残念ながら,Fortran 2018相当のget_commandget_command_argumentが利用できるのは,テストした限りではIntel Fortranのみです.

Fortranでは,文字列にallocatable属性を付けると,自動割付文字列という扱いになり,文字列にリテラルなどを代入する際に,その長さが自動で変化します.しかし,この自動割付は代入時にしか有効ではないので,引数errmsgに未割付の文字列を渡すと,実行時エラーとなります.そのため,上記のプログラムでは,errmsgに相当する文字列error_messageは長さを固定し,出力の際にtrim関数で文字列後部の空白を消しています.

Fortranの次の規格(おそらくFortran 2023)では,自動割付は代入時のみという制約が外れるので,今後はもっと使いやすくなると予想されます.

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
3
Help us understand the problem. What are the problem?