Skip to content
Sylvain303 edited this page Sep 21, 2021 · 8 revisions

Welcome to the docopts wiki!

The wiki pages are used for discussion. Feel free to contribute.

docopts documentation lives in README

Docopt Grammar

EBNF like prototype:

(*
  here is grammar definition for docopt language, in EBNF.
  Only the "Usage: prog" is mandatory
*)
Docopt =   [ Prologue ]
         ,   Usage
         , [ Free_text ]
         , [ Options ]
         , [ Free_text ]
         ;

(*
  Prologue can be used to introduce the program.
  Matching USAGE leaves the Prologue.
*)
Prologue = Line_of_text, NEWLINE | { Prologue } ;
NEWLINE = ? all line ending char combination ? ;
Line_of_text = ALL_CHARACTERS - USAGE ;
ALL_CHARACTERS = ? all visible characters ? ;
USAGE   = ? "Usage:" case insensitive ? ;

Usage   = USAGE , First_Program_Usage | { Program_Usage } ;
First_Program_Usage = PROG_NAME , [ Expr ] ;
(*
 PROG_NAME is catched at first definition and stay the same literal for all the parsing
 Program_Usage can be break multi-line: Indent + PROG_NAME will start a new Program_Usage

 Usage: ./my_program.sh [-h] [--lovely-option] FILENAME
        ./my_program.sh another LINE OF --usage
        my_program      will continue [--above] <usage-definition>

 PROG_NAME  on first usage parsing it becomes: "./my_program.sh"
*)
PROG_NAME = ? any non space characters ? ;
Program_Usage = Indent , PROG_NAME  [ Expr ] ;
Indent = ? Long_space at the beginning of the line ? ;
Long_space = "  " , { ' ' } | '\t' , { '\t' } ;
Expr  = Seq , { '|' ,  Seq } ;
Seq  = Atom , [ "..." ] ;
Atom = '(' , Expr , ')'
     | '[' , Expr , ']'
     | "options"
     | Long_def
     | Shorts_option
     | Argument
     | Command
     | '-' | "--" (* single-dash and double-dash *)
     ;
Shorts_option = Short | Short , Argument ;
Long_def = Long | Long , Argument | Long , '=' , Argument ;
Long = '--' , Chars , Chars { '_' | '-' | Chars } ;
Short = '-' , Chars ;
Chars = Lowercase | Capital | Digit ;
Lowercase = ? lowercase latin1 letters ? ;
Capital  = ? Uppercase latin1 letters ? ;
Digit = ? numbers 0 at 9 ? ;
Argument = Angle_Argument | Capital_Argument ;
Angle_Argument = '<' , Lowercase , { Lowercase | '_'  | '-' | Digit } , '>' ;
Capital_Argument =  Capital , { Capital | '_' | Digit } ;

Options =  OPTIONS_KEYWORD , Options_Help { Options_Help } ;
OPTIONS_KEYWORD =  ? "Options:" case insensitive ? ;
Options_Help = Indent , Option_Flag , Option_description ;
Option_Flag  = ( Short_def | Long_def ) , [ ( " " | "," ) ,  Option_Flag ] ;
Short_def = Short | Short Argument ;
Option_description = Long_space , Description_text , [ Default_value ] ;
Description_text = ALL_CHARACTERS - Default_value ;
(*
   Value_for_default:
   ']' can be given for default that way: [default: "]"]
   double quote can be escaped too: [default: "{ \"I love \" : \"JSON\" }"]
   emtpy string: [default: ""]
   
   This is not done that way in python:
   https://github.com/docopt/docopt/blob/20b9c4ffec71d17cee9fd963238c8ec240905b65/docopt.py#L200
   first value found for "default:" note the first space is not part of the value.
   matched = re.findall('\[default: (.*)\]', description, flags=re.I)
   value = matched[0] if matched else None
*)
Default_value = '[' , "default:" , ' ' , Value_for_default , ']' ;
Value_for_default = string
                  | ALL_CHARACTERS - ']'
                  ;
string = '"' , { ALL_CHARACTERS - '"' | escaped_double_quote } , '"' ;
escaped_double_quote = '\' , '"' ;

(*
  Free_text allow to add help information in a section like message, which is eaten by the parser.
  Free_text could be extend and or parsed on grammar extension, new defined sections for example:

Arguments:
  FILENAME    Inputfile
  COUNTER     Number of time to repeat the action.

etc.

Howto:
  Some information, bla, bla
Indent is not mandatory, it's free.

Usage: <== this will fail? or not? as usage is already parsed. (failure on python's version 0.6.1)
*)
Free_text = ALL_CHARACTERS - USAGE - OPTIONS_KEYWORD , NEWLINE | { Free_text } ;

Shell helpers

# auto extract the Usage string from the top shell script comment
# ie: help=$(sed -n -e '/^# Usage:/,/^$/ s/^# \?//p' < $0)
help_string=$(docopt_get_help_string $0)

# if the option as multiples values, you can get it into an array
array_opt=( $(docopt_get_values args --multiple-time) )

Examples

naval_fate.sh

See python/docopt

Source: naval_fate.sh

naval_fate.sh ship Guardian move 100 150 --speed=15

returned $parsed string to be evaluated:

declare -A arguments
arguments['--drifting']=false    
arguments['mine']=false
arguments['--help']=false
arguments['move']=true
arguments['--moored']=false
arguments['new']=false
arguments['--speed']='15'
arguments['remove']=false
arguments['--version']=false
arguments['set']=false
arguments['<name>']='Guardian'
arguments['ship']=true
arguments['<x>']='100'
arguments['shoot']=false
arguments['<y>']='150'

Limitations

  • repeatable options with filename with space inside, will not be easily split on $IFS, but old fake nested array is still available or use an helper eval "$(docopt_get_eval_array args FILE myarray)"

API proposal for docopts - docopt on shell (bash) - DEPRECATED

API_proposal.md Was issue #7 for the discussion and proposing your changes.