淺談linux的命令行解析參數之getopt_long函數

前言
         在linux中,經常需要各種命令,通常情況下都會帶各種參數,而這些參數是如何解析的呢?通常使用GNU C提供的函數getopt、getopt_long、getopt_long_only函數來解析命令行參數。

一、關於命令行參數
     命令行參數可以分爲兩類,一類是短選項,一類是長選項,短選項在參數前加一槓"-",長選項在參數前連續加兩槓"--",如下表(ls 命令參數)所示,其中-a,-A,-b都表示短選項,--all,--almost-all, --author都表示長選項。他們兩者後面都可選擇性添加額外參數。比如--block-size=SIZE,SIZE便是額外的參數。

二、getopt_long函數
     getopt函數只能處理短選項,而getopt_long函數兩者都可以,可以說getopt_long已經包含了getopt_long的功能。因此,這裏就只介紹getopt_long函數。而getopt_long與getopt_long_only的區別很小,等介紹完getopt_long,在提起會更好。

#include <unistd.h>  
extern char *optarg;  
extern int optind, opterr, optopt;  
#include <getopt.h>
int getopt(int argc, char * const argv[],const char *optstring);  
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);  
int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
參數以及返回值介紹(以上三個函數都適用):

1、argc和argv和main函數的兩個參數一致。

2、optstring: 表示短選項字符串。

    形式如“a:b::cd:“,分別表示程序支持的命令行短選項有-a、-b、-c、-d,冒號含義如下:
    (1)只有一個字符,不帶冒號——只表示選項, 如-c 
    (2)一個字符,後接一個冒號——表示選項後面帶一個參數,如-a 100
    (3)一個字符,後接兩個冒號——表示選項後面帶一個可選參數,即參數可有可無,如果帶參數,則選項與參數直接不能有空格
        形式應該如-b200
3、longopts:表示長選項結構體。結構如下:

struct option 
{  
     const char *name;  
     int         has_arg;  
     int        *flag;  
     int         val;  
};  
eg:
 static struct option longOpts[] = {
      { "daemon", no_argument, NULL, 'D' },
      { "dir", required_argument, NULL, 'd' },
      { "out", required_argument, NULL, 'o' },
      { "log", required_argument, NULL, 'l' },
      { "split", required_argument, NULL, 's' },
      { "http-proxy", required_argument, &lopt, 1 },
      { "http-user", required_argument, &lopt, 2 },
      { "http-passwd", required_argument, &lopt, 3 },
      { "http-proxy-user", required_argument, &lopt, 4 },
      { "http-proxy-passwd", required_argument, &lopt, 5 },
      { "http-auth-scheme", required_argument, &lopt, 6 },
      { "version", no_argument, NULL, 'v' },
      { "help", no_argument, NULL, 'h' },
      { 0, 0, 0, 0 }
    };
  (1)name:表示選項的名稱,比如daemon,dir,out等。

  (2)has_arg:表示選項後面是否攜帶參數。該參數有三個不同值,如下:

           a: no_argument(或者是0)時 ——參數後面不跟參數值,eg: --version,--help
           b: required_argument(或者是1)時 ——參數輸入格式爲:--參數 值 或者 --參數=值。eg:--dir=/home
           c: optional_argument(或者是2)時  ——參數輸入格式只能爲:--參數=值
  (3)flag:這個參數有兩個意思,空或者非空。

           a:如果參數爲空NULL,那麼當選中某個長選項的時候,getopt_long將返回val值。
                   eg,可執行程序 --help,getopt_long的返回值爲h.             
           b:如果參數不爲空,那麼當選中某個長選項的時候,getopt_long將返回0,並且將flag指針參數指向val值。

                   eg: 可執行程序 --http-proxy=127.0.0.1:80 那麼getopt_long返回值爲0,並且lopt值爲1。

  (4)val:表示指定函數找到該選項時的返回值,或者當flag非空時指定flag指向的數據的值val。

4、longindex:longindex非空,它指向的變量將記錄當前找到參數符合longopts裏的第幾個元素的描述,即是longopts的下標值。

5、全局變量:

        (1)optarg:表示當前選項對應的參數值。

        (2)optind:表示的是下一個將被處理到的參數在argv中的下標值。

        (3)opterr:如果opterr = 0,在getopt、getopt_long、getopt_long_only遇到錯誤將不會輸出錯誤信息到標準輸出流。opterr在非0時,向屏幕輸出錯誤。

        (4)optopt:表示沒有被未標識的選項。

6、返回值:

         (1)如果短選項找到,那麼將返回短選項對應的字符。

         (2)如果長選項找到,如果flag爲NULL,返回val。如果flag不爲空,返回0

         (3)如果遇到一個選項沒有在短字符、長字符裏面。或者在長字符裏面存在二義性的,返回“?”

         (4)如果解析完所有字符沒有找到(一般是輸入命令參數格式錯誤,eg: 連斜槓都沒有加的選項),返回“-1”

         (5)如果選項需要參數,忘了添加參數。返回值取決於optstring,如果其第一個字符是“:”,則返回“:”,否則返回“?”。
注意:

        (1)longopts的最後一個元素必須是全0填充,否則會報段錯誤

        (2)短選項中每個選項都是唯一的。而長選項如果簡寫,也需要保持唯一性。

三、測試(自行測試)
1、官網給出測試用例。

#include <stdio.h>     /* for printf */
#include <stdlib.h>    /* for exit */
#include <getopt.h>
 
int
main(int argc, char **argv)
{
    int c;
    int digit_optind = 0;
 
   while (1) {
        int this_option_optind = optind ? optind : 1;
        int option_index = 0;
        static struct option long_options[] = {
            {"add",     required_argument, 0,  0 },
            {"append",  no_argument,       0,  0 },
            {"delete",  required_argument, 0,  0 },
            {"verbose", no_argument,       0,  0 },
            {"create",  required_argument, 0, 'c'},
            {"file",    required_argument, 0,  0 },
            {0,         0,                 0,  0 }
        };
 
       c = getopt_long(argc, argv, "abc:d:012",
                 long_options, &option_index);
        if (c == -1)
            break;
 
       switch (c) {
        case 0:
            printf("option %s", long_options[option_index].name);
            if (optarg)
                printf(" with arg %s", optarg);
            printf("\n");
            break;
 
       case '0':
        case '1':
        case '2':
            if (digit_optind != 0 && digit_optind != this_option_optind)
              printf("digits occur in two different argv-elements.\n");
            digit_optind = this_option_optind;
            printf("option %c\n", c);
            break;
 
       case 'a':
            printf("option a\n");
            break;
 
       case 'b':
            printf("option b\n");
            break;
 
       case 'c':
            printf("option c with value '%s'\n", optarg);
            break;
 
       case 'd':
            printf("option d with value '%s'\n", optarg);
            break;
 
       case '?':
            break;
 
       default:
            printf("?? getopt returned character code 0%o ??\n", c);
        }
    }
 
   if (optind < argc) {
        printf("non-option ARGV-elements: ");
        while (optind < argc)
            printf("%s ", argv[optind++]);
        printf("\n");
    }
 
   exit(EXIT_SUCCESS);
}
2、自己項目相關一個例子。

#include<stdio.h>
#include <getopt.h>
#include<iostream>
#include<string>
#include<stdlib.h>
using namespace std;
 
void showUsage() {
  //cout << "Usage: " << PACKAGE_NAME << " [options] URL ..." << endl;
  cout << "Options:" << endl;
  cout << " -d, --dir=DIR              The directory to store downloaded file." << endl;
  cout << " -o, --out=FILE             The file name for downloaded file." << endl;
  cout << " -l, --log=LOG              The file path to store log. If '-' is specified," << endl;
  cout << "                            log is written to stdout." << endl;
  cout << " -D, --daemon               Run as daemon." << endl;
  cout << " -s, --split=N              Download a file using s connections. s must be" << endl;
  cout << "                            between 1 and 5. If this option is specified the" << endl;
  cout << "                            first URL is used, and the other URLs are ignored." << endl;
  cout << " --http-proxy=HOST:PORT     Use HTTP proxy server. This affects to all" << endl;
  cout << "                            URLs." << endl;
  cout << " --http-user=USER           Set HTTP user. This affects to all URLs." << endl;
  cout << " --http-passwd=PASSWD       Set HTTP password. This affects to all URLs." << endl;
  cout << " --http-proxy-user=USER     Set HTTP proxy user. This affects to all URLs" << endl;
  cout << " --http-proxy-passwd=PASSWD Set HTTP proxy password. This affects to all URLs." << endl;
  cout << " --http-auth-scheme=SCHEME  Set HTTP authentication scheme. Currently, BASIC" << endl;
  cout << "                            is the only supported scheme." << endl;
  cout << " -v, --version              Print the version number and exit." << endl;
  cout << " -h, --help                 Print this message and exit." << endl;
  cout << "URL:" << endl;
  cout << " You can specify multiple URLs. All URLs must point to the same file" << endl;
  cout << " or a download fails." << endl;
  cout << "Examples:" << endl;
  cout << " Download a file by 1 connection:" << endl;
  cout << "  aria2c http://AAA.BBB.CCC/file.zip" << endl;
  cout << " Download a file by 2 connections:" << endl;
  cout << "  aria2c -s 2 http://AAA.BBB.CCC/file.zip" << endl;
  cout << " Download a file by 2 connections, each connects to a different server." << endl;
  cout << "  aria2c http://AAA.BBB.CCC/file.zip http://DDD.EEE.FFF/GGG/file.zip" << endl;
  cout << "Reports bugs to <tujikawa at rednoah dot com>" << endl;
}
 
int main(int argc, char* argv[]) {
  bool stdoutLog = false;
  string logfile;
  string dir;
  string ufilename;
  int split = 0;
  bool daemonMode = false;
  int c;
 
 
  while(1) {
    int optIndex = 0;
    int lopt;
    static struct option longOpts[] = {
      { "daemon", no_argument, NULL, 'D' },
      { "dir", required_argument, NULL, 'd' },
      { "out", required_argument, NULL, 'o' },
      { "log", required_argument, NULL, 'l' },
      { "split", required_argument, NULL, 's' },
      { "http-proxy", required_argument, &lopt, 1 },
      { "http-user", required_argument, &lopt, 2 },
      { "http-passwd", required_argument, &lopt, 3 },
      { "http-proxy-user", required_argument, &lopt, 4 },
      { "http-proxy-passwd", required_argument, &lopt, 5 },
      { "http-auth-scheme", required_argument, &lopt, 6 },
      { "version", no_argument, NULL, 'v' },
      { "help", no_argument, NULL, 'h' },
      { 0, 0, 0, 0 }
    };
    c = getopt_long(argc, argv, "Dd:o:l:s:vh", longOpts, &optIndex);
    printf("返回值: %c\n",c);
    if(c == -1) {
      break;
    }
    switch(c) {
    case 0:{
      switch(lopt) {
      case 1: {
    printf("1: %s\n",optarg);
    break;
      }
      case 2:
    printf("2: %s\n",optarg);
    break;
      case 3:
    printf("3: %s\n",optarg);
    break;
      case 4:
    printf("4: %s\n",optarg);
    break;
      case 5: 
    printf("5: %s\n",optarg);
    break;
      case 6:
    printf("6: %s\n",optarg);
    break;
      }
      break;
    }
    case 'D':
      printf("D: %s\n",optarg);
      break;
    case 'd':
      printf("d: %s\n",optarg);
      break;
    case 'o':
      printf("o: %s\n",optarg);
      break;
    case 'l':
     printf("l: %s\n",optarg);
      break;
    case 's':
      printf("s: %s\n",optarg);
      break;
    case 'v':
      printf("s: %s\n",optarg);
      //showVersion();
      exit(0);
    case 'h':
      showUsage();
      exit(0);
    default:
      showUsage();
      exit(1);
    }
  }
  return 0;
}
四、參考文章
[1].https://blog.csdn.net/cashey1991/article/details/7942809

[2].https://www.jianshu.com/p/7a0a8d882787
--------------------- 
作者:YehChiTian 
來源:CSDN 
原文:https://blog.csdn.net/qq_33850438/article/details/80172275 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章