2015-04-01 1 views
-1

предположит, у меня есть строка, как следующийАнализировать оболочку строки в кавычках в execv совместимого вектор аргумента

echo "foo" "bar\"blub""baz" "'" "\"" foo\ bar "\\" '\'' """"  Lots" "of\ whitespace 

Теперь я хочу, чтобы выполнить над строкой в ​​качестве команды, как если бы она была напечатана в оболочку через вызов Unix.execv , Если бы я не сделал ни одной ошибки, то оболочка будет разбирать выше в следующем OCAML списке:

["echo"; "foo"; "bar\"blubbaz"; "'"; "\""; "foo bar"; "\\"; "'", "", "Lots of whitespace"] 

Какая библиотека получает меня из исходной строки в анализируемом списке?

В конечном счете, я хочу передать полученный список Unix.execvpe. Существует также Unix.open_process_full, который способен обрабатывать мою исходную строку, используя /bin/sh, но я обнаружил, что мое приложение на 16% быстрее при вызове внешней программы напрямую без /bin/sh. Теперь я хочу иметь возможность принимать больше входных строк, включая цитирование и экранирование.

Нужно ли мне рулон мой собственный парсер?

Там существует функция POSIX wordexp но оборачивать эту функцию не решить мою проблему, потому что wordexp делает больше, чем то, что я хочу (подстановка команд, оценивает шарики, заменяет Тильда и переменные окружения).

Я только хочу, чтобы кавычки и экраны были разрешены.

+0

Я не знаю стандарта (как в стандарте «POSIX или ...»), который выполняет задание, которое вы хотите. Это означает, что вы, вероятно, закончите свой собственный или копаете код одного или нескольких оболочек и т. Д. –

+0

Почему именно вы хотите разделить слова, похожие на, но не совсем как на оболочку? Как вы решаете, какие синтаксические функции вы хотите поддерживать, а какие - нет? Почему 'sh', а не лучший формат цитирования? –

+0

@thatotherguy вход поступает из файла конфигурации, который хранит строку, которая должна быть выполнена в переменной. Эта строка не должна содержать подстановочные знаки, переменные среды или тильда. Если я использую 'wordexp', то я не могу сказать, чтобы он выдавал ошибку или иным образом отказывался анализировать строки, которые содержат эти вещи. Вы спрашиваете, почему не лучший формат цитирования, чем оболочка POSIX? – josch

ответ

0

Я приготовил решение этой проблемы с помощью ocamllex. Отправьте его здесь, если кто-то захочет сделать что-то подобное. Он должен быть легко расширяемым, чтобы включать функции, выходящие за пределы диапазона поддерживаемых в настоящее время escape-символов и других функций оболочки.

{ 
    exception UnknownShellEscape of string 
    exception UnmatchedChar of char 
    let buf_from_str str = 
    let buf = Buffer.create 16 in 
    Buffer.add_string buf str; 
    buf 
} 

let safechars = [^ '"' ''' '\\' ' ' '\t']+ 
let space = [ ' ' '\t' ]+ 

rule shell_command argv = parse 
| space   { shell_command argv lexbuf } 
| safechars  { uquote argv (buf_from_str (Lexing.lexeme lexbuf)) lexbuf } 
| '\\' '"'  { uquote argv (buf_from_str "\"") lexbuf } 
| '\\' '''  { uquote argv (buf_from_str "'") lexbuf } 
| '\\' '\\'  { uquote argv (buf_from_str "\\") lexbuf } 
| '\\' ' '  { uquote argv (buf_from_str " ") lexbuf } 
| '\\' _ as c { raise (UnknownShellEscape c) } 
| '"'   { dquote argv (Buffer.create 16) lexbuf } 
| '''   { squote argv (Buffer.create 16) lexbuf } 
| _ as c  { raise (UnmatchedChar c) } 
| eof { List.rev argv } 
and uquote argv buf = parse 
| (space|eof) { shell_command ((Buffer.contents buf)::argv) lexbuf } 
| '\\' '"' { Buffer.add_string buf "\""; uquote argv buf lexbuf } 
| '\\' ''' { Buffer.add_string buf "'"; uquote argv buf lexbuf } 
| '\\' '\\' { Buffer.add_string buf "\\"; uquote argv buf lexbuf } 
| '\\' ' ' { Buffer.add_string buf " "; uquote argv buf lexbuf } 
| '\\' _ as c { raise (UnknownShellEscape c) } 
| '"'   { dquote argv buf lexbuf } 
| '''   { squote argv buf lexbuf } 
| safechars { Buffer.add_string buf (Lexing.lexeme lexbuf); uquote argv buf lexbuf } 
| _ as c  { raise (UnmatchedChar c) } 
and dquote argv buf = parse 
| '"' (space|eof) { shell_command ((Buffer.contents buf)::argv) lexbuf } 
| '"' '"'   { dquote argv buf lexbuf } 
| '"' '''   { squote argv buf lexbuf } 
| '"'    { uquote argv buf lexbuf } 
| '\\' '"'  { Buffer.add_string buf "\""; dquote argv buf lexbuf } 
| '\\' '\\'  { Buffer.add_string buf "\\"; dquote argv buf lexbuf } 
| '\\' _ as c  { raise (UnknownShellEscape c) } 
| [^ '"' '\\' ]+ { Buffer.add_string buf (Lexing.lexeme lexbuf); dquote argv buf lexbuf } 
| _ as c   { raise (UnmatchedChar c) } 
and squote argv buf = parse 
| ''' (space|eof) { shell_command ((Buffer.contents buf)::argv) lexbuf } 
| ''' '''   { squote argv buf lexbuf } 
| ''' '"'   { dquote argv buf lexbuf } 
| '''    { uquote argv buf lexbuf } 
| [^ ''' ]+  { Buffer.add_string buf (Lexing.lexeme lexbuf); squote argv buf lexbuf } 
| _ as c   { raise (UnmatchedChar c) } 

{ 
    let main() = 
    let cin = 
     if Array.length Sys.argv > 1 
     then open_in Sys.argv.(1) 
     else stdin 
    in 
    let lexbuf = Lexing.from_channel cin in 
    let argv = shell_command [] lexbuf in 
    List.iter (Printf.printf "%s\n") argv 

    let _ = Printexc.print main() 
} 

Чтобы попробовать его запустить:

$ ocamllex test.mll 
$ echo 'echo "foo" "bar\\"blub""baz" "'\''" "\\"" foo\\ bar '\ 
> '"\\\\" """"'\'''\'''\'''\''""  Lots" "of\\ whitespace' \ 
> | ocaml test.ml 
echo 
foo 
bar"blubbaz 
' 
" 
foo bar 
\ 

Lots of whitespace 

успеха! \ o/