1. 程式人生 > 其它 >linux的命令列解析引數之getopt_long函式

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;
}

[參考] : https://blog.csdn.net/qq_33850438/article/details/80172275