现代Fortran的命令行参数解析器,使用了面向对象特性。
program test
use iso_fortran_env, only: stdout => output_unit
use argparse
implicit none
character(len=10), parameter :: program_name = "qcalc"
type(argparser) :: args
args = argparser("A quantum physics calculation program.")
call args%set_program_name(program_name)
call args%add_help_option()
call args%add_sc_option("-v", "--version", "show version info", show_version_info)
call args%add_option_logical("-o", "--openmp", "use openmp or not") ! logical option has no default
call args%add_option_logical("-m", "--mpi", "use mpi or not")
call args%add_option_integer("-t", "--thread", "thread number,\nit is valid only if openmp is set", 1)
call args%add_option_integer("-p", "--process", "process number,\nit is valid only if mpi is set", 1)
call args%add_option_string("", "--chemical", "chemical formula", "H2O") ! short name can be empty
call args%add_named_argument_string("input", "initialize file")
call args%add_named_argument_string("output", "output file")
call args%parse()
if (args%has_option("--openmp")) then
print '(A,I2,A)', "openmp is used, and we use ", args%get_option_integer("-t"), " threads"
end if
if (args%has_option("--mpi")) then
print '(A,I2,A)', "mpi is used, and we use ", args%get_option_integer("-p"), " processes"
end if
print '(A,A)', "the calculated chemical is ", trim(args%get_option_string("--chemical"))
print '(A,A)', "the input file is ", trim(args%get_argument_string("input"))
print '(A,A)', "the output file is ", trim(args%get_argument_string("output"))
! you can also print the cached `args` into INI file
! print '(/,A)', "All of the options and arguments are shown below"
! call args%print_as_ini(stdout, .true.)
contains
subroutine show_version_info
print "(A)", trim(program_name)//" version 0.1.0"
end subroutine show_version_info
end program test
将其编译成qclac
,使用方式如下
> qclac -?
usage: qcalc [options] [=input] [=output]
A quantum physics calculation program.
options:
-?, --help show this help message
-v, --version show version info
-o, --openmp use openmp or not
-m, --mpi use mpi or not
-t, --thread (integer [=1]) thread number,
it is valid only if openmp is set
-p, --process (integer [=1]) process number,
it is valid only if mpi is set
--chemical (string [=H2O]) chemical formula
named arguments:
input (string) initialize file
output (string) output file
> qclac -om -t 16 input=input.txt output=out.bin
openmp is used, and we use 16 threads
mpi is used, and we use 1 processes
the calculated chemical is H2O
the input file is input.txt
the output file is out.bin
最简单的方法是复制src/argparse-f.f90
文件到你的项目中。推荐使用fpm
[dependencies]
argparse-f = { git="https://github.com/0382/argparse-f.git" }
此外,argparse-f
也支持Meson构建系统。
在我的库里面,命令行参数分为两大类(可选的选项,和必选的参数),每类又分为两种,总共四种命令行参数。
选项,从语义上来说是可选的。它分成两种:一般选项和短路选项。
用如下方式添加一般选项:
call args%add_option_integer("-t", "--thread", "thread number", 1)
其中第一个参数是短选项名,是可以为空字符串的,如果非空,则必须是一个'-'后接单个字符;第二个是长选项名,不能为空字符串,必须以"--"开头。第三个是帮助信息,最后一个是该选项的默认值。
当前版本一般选项仅支持五种数据类型:logical, integer, real, real(kind=8), character(len=*)
(添加real(8)
的方法是add_option_double
,添加character(len=*)
的方法是add_option_string
)。
除了bool型的option,其余的option添加时都要给定默认的值。 对于bool型,不需要默认值,检查到命令行参数有这个选项,就是true否则为false。例如
ls -l
此时-l选项为true。而其他类型选项需要在其后面加上参数,比如
greet --name Alice --age 24
于是--name
选项的值为"Alice"
,--age
选项的值为24
。
短路选项(short circuit option)按照如下方式添加
call args%add_sc_option("-v", "--version", "show version info", show_version_info)
contains
subroutine show_version_info
print "(A)", trim(program_name)//" version 0.1.0"
end subroutine show_version_info
短路选项仅支持bool类型,添加该选项时需要给定一个回调函数,必须是无参数的subroutine
。短路选项是最先搜索解析一种命令行参数。比如
git --help
gcc -v
只要命令行参数包含了这类参数,则调用回调函数,并立即(正常)退出程序。
参数,和选项相反是必须提供的。如果某个参数没有提供,则程序会报错并退出。参数分为位置参数和命名参数两种
当前版本参数仅支持四种数据类型:integer, real, real(kind=8), character(len=*)
。不支持logical
类型,实际上logical
类型用选项是更合适的。
按照位置获取的参数,例如
ffplay video.mp4
video.mp4
就是一个位置参数。如果使用ffplay
程序没有指定这个位置参数,那么程序就发生错误并退出。
按照如下方式添加位置参数
call args%add_argument_string("input", "initialize file")
该函数的第一个哑元表示参数的名字,第二个表示帮助信息。
这是为了解决我个人工作中遇到的情况而定义的。它的使用方式例如
greet name=Alice age=24
显然这样使用参数非常繁琐,不应该作为轻量级的命令行程序使用。但是可以放在一个较重的工程中,并且运行的时候是用脚本调用程序而不是直接在命令行使用。这个时候,使用命名参数可以让你的脚本更具可读性。
使用如下方式添加命名参数 Add named argument like this:
call args%add_named_argument_string("input", "initialize file")
解析命令行参数时,先解析命名参数,剩下的自动按照顺序赋值给位置参数。命名参数不必按照设置的顺序指定。
使用类似args%get_option_logical(name)
的方法获取结果选项的结果,这里的name
既可以长名字也可以短名字。对于logical
类型的选项,特别提供了args%has_option(name)
函数。
使用类似args%get_argument_logical(name)
的方法获取结果参数的结果。命名参数和位置参数的名字不允许冲突,所以获取的时候可以不用区分,就没有get_named_argument_xxx
版本的函数了。
短路选项和一般选项的名字不允许冲突。命名参数和位置参数的名字也不允许冲突。
这是为了实现linux一些基本命令行工具类似的效果。比如
ls -lah
同时指定了-l
, -a
和-h
选项。只有logical
类型的选项才能够这样指定。
这也是为什么选项的短名字仅允许一个字符。
使用args%add_help_option()
可以添加默认的帮助选项,它使用的名字是-?
和--help
。如果你不喜欢这个名字,可以添加自定义的帮助选项
call args%add_sc_option("-h", "--help", "show this help message", print_help)
contains
subroutine print_help
call args%print_help()
end subroutine
默认使用
-?
而不是-h
是为了给其他的选项留出选择空间。
有的时候我们的帮助信息很长,如果写在一行但是控制台的宽度不够造成换行会很难看。你可以在帮助信息里面加上\n
作为换行标识。当然Fortran是没有转义字符的,不过这不重要。这个包会根据\n
自动添加换行和空格使得帮助信息更好看。
你可以将argparser
获取的结果打印为INI文件格式
call args%print_as_ini(stdout, .true.)
这个函数的第二个参数表示是否打印注释,如果为.true.
那么会把帮助信息打印为注释。
如果你运行程序不带任何命令行参数同时程序应该需要argument,那么argparser
会调用print_usage
并退出。print_usage
实际上就是print_help
的第一行信息,算是一个简短的帮助信息。set_program_name
仅仅影响print_usage
时显示的程序名字,如果不调用这个函数,那么会使用argv[0]
。
这个包和我自己写的c++包argparse工作方式是类似的。这两个包都受到了c++包cmdline以及python标准库的argparse模块的启发。