Как правильно формализовать использование команд GNU/Linux в командной строке?

Я хотел бы написать формальную грамматику, подобную BNF, для описания использования командной строки некоторых инструментов GNU/Linux. Например, я могу описать использование команды cat так:

(cat-command) : 'cat' (arguments-list)
(arguments-list) : (argument)
(arguments-list) : (arguments-list) (argument)
(argument) : (file)

Проблема в том, что я не могу записать точную грамматику для некоторых команд, таких как md5sum. Моя первая попытка сделать это будет следующей:

(md5sum-command) : 'md5sum' (arguments-list)
(arguments-list) : (argument)
(arguments-list) : (arguments-list) (argument)
(argument) : (file)
(argument) : '--check'

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

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


person Francesco Turco    schedule 08.04.2010    source источник
comment
Вы уверены, что ваша грамматика НЕверна? Для меня многие команды Unix вполне охотно принимают несколько вхождений одного и того же аргумента, например, 'ls -l -l -l'.   -  person High Performance Mark    schedule 08.04.2010
comment
Да, ты прав. Но, может быть, есть какие-то инструменты, которые не допускают повторений. Я также могу написать свою собственную программу, которая не допускает повторений. Так что думаю моя проблема все еще актуальна.   -  person Francesco Turco    schedule 08.04.2010


Ответы (2)


Вы можете попробовать что-то вроде:

(md5sum-command) : 'md5sum' (arguments-list)
(arguments-list) : (file-arguments) | '--check' (file-arguments)
(file-arguments) : (file) (file-arguments)

Предполагая, что вы хотите указать ровно один --check для каждой команды, но не зависеть от того, является ли он первым аргументом, вы можете использовать:

(md5sum-command) : 'md5sum' (arguments-list)
(arguments-list) : (file-arguments) | (file-arguments) '--check' (file-arguments)
(file-arguments) : (file) (file-arguments)

Также обратите внимание, что символ трубы (|) — это просто ярлык для дополнительного правила. Следующее эквивалентно:

(md5sum-command) : 'md5sum' (arguments-list)
(arguments-list) : (file-arguments) 
(arguments-list) : (file-arguments) '--check' (file-arguments)
(file-arguments) : (file) (file-arguments)

Я был бы удивлен, если бы вы не могли указать большинство команд unix с контекстно-свободной грамматикой, подобной тем, которые выражены в BNF.

person Daren Thomas    schedule 08.04.2010
comment
Ваш вариант вроде работает, но он недостаточно общий. Например, вам не нужно помещать аргумент --check сразу после аргумента md5sum: вы можете указать много файлов до и после него. - person Francesco Turco; 08.04.2010
comment
Прочитав Вашу новую версию, успел записать следующее: (список-аргументов) : [(список-файлов)] [(опция-режима)] [(список-файлов)] ['--check'] [( список-файлов)] (список-аргументов) : [(список-файлов)] ['--check'] [(список-файлов)] [(опция-режима)] [(список-файлов)] (опция-режима ) : '--binary' (режим-опция) : '--text' Я не мог сокращать дальше. Таким образом, добавление других аргументов, таких как '--quiet', '--warn' или '--status', привело бы к чрезвычайно длинной грамматике для такой простой команды. - person Francesco Turco; 09.04.2010
comment
Истинный. Вы, вероятно, захотите провести различие между аргументами, которые являются просто флагами (могут появляться в любом месте в списке аргументов, действовать глобально) и аргументами, которые действуют на файлы, следующие за ними. - person Daren Thomas; 09.04.2010

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

argument(0) == "md5sum"
forall i, if i != 0 then argument(i) == "--binary" or
                         argument(i) == "--text" or
                         argument(i) == "--check" or
                         argument(i) == "--status" or
                         argument(i) belongs to <file>
0 <= instances("--binary") + instances("--text") <= 1
0 <= instances("--check") <= 1
if instances("--check") == 1 then 0 <= instances("--status") <= 1

Я не буду отмечать этот ответ как правильный, потому что мне все еще любопытно узнать, существует ли способ генерировать правильные команды.

person Francesco Turco    schedule 09.04.2010