FortranでOSに依存せずにファイルを削除する簡単な方法

# 概要
`close`文に`status="delete"`を指定することで，開いたファイルを閉じると同時に削除できます．

# テスト環境
- Windows
    - Windows 10
    - gfortran 10.3.0
    - intel fortran classic 2021.5.0
    - nag fortran 7.1
- Linux
    - Ubuntu 20.04
    - gfortran 9.4.0
    - intel fortran classic 2021.4.0

# ファイルの削除
プログラムの実行中にファイルを削除したいという要望は，色々な場面で生じます．例えば，プログラム実行中に作成した一時的なファイルをプログラム終了時に削除したい，といった場面です．OSのシステムコマンドを実行する`system`手続も存在しますが，OSによってコマンドやオプションが異なると，移植性のあるプログラムの作成が難しくなります．

```Fortran
! gfortran
integer(int32) :: stat

call system("del ファイル名", status=stat) ! Windows用
! call system("rm ファイル名", status=stat) ! Linux用
```

また，`system`手続はFortran標準ではなく，コンパイラによって書き方が異なります．gfortranとNAG Fortranは`system`サブルーチン，Intel Fortran classic (ifort)の場合は`system`関数です[^system_proc]．

```Fortran
! NAG Fortran
use :: f90_unix_proc
implicit none
integer(int32) :: stat

call system("del ファイル名", status=stat)
```

```Fortran
! Intel Fortran classic
use :: ifport
implicit none
integer(int32) :: result ! ifportモジュールの中でstat手続が定義されており，変数名として使うと衝突する

result = system("del ファイル名")
```

[^system_proc]: ifortであっても，実行の成否を取得しないのであれば，`system`サブルーチンが利用できます．

このような事情から，ファイルを削除するには，`system`手続ではなくFortranの標準機能で実現することを検討した方がよいでしょう．

Fortranでは，ファイルを開く際に`open`文，閉じる際に`close`文を用います．`close`文の`status`指定子に`"delete"`を指定することで，読み取り専用でないファイルを削除できます．

```Fortran
program main
    use, intrinsic :: iso_fortran_env
    implicit none

    integer(int32) :: unit_number, stat

    open (newunit=unit_number, file=ファイル名, iostat=stat)
    close (unit_number, status="delete", iostat=stat)
end program main
```

`status`指定子には，もう一つ有効な値として`"keep"`があります．これが指定されていると，ファイルを閉じる際にファイルを削除しません．`status`が省略された場合は，`status="keep"`が適用されます．

# 使用例
プログラム内で気軽に一時ファイルが削除できるようになりました．個人的には，この機能を知ったことで，ファイル出力を伴う手続のテストがやりやすくなったように感じます．

テストを行うことを考えると，ファイル出力の成否を確認する方法としては，ファイル出力を行った後にファイルが存在するかを確認することが最も簡単です．しかし，以前のテスト結果が残っていると，ファイル出力に成功した結果上書きされたファイルなのか，ファイル出力に失敗したのかがすぐには判りません．

また，バージョン管理ツールを使っていると，テストを実行した後に一時ファイルが残される場合に一時ファイルを追跡しないよう設定する必要があります．一時ファイルは追跡したくないけど，同じ拡張子を持つ他のファイルは追跡するといった設定が必要になる場合もあるでしょう．追跡しない（Untrackedのまま放置する）と，リポジトリのブランチ切替が妨げられる場合もあります．

テストを行うことで煩わしい状況になる（一時ファイルが大量に作られる，バージョン管理ツールの追加の設定が必要になる等）と，その煩わしい状況を避けるためにテストを実行しなくなってしまいます．これでは本末転倒です．プログラム内で気軽に一時ファイルが削除できるなら，活用した方がよいでしょう．

例えば，ファイル出力を伴う手続のテストを下記のような手順で行うと，一時ファイルを残さずにテストを完了できます．

```Fortran
program main
    use, intrinsic :: iso_fortran_env
    implicit none

    character(:), allocatable :: name
    integer(int32) :: stat, unit

    name = 出力されるファイルの名前を設定する

    ! 1. 古いファイルが存在しているかを確認し，存在していれば削除する．
    ! 存在しているのに開けない場合，開けたが削除できない場合はテストが失敗したとする．
    block
        logical :: old_file_exists

        inquire (file=name, exist=old_file_exists)
        if (old_file_exists) then
            ! 削除するために開く．
            open (newunit=unit, file=name, status="old", iostat=stat)

            if (stat == 0) then
                ! open時にエラーがなければcloseする．
                close (unit, status="delete", iostat=stat)
            end if
        else
            stat = 0
        end if
        if (stat /= 0) error stop "test failed"
    end block

    ! 2. ファイル出力を伴う処理を実行する．
    

    ! 3. 出力されたファイルを開く．
    ! 開けない場合はテストが失敗したとする．
    open (newunit=unit, file=name, status="old", action="read", position="rewind", iostat=stat)
    if (stat /= 0) error stop "test failed"

    ! 4. ファイルの中が想定通りの結果になっているかを調べる．
    ! 想定通りでない場合はテストが失敗したとする．
    

    ! 5. ファイルを削除する．
    ! 削除できなかった場合はテストが失敗したとする．
    close (unit, status="delete", iostat=stat)
    if (stat /= 0) error stop "test failed"
end program main
```

# まとめ
`close`文の`status`指定子を利用することで，ファイルを閉じる際にファイルを削除することができます．

`close`文は頻繁に使っていたのですが，`status`指定子を持っていることを把握していませんでした．使い慣れた処理であっても，調べてみると新しい発見がありますね．