Управление подстановочными символами командной строки оболочки в C или C ++ - PullRequest
1 голос
/ 11 апреля 2010

Я пишу программу, foo, на C ++. Обычно он вызывается из командной строки следующим образом:

foo *.txt

Мой main() получает аргументы обычным способом. Во многих системах argv[1] буквально *.txt, и мне приходится вызывать системные подпрограммы, чтобы выполнить подстановочный знак. Однако в системах Unix оболочка расширяет подстановочный знак перед вызовом моей программы, и все совпадающие имена файлов будут в argv.

Предположим, я хотел добавить параметр в foo, который заставляет его переходить в подкаталоги.

foo -a *.txt

обработает все текстовые файлы в текущем каталоге и все его подкаталоги.

Я не понимаю, как это сделать, поскольку к тому времени, когда моя программа получит шанс увидеть -a, оболочка уже выполнит расширение, и пользовательский ввод *.txt будет потерян. Тем не менее, существуют распространенные Unix-программы, которые работают таким образом. Как они это делают?

Как на земле Unix, как я могу контролировать расширение подстановочных знаков?

(рекурсивный просмотр в подкаталогах - это только один пример. В идеале я пытаюсь понять общее решение для управления расширением подстановочного знака.)

Ответы [ 4 ]

6 голосов
/ 11 апреля 2010

Ваша программа не имеет никакого влияния на расширение командной строки оболочки. Какая программа будет вызвана, определяется после того, как будет выполнено все расширение, поэтому уже слишком поздно что-либо менять в программном обеспечении.

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

program -a '*.txt'

Если ваша программа вызывается таким образом, она получит два параметра -a и *.txt.

В Unix, вы должны просто оставить это пользователю, чтобы вручную запретить расширение подстановочного знака, если это не нужно.

3 голосов
/ 11 апреля 2010

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

Обратите внимание, что параметры -R и -r обычно используются для обозначения рекурсии - см. cp, ls и т. Д. Для примеров.

Если вы правильно организовали вещи так, что подстановочные знаки передаются в вашу программу как подстановочные знаки, и вы хотите выполнить рекурсию, то POSIX предоставляет подпрограммы для помощи:

  • nftw - обход дерева файлов (рекурсивный доступ).
  • fnmatch, glob, wordexp - для сопоставления и расширения имени файла

Существует также ftw, что очень похоже на nftw, но помечено как устаревшее, поэтому новый код не должен его использовать.


Адриан спросил:

Но я могу сказать ls -R * .txt без одинарных кавычек и получить рекурсивный список. Как это работает?

Чтобы адаптировать вопрос к удобному месту на моем компьютере, давайте рассмотрим:

$ ls -F | grep '^m'
makefile
mapmain.pl
minimac.group
minimac.passwd
minimac_13.terminal
mkmax.sql.bz2
mte/
$ ls -R1 m*
makefile
mapmain.pl
minimac.group
minimac.passwd
minimac_13.terminal
mkmax.sql.bz2

mte:
multithread.ec
multithread.ec.original
multithread2.ec
$

Итак, у меня есть подкаталог 'mte', который содержит три файла. И у меня есть шесть файлов с именами, которые начинаются с «m».

  • Когда я набираю 'ls -R1 m *', оболочка отмечает метасимвол '*' и использует его эквивалент glob() или wordexp(), чтобы развернуть его в список имен:

    1. Makefile
    2. mapmain.pl
    3. minimac.group
    4. minimac.passwd
    5. minimac_13.terminal
    6. mkmax.sql.bz2
    7. МТ
  • Затем оболочка организует запуск /bin/ls с 9 аргументами (имя программы, опция -R1, плюс 7 имен файлов и завершающий нулевой указатель).

  • Команда ls отмечает параметры (рекурсивный и одностолбцовый вывод) и приступает к работе.
    • Первые 6 имен (как это бывает) являются простыми файлами, поэтому ничего рекурсивного делать не надо.
    • Фамилия является каталогом, поэтому ls печатает его имя и его содержимое, вызывая его эквивалент nftw() для выполнения работы.
    • На данный момент, это сделано.
  • Этот необработанный пример не показывает, что происходит, когда имеется несколько каталогов, и поэтому приведенное выше описание упрощает обработку.
  • В частности, ls сначала обрабатывает имена не-каталогов, а затем обрабатывает имена каталогов в алфавитном порядке (по умолчанию) и выполняет сканирование в глубину каждого каталога.
1 голос
/ 11 апреля 2010

Я хотел бы указать на другой способ отключить расширение по шаблону. Вы можете указать своей оболочке прекратить расширение групповых символов с помощью опции noglob.

С использованием bash set -o noglob:

> touch a b c
> echo *
a b c
> set -o noglob
> echo *
*

А с csh используйте set noglob:

> echo *
a b c
> set noglob
> echo *
*
1 голос
/ 11 апреля 2010
foo -a '*.txt'

Часть работы оболочки (в Unix) заключается в расширении подстановочных аргументов командной строки. Вы предотвращаете это с помощью кавычек.

Кроме того, в системах Unix команда "find" делает то, что вам нужно:

find . -name '*.txt'

будет рекурсивно выводить список всех файлов из текущего каталога.

Таким образом, вы могли бы сделать

foo `find . -name '*.txt'`
...