netlib.narod.ru< Назад | Оглавление | Далее >

5.10. Аргументы командной строки

В операционной среде, обеспечивающей поддержку Си, имеется возможность передать аргументы или параметры запускаемой программе с помощью командной строки. В момент запуска программы функции main передаются два аргумента. В первом, обычно называемом argc (сокращение от argument count), стоит количество аргументов, заданых в командной строке. Второй параметр, называемый argv (от argument vector), является указателем на массив символьных строк, содержащих сами аргументы. Для работы с этими строками обычно используются указатели нескольких уровней.

Простейший пример — программа echo (эхо), которая печатает аргументы своей командной строки в одной строчке, отделяя их друг от друга пробелами. Так, команда

    echo Здравствуй, мир!

напечатает

    Здравствуй, мир!

По общепринятому соглашению argv[0] есть имя вызываемой программы, так что значение argc никогда не бывает меньше 1. Если argc равно 1, то в командной строке после имени программы никаких аргументов нет. В нашем примере argc равно 3, и соответственно argv[0], argv[1] и argv[2] представляют собой строки "echo", "Здравствуй," и "мир!". Первый необязательный аргумент — это argv[1], последний — argv[argc-1]. Кроме того, стандарт требует, чтобы argv[argc] всегда был пустым указателем (NULL).


Рис. 13.

Первая версия программы echo трактует argv как массив указателей на строки.

  #include <stdio.h>

  /* Эхо аргументов командной строки, версия 1 */
  main(int argc, char *argv[])
  {
      int i;

      for (i = 1; i < argc; i++)
          printf("%s%s", argv[i], (i < argc-1) ? " " : "");
      printf("\n");
      return 0;
  }

Так как argv — это указатель на массив указателей, мы можем работать с ним как с указателем, а не как с индексируемым массивом. Следующая программа основана на приращении argv, он увеличивается таким образом, что его значение в каждый отдельный момент указывает на очередной указатель на char; перебор указателей заканчивается, когда исчерпан argc.

  #include <stdio.h>

  /* Эхо аргументов командной строки. Версия 2 */
  main(int argc, char *argv[])
  {
      while (--argc > 0)
          printf("%s%s", *++argv, (argc > 1) ? " " : "");
      printf("\n");
      return 0;
  }

Аргумент argv — указатель на начало массива строк аргументов. Использование в ++argv префиксного оператора ++ приведет к тому, что первым будет напечатан argv[1], а не argv[0]. Каждое очередное приращение указателя дает нам следующий аргумент, на котроый указывает *argv. В это же время значение argc уменьшается на 1, и, когда оно станет равно нулю, все аргументы будут напечатаны.

Инструкцию printf можно было бы написать и так:

  printf((argc > 1) ? "%s " : "%s", *++argv);

Как видите, формат в printf тоже может быть выражением.

В качестве второго примера возьмем программу поиска образца, рассмотренную в параграфе 4.1, и несколько усовершенствуем ее. Если вы помните, образец для поиска мы «вмонтировали» глубоко в программу, а это, очевидно, не лучшее решение. Построим нашу программу по аналогии с grep из UNIX, т.е. так, чтобы образец для поиска задавался первым аргументом в командной строке.

  #include <stdio.h>
  #include <string.h>
  #define MAXLINE 1000

  int getline(char *line, int max);

  /* find: печать строк с образцом,
           задаваемым первым аргументом */
  main(int argc, char *argv[])
  {
      char line[MAXLINE];
      int found = 0;

      if (argc != 2)
          printf("Используйте в find образец.\n");
      else
          while (getline(line, MAXLINE) > 0)
              if (strstr(line, argv[1]) != NULL) {
                  printf("%s", line);
                  found++;
              }
      return found;
  }

Стандартная функция strstr(s, t) возвращает указатель на первую встретившуюся строку t в строке s или NULL, если таковой в s не встретилось. Функция объявлена в заголовочном файле <string.h>.

Эту модель можно развивать и дальше, чтобы проиллюстрировать другие операции с указателями. Предположим, что мы вводим еще два необязательных аргумента. Один из них предписывает печатать все строки, кроме тех, в которых встречается образец; второй — перед каждой выводимой строкой печатать ее порядковый номер.

По общему соглашению для Си-программ в системе UNIX знак минус перед аргументом указывает на то, что это необязательный признак или параметр. Так, если -x служит признаком слова «кроме», которое изменяет задание на противоположное, а -n указывает на необходимость нумерации строк, то команда

  find -x -n образец

напечатает все строки, в которых не найден указанный образец, и, кроме того, перед каждой строкой укажет ее номер.

Необязательные аргументы разрешается располагать в любом порядке, при этом лучше, чтобы остальная часть программы не зависела от числа представленных аргументов. Кроме того, пользователю было бы удобно, если бы он мог комбинировать необязательные аргументы, например так:

    find -nx образец

А теперь напишем нашу программу.

  #include <stdio.h>
  #include <string.h>
  #define MAXLINE 1000

  int getline(char *line, int max);

  /* find: печать строк с образцами
           заданными аргументом. Вариант 2 */
  main(int argc, char *argv[])
  {
      char line[MAXLINE];
      long lineno = 0;
      int c, except = 0, number = 0, found = 0;

      while (--argc > 0 && (*++argv)[0] == '-')
          while (c = *++argv[0])
              switch (c) {
              case 'x':
                  except = 1;
                  break;
              case 'n':
                  number = 1;
                  break;
              default:
                  printf("find: неверный параметр %c\n", c);
                  argc = 0;
                  found = -1;
                  break;
              }
      if (argc != 1)
          printf("Используйте: find -x -n образец\n");
      else
          while (getline(line, MAXLINE) > 0) {
              lineno++;
              if ((strstr(line, *argv) != NULL) != except) {
                  if (number)
                      printf("%ld:", lineno);
                  printf("%s", line);
                  found++;
              }
          }
      return found;
  }

Перед получением очередного аргумента argc уменьшается на 1, а argv «перемещается» на следующий аргумент. После завершения цикла при отсутствии ошибок argc содержит количество еще не обработанных аргументов, а argv указывает на первый из них. Таким образом, в конце концов argc должен быть равен 1, а *argv указывать на образец. Заметим, что *++argv является указателем на аргумент-строку, а (*++argv)[0] — его первым символом, на который можно сослаться и другим способом:

    **++argv

Поскольку операция индексирования [] имеет более высокий приоритет, чем * и ++, круглые скобки здесь обязательны, без них выражение трактовалось бы так же, как *++(argv[0]). Именно такое выражение мы применим во внутреннем цикле, где просматриваются символы конкретного аргумента. Во внутреннем цикле выражение *++argv[0] увеличивает указатель argv[0].

Потребность в более сложных выражениях для указателей возникает не так уж часто. Но если такое случится, то разбивая процесс вычисления указателя на два или три шага, вы облегчите восприятие этого выражения.


Упражнение 5-10


Напишите программу expr, интерпретирующую обратную польскую запись выражения, задаваемого командной строкой, в который каждый оператор и операнд представлены отдельным аргументом. Например,

    expr 2 3 4 + *

вычисляется так же, как выражение 2 * (3 + 4).



Упражнение 5-11


Усовершенствуйте программы entab и detab (см. упражнения 1.20 и 1.21) таким образом, чтобы через аргументы можно было задавать список позиций табуляции.



Упражнение 5-12


Расширьте возможности entab и detab таким образом, чтобы при обращении вида

    entab -m +n

позиции табуляции начинались с m-й позиции и располагались через каждые n позиций. Разработайте удобный для пользователя вариант поведения программы по умолчанию (когда нет никаких аргументов).



Упражнение 5-13


Напишите программу tail, печатающую n последних введенных строк. По умолчанию значение n равно 10, но при желании n можно задать с помощью аргумента. Обращение вида

    tail -n

печатает n последних строк. Программа должна вести себя осмысленно при любых входных данных и любом значении n. Напишите программу так, чтобы наилучшим образом использовать память; запоминание строк организуйте, как в программе сортировки, описанной в параграфе 5.6, а не на основе двумерного массива с фиксированным размером строки.



netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz