解析命令行选项

Introduction

ParseOptions类负责解析通过 argc 和 argv 传递给main()的命令行选项。首先我们给出一个从命令行调用 Kaldi 程序的例子:

gmm-align --transition-scale=10.0 --beam=75 \
       exp/mono/tree exp/mono/30.mdl data/L.fst \
       'ark:add-deltas --print-args=false scp:data/train.scp ark:- |' \
        ark:data/train.tra ark:exp/tri/0.ali

命令行选项只有长形式(没有单字符选项),必须出现在位置参数前面。在这里例子里有六个位置参数,从“exp/mono/tree”开始;注意以“ark:add-delta”开头的是一个带空格的单个字符串;单引号里的内容会被 shell 解释;这个参数作为管道被调用。

Example of parsing command-line options

我们解释下这些选项是如何被 C++ 处理的,下面是 gmm-align.cc中的部分代码(为了表达更清晰,略微改动了下):

int main(int argc, char *argv[])
{
  try { // try-catch block is standard and relates to handling of errors.
    using namespace kaldi;
    const char *usage =
        "Align features given [GMM-based] models.\n" 
        "Usage: align-gmm [options] tree-in model-in lexicon-fst-in feature-rspecifier "
        "transcriptions-rspecifier alignments-wspecifier\n";
    // Initialize the ParseOptions object with the usage string.
    ParseOptions po(usage);
    // Declare options and set default values.
    bool binary = false;
    BaseFloat beam = 200.0;
    // Below is a structure containing options; its initializer sets defaults.
    TrainingGraphCompilerOptions gopts;
    // Register the options with the ParseOptions object.
    po.Register("binary", &binary, "Write output in binary mode");
    po.Register("beam", &beam, "Decoding beam");
    gopts.Register(&po);
    // The command-line options get parsed here.
    po.Read(argc, argv);
    // Check that there are a valid number of positional arguments.
    if(po.NumArgs() != 6) {
      po.PrintUsage();
      exit(1);
    }
    // The positional arguments get read here (they can only be obtained
    // from ParseOptions as strings).
    std::string tree_in_filename = po.GetArg(1);
    ...
    std::string alignment_wspecifier = po.GetArg(6);
    ...   
  } catch(const std::exception& e) {
    std::cerr << e.what();
    return -1;
  }
}

读上面的代码基本就明白它的功能。在通常的 Kaldi 程序中,处理流程是这样的:

  • 用包含使用说明的字符串初始化 ParseOptions 对象
  • 声明并给出可选参数(选项结构(options structure))的初始值
  • ParseOptions 对象注册命令行选项(选项结构有它们自己的注册函数,执行同样的功能)
  • 执行 “po.Read(argc, argv);”[如果存在无效选项,此处会退出程序]
  • 检查 po.NumArgs() 在有效范围内
  • 利用 po.GetArg(1) 等获取位置参数;对可能超出有效数目范围的可选位置参数来说,可用 po.GetOptArg(n)来获取第n个参数,如果n超出范围则返回空字符串

一般来说,在 Kaldi 程序中写一个新命令行时,最简单的方法就是拷贝一个现成的然后修改它。

Implicit command-line arguments

特定的命令行选项会被 ParseOptions 对象自动注册。这些包括:

  • -config 这个选项会从配置文件中导入命令行选项。e.g.如果我们设定 -config=configs/my.confmy.conf文件可能包含:
        --first-option=15  # This is the first option
        --second-option=false # This is the second option
    
  • -print-args 这个布尔型选项控制程序是否将命令行参数打印到标准错误流(默认是真);-print-args=false会将它关掉
  • -help 这个布尔型参数,如果是真,程序会打印出使用说明(像其他布尔型参数一样,只用-help就认为是真)。一般你不输入任何命令行参数时就可以获得使用说明,因为大部分程序都需要有位置参数。
  • -verbose 它控制冗余度,进而决定用 KALDI_VLOG 输出的日志信息是否被打印。值越大打印的信息越多(e.g.典型值是-verbose=2