После использования PHP getopt(), как узнать, какие аргументы остались?

Итак, в PHP есть встроенная функция getopt(), которая возвращает информацию о том, какие параметры программы пользователь предоставил. Только, если я что-то не упустил, это совершенно не работает! Из руководства:

Разбор опций завершится при обнаружении первой неопции, все последующие отбрасываются.

Таким образом, getopt() возвращает массив только с параметрами valid and parsed. Вы по-прежнему можете увидеть всю исходную командную строку, взглянув на $argv, которая остается неизменной, но как узнать, где в этой командной строке getopt() остановился анализ аргументов? Важно знать это, если вы хотите рассматривать оставшуюся часть командной строки как другие вещи (например, имена файлов).

Вот пример...

Предположим, я хочу настроить скрипт для приема следующих аргументов:

Usage: test [OPTION]... [FILE]...

Options:
  -a  something
  -b  something
  -c  something

Тогда я мог бы вызвать getopt() следующим образом:

$args = getopt( 'abc' );

И, если бы я запустил скрипт следующим образом:

$ ./test.php -a -bccc file1 file2 file3

Я должен ожидать, что мне вернут следующий массив:

Array
(
    [a] =>
    [b] =>
    [c] => Array
        (
            [0] =>
            [1] =>
            [2] =>
        )
)

Итак, вопрос в следующем: откуда мне знать, что три непроанализированных аргумента FILE без опций начинаются с $argv[ 3 ]???


person edam    schedule 03.10.2013    source источник
comment
Вы можете заглянуть в Console_GetOpt (stackoverflow.com/ вопросов/1023095/), но для этого требуется PEAR.   -  person andig    schedule 10.12.2013
comment
Вы указали файл в качестве последнего параметра, чтобы вы могли сделать $argv[count($argv) - 1]   -  person EaterOfCode    schedule 26.01.2015
comment
@EaterOfCode: этого недостаточно. Вы можете указать любое количество файлов, поэтому вам нужно знать, сколько необработанных аргументов, не являющихся параметрами, есть в конце.   -  person edam    schedule 26.02.2015
comment
Если у вас нет параметров, требующих значений, или необязательных значений, вы можете просто обрезать все элементы $argv, начинающиеся с дефиса, оставив только необязательные аргументы: preg_grep('/^-/', $argv, PREG_GREP_INVERT)   -  person Quinn Comendant    schedule 21.06.2015
comment
Ха-ха, спустя 30 минут, чтобы решить эту проблему, я нашел свой собственный старый комментарий выше. ;) Решение по-прежнему стоит. Обновлено использование: $argv = array_values(preg_grep('/^-/', $argv, PREG_GREP_INVERT));   -  person Quinn Comendant    schedule 24.08.2017
comment
В PHP 7.1 и более поздних версиях есть необязательный третий параметр &$optind, который получает индекс первого оставшегося аргумента. Я не знаю, как решить эту проблему для PHP 7.0 и более ранних версий. Срок службы всех из них истек, поэтому, если вы можете, вам следует обновить их.   -  person Enno    schedule 01.05.2019


Ответы (4)


Начиная с PHP 7.1, getopt поддерживает необязательный параметр by-ref, &$optind, который содержит индекс, в котором остановлен синтаксический анализ аргумента. Это полезно для смешивания флагов с позиционными аргументами. Например.:

user@host:~$ php -r '$i = 0; getopt("a:b:", [], $i); print_r(array_slice($argv, $i));' -- -a 1 -b 2 hello1 hello2
Array
(
    [0] => hello1
    [1] => hello2
)
person adsr    schedule 18.10.2016
comment
Я пишу в 2021 году, что я все еще ограничен PHP 5.5 и не могу использовать третий вариант getopt(). - person ob-ivan; 22.01.2021

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

$arg_a = null; // -a=YOUR_OPTION_A_VALUE
$arg_b = null; // -b=YOUR_OPTION_A_VALUE
$arg_c = null; // -c=YOUR_OPTION_A_VALUE

$arg_file = null;  // -file=YOUR_OPTION_FILE_VALUE

foreach ( $argv as $arg )
{
    unset( $matches );

    if ( preg_match( '/^-a=(.*)$/', $arg, $matches ) )
    {
        $arg_a = $matches[1];
    }
    else if ( preg_match( '/^-b=(.*)$/', $arg, $matches ) )
    {
        $arg_b = $matches[1];
    }
    else if ( preg_match( '/^-c=(.*)$/', $arg, $matches ) )
    {
        $arg_c = $matches[1];
    }
    else if ( preg_match( '/^-file=(.*)$/', $arg, $matches ) )
    {
        $arg_file = $matches[1];
    }
    else
    {
        // all the unrecognized stuff
    }
}//foreach

if ( $arg_a === null )    { /* missing a - do sth here */ }
if ( $arg_b === null )    { /* missing b - do sth here */ }
if ( $arg_c === null )    { /* missing c - do sth here */ }
if ( $arg_file === null ) { /* missing file - do sth here */ }

echo "a=[$arg_a]\n";
echo "b=[$arg_b]\n";
echo "c=[$arg_c]\n";
echo "file=[$arg_file]\n";

Я всегда так делаю, и это работает. Более того, я могу делать с ним все, что захочу.

person Artur    schedule 03.10.2013
comment
Да, но я бы предпочел иметь возможность использовать встроенную библиотеку, а не таскать с собой повсюду свою собственную. Мне это кажется серьезным недостатком встроенной библиотеки. (Кроме того, код вашего примера не будет обрабатывать, например, параметр командной строки -bccc в моем примере. Я уверен, что есть более полные примеры команды -line-парсеры для PHP существуют, но это противоречит сути этого вопроса!) - person edam; 03.10.2013
comment
Просто используйте strtok и foreach результаты будут лучше. - person Gasol; 19.12.2013
comment
Я столкнулся с несколькими ситуациями, когда встроенные функции PHP немного не соответствовали действительности, и мне нужно было создать свои собственные. Я не думаю, что за это следует проголосовать, потому что он творчески пытается решить проблему, на которую встроенная функция PHP не отвечает. Это не идеальный ответ, но и не дрянной. - person n0nag0n; 04.09.2015
comment
@ImmortalFirefly: это не решает вопрос - я уже знаю, что могу свернуть свой собственный! Мой вопрос был конкретно о том, как я могу использовать встроенный в PHP метод getopt() для той цели, для которой он предназначен. - person edam; 01.02.2016

Следующее может использоваться для получения любых аргументов после параметров командной строки. Его можно использовать до или после вызова PHP getopt() без изменения результата:

# $options = getopt('cdeh');

$argx = 0;

while (++$argx < $argc && preg_match('/^-/', $argv[$argx])); # (no loop body)

$arguments = array_slice($argv, $argx);

$arguments теперь содержит любые аргументы, следующие за любыми начальными параметрами. В качестве альтернативы, если вы не хотите, чтобы аргументы находились в отдельном массиве, тогда $argx является индексом первого фактического аргумента: $argv[$argx].

Если нет аргументов, после любых начальных опций, то:

  • $arguments — это пустой массив [], а
  • count($arguments) == 0 и
  • $argx == $argc.
person Brian Howell    schedule 15.04.2016
comment
Это ужасный ответ! Он пропускает любые аргументы командной строки, начинающиеся с -, и предполагает, что остальные аргументы не являются параметрами! Что, если я запущу командную строку: ./example --name Bob -s 23? Здесь у меня может быть нет необязательных аргументов (если --name и -s оба принимают опциональный аргумент), и ваш код скажет, что ["Bob", "-s", "23"] были необязательными аргументами. Проблема в том, что пока вы не проанализируете параметры, вы не сможете определить, какие аргументы не являются параметрами. PHP getopt() должен каким-то образом указать, какие аргументы не являются частью опции. И это не так. - person edam; 20.05.2016

Взгляните на GetOptionKit, чтобы избавиться от синтаксического анализа флага.

http://github.com/c9s/GetOptionKit

GetOptionKit можно легко интегрировать в сценарий командной строки. Он поддерживает ограничение типа, проверку значений и так далее.

person c9s    schedule 20.05.2016
comment
Спасибо. Я знаю, что для этого есть другие библиотеки. Мне просто интересно, есть ли способ сделать это с помощью встроенной в PHP функции getopt(). Хотя вроде нет. - person edam; 20.05.2016