2 # A bash command completion script for ovs-appctl.
5 # Right now, the script can do the following:
7 # - display available completion or complete on unfinished user input
8 # (long option, subcommand, and argument).
10 # - once the subcommand (e.g. ofproto/trace) has been given, the
11 # script will print the subcommand format.
13 # - the script can convert between keywords like 'bridge/port/interface/dp'
14 # and the available record in ovsdb.
18 # - only support small set of important keywords
19 # (dp, datapath, bridge, switch, port, interface, iface).
21 # - does not support parsing of nested option
22 # (e.g. ovsdb-tool create [db [schema]]).
24 # - does not support expansion on repeatitive argument
25 # (e.g. ovs-dpctl show [dp...]).
27 # - only support matching on long options, and only in the format
28 # (--option [arg], i.e. should not use --option=[arg]).
37 # Expandable keywords.
38 _KWORDS
=(bridge switch port interface iface dp_name dp
)
45 # Output to the compgen.
49 # For ovs-appctl command only.
51 # Target in the current completion, default ovs-vswitchd.
54 _POSSIBLE_TARGETS
="ovs-vswitchd ovsdb-server ovs-ofctl"
61 # Extracts all subcommands of 'command'.
62 # If fails, returns nothing.
64 local command=$_COMMAND
68 if [ -n "$_APPCTL_TARGET" ]; then
69 target
="--target $_APPCTL_TARGET"
72 subcmds
="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \
73 | cut -d ' ' -f1)" || error
="TRUE"
75 if [ -z "$error" ]; then
80 # Extracts all long options of ovs-appctl.
81 # If fails, returns nothing.
83 local command=$_COMMAND
86 options
="$($command --option 2>/dev/null | sort | sed -n '/^--.*/p' | cut -d '=' -f1)" \
89 if [ -z "$error" ]; then
94 # Returns the option format, if the option asks for an argument.
95 # If fails, returns nothing.
96 option_require_arg
() {
97 local command=$_COMMAND
99 local require_arg error
101 require_arg
="$($command --option | sort | sed -n '/^--.*/p' | grep -- "$option" | grep -- "=")" \
104 if [ -z "$error" ]; then
109 # Combination Discovery
110 # =====================
114 # Given the subcommand formats, finds all possible completions
115 # at current completion level.
116 find_possible_comps
() {
125 # If it is an optional argument, gets all completions,
127 if [ -n "$(sed -n '/^\[.*\]$/p' <<< "$arg")" ]; then
128 local opt_arg
="$(sed -e 's/^\[\(.*\)\]$/\1/' <<< "$arg")"
131 IFS
='|' read -a opt_args
<<< "$opt_arg"
132 comps
="${opt_args[@]} $comps"
133 # If it is in format "\[*", it is a start of nested
134 # option, do not parse.
135 elif [ -n "$(sed -n "/^\
[.
*$
/p
" <<< "$arg")" ]; then
137 # If it is a compulsory argument, adds it to the comps
138 # and break, since all following args are for next stage.
142 IFS
='|' read -a args
<<< "$arg"
143 comps
="${args[@]} $comps"
152 # Given the subcommand format, and the current command line input,
153 # finds keywords of all possible completions.
154 subcmd_find_keyword_based_on_input
() {
162 # finds all combinations by searching for '{}'.
163 # there should only be one '{}', otherwise, the
164 # command format should be changed to multiple commands.
165 mult
="$(sed -n 's/^.*{\(.*\)}.*$/ \1/p' <<< "$format" | tr '|' '\n' | cut -c1-)"
166 if [ -n "$mult" ]; then
170 tmp
="$(sed -e "s@
{\
(.
*\
)}@
$line@
" <<< "$format")"
173 combs
="$(tr '@' '\n' <<< "$combs")"
178 # Now, starts from the first argument, narrows down the
179 # subcommand format combinations.
180 for arg
in "${subcmd_line[@]}"; do
181 local kword possible_comps
183 # Finds next level possible comps.
184 possible_comps
=$
(find_possible_comps
"$combs")
186 kword
="$(arg_to_kwords "$arg" "$possible_comps")"
187 # Returns if could not find 'kword'
188 if [ -z "$kword" ]; then
191 # Trims the 'combs', keeps context only after 'kword'.
192 if [ -n "$combs" ]; then
193 combs
="$(sed -n "s@^.
*\
[\
{0,1\
}$kword|\
{0,1\
}[a-z_
]*\
]\
{0,1\
} @@p
" <<< "$combs")"
196 comps
="$(find_possible_comps "$combs")"
208 # Prints the input to stderr. $_PRINTF_ENABLE must be filled.
210 local stderr_out
="$@"
212 if [ -n "$_PRINTF_ENABLE" ]; then
213 printf "\n$stderr_out" 1>&2
217 # Extracts the bash prompt PS1, outputs it with the input argument
218 # via 'printf_stderr'.
220 # Original idea inspired by:
221 # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2
223 # The code below is taken from Peter Amidon. His change makes it more
225 extract_bash_prompt
() {
228 myPS1
="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")"
229 v
="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')"
230 v
="${v##*# Begin prompt}"
231 _BASH_PROMPT
="$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')"
241 # All completion functions.
245 result
=$
(ovs-vsctl list-br
2>/dev
/null |
grep -- "^$1") || error
="TRUE"
247 if [ -z "$error" ]; then
253 local ports result error
256 all_ports
=$
(ovs-vsctl
--format=table \
259 list Port
2>/dev
/null
) || error
="TRUE"
260 ports
=$
(printf "$all_ports" |
sort |
tr -d '"' |
uniq -u)
261 result
=$
(grep -- "^$1" <<< "$ports")
263 if [ -z "$error" ]; then
269 local bridge bridges result error
271 bridges
=$
(ovs-vsctl list-br
2>/dev
/null
) || error
="TRUE"
272 for bridge
in $bridges; do
275 ifaces
=$
(ovs-vsctl list-ifaces
"${bridge}" 2>/dev
/null
) || error
="TRUE"
276 result
="${result} ${ifaces}"
279 if [ -z "$error" ]; then
285 local dps result error
287 dps
=$
(ovs-appctl dpctl
/dump-dps
2>/dev
/null | cut
-d '@' -f2) || error
="TRUE"
288 result
=$
(grep -- "^$1" <<< "$dps")
290 if [ -z "$error" ]; then
295 # Converts the argument (e.g. bridge/port/interface/dp name) to
296 # the corresponding keywords.
297 # Returns empty string if could not map the arg to any keyword.
300 local possible_kwords
=($2)
301 local non_parsables
=()
305 for kword
in ${possible_kwords[@]}; do
308 match
="$(complete_bridge "$arg")"
311 match
="$(complete_port "$arg")"
314 match
="$(complete_iface "$arg")"
317 match
="$(complete_dp "$arg")"
320 if [ "$arg" = "$kword" ]; then
323 non_parsables
+=("$kword")
329 if [ -n "$match" ]; then
335 # If there is only one non-parsable kword,
336 # just assumes the user input it.
337 if [ "${#non_parsables[@]}" -eq "1" ]; then
338 echo "$non_parsables"
343 # Expands the keywords to the corresponding instance names.
345 local possible_kwords
=($@
)
347 local printf_expand_once
=
350 for kword
in ${possible_kwords[@]}; do
355 match
="$(complete_bridge "")"
358 match
="$(complete_port "")"
361 match
="$(complete_iface "")"
364 match
="$(complete_dp "")"
367 # Treats option as kword as well.
374 match
=$
(echo "$match" |
tr '\n' ' ' |
tr -s ' ' |
sed -e 's/^[ \t]*//')
376 if [ -n "$_PRINTF_ENABLE" ]; then
379 if [ -z "$printf_expand_once" ]; then
380 printf_expand_once
="once"
381 printf -v output_stderr
"\nArgument expansion:\n"
383 printf -v output_stderr
"$output_stderr available completions \
384 for keyword \"%s\": %s " "$kword" "$match"
386 printf_stderr
"$output_stderr"
401 # This function takes the current command line arguments as input,
402 # finds the command format and returns the possible completions.
403 parse_and_compgen
() {
404 local command=$_COMMAND
405 local subcmd_line
=($@
)
406 local subcmd
=${subcmd_line[0]}
412 if [ -n "$_APPCTL_TARGET" ]; then
413 target
="--target $_APPCTL_TARGET"
416 # Extracts the subcommand format.
417 subcmd_format
="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \
418 | awk -v opt=$subcmd '$1 == opt {print $0}' | tr -s ' ' )"
420 # Finds the possible completions based on input argument.
421 comp_keyword
="$(subcmd_find_keyword_based_on_input "$subcmd_format" \
422 "${subcmd_line[@]}")"
424 # Prints subcommand format and expands the keywords if 'comp_keyword'
426 if [ -n "$comp_keyword" ]; then
427 printf_stderr
"$(printf "\nCommand format
:\n%s
" "$subcmd_format")"
428 comp_wordlist
="$(kwords_to_args "$comp_keyword")"
429 # If there is no expanded completions, returns "NO_EXPAN" to
430 # distinguish from the case of no available completions.
431 if [ -z "$comp_wordlist" ]; then
434 echo "$comp_wordlist"
446 # Takes the current command line arguments and returns the possible
449 # At the beginning, the options are checked and completed. For ovs-appctl
450 # completion, The function looks for the --target option which gives the
451 # target daemon name. If it is not provided, by default, 'ovs-vswitchd'
454 # Then, tries to locate and complete the subcommand. If the subcommand
455 # is provided, the following arguments are passed to the 'parse_and_compgen'
456 # function to figure out the corresponding completion of the subcommand.
458 # Returns the completion arguments on success.
460 local cmd_line_so_far
=($@
)
461 local comp_wordlist _subcmd options i
464 # Parse the command-line args till we find the subcommand.
465 for i
in "${!cmd_line_so_far[@]}"; do
466 # if $i is not greater than $j, it means the previous iteration
467 # skips not-visited args. so, do nothing and catch up.
468 if [ $i -le $j ]; then continue; fi
470 if [[ "${cmd_line_so_far[i]}" =~ ^
--* ]]; then
471 # If --target is found, locate the target daemon.
472 # Else, it is an option command, fill the comp_wordlist with
474 if [ "$_COMMAND" = "ovs-appctl" ] \
475 && [[ "${cmd_line_so_far[i]}" =~ ^
--target$
]]; then
476 _APPCTL_TARGET
="ovs-vswitchd"
478 if [ -n "${cmd_line_so_far[j+1]}" ]; then
481 for daemon
in $_POSSIBLE_TARGETS; do
482 # Greps "$daemon" in argument, since the argument may
483 # be the path to the pid file.
484 if [ "$daemon" = "${cmd_line_so_far[j+1]}" ]; then
485 _APPCTL_TARGET
="$daemon"
492 comp_wordlist
="$_POSSIBLE_TARGETS"
496 options
="$(extract_options $_COMMAND)"
497 # See if we could find the exact option.
498 if [ "${cmd_line_so_far[i]}" = "$(grep -- "${cmd_line_so_far[i]}" <<< "$options")" ]; then
499 # If an argument is required and next argument is non-empty,
500 # skip it. Else, return directly.
501 if [ -n "$(option_require_arg "${cmd_line_so_far[i]}")" ]; then
503 if [ -z "${cmd_line_so_far[j]}" ]; then
504 printf_stderr
"\nOption requires an arugment."
509 # Else, need to keep completing on option.
511 comp_wordlist
="$options"
516 # Takes the first non-option argument as subcmd.
517 _subcmd
="${cmd_line_so_far[i]}"
521 if [ -z "$comp_wordlist" ]; then
522 # If the subcommand is not found, provides all subcmds and options.
523 if [ -z "$_subcmd" ]; then
524 comp_wordlist
="$(extract_subcmds) $(extract_options)"
525 # Else parses the current arguments and finds the possible completions.
527 # $j stores the index of the subcmd in cmd_line_so_far.
528 comp_wordlist
="$(parse_and_compgen "${cmd_line_so_far[@]:$j}")"
532 echo "$comp_wordlist"
540 # The compgen function.
541 _ovs_command_complete
() {
544 _COMMAND
=${COMP_WORDS} # element 0 is the command.
546 cur
=${COMP_WORDS[COMP_CWORD]}
548 # Do not print anything at first [TAB] execution.
549 if [ "$COMP_TYPE" -eq "9" ]; then
552 _PRINTF_ENABLE
="enabled"
555 # Extracts bash prompt PS1.
556 if [ "$1" != "debug" ]; then
560 # Invokes the helper function to get all available completions.
561 # Always not input the 'COMP_WORD' at 'COMP_CWORD', since it is
562 # the one to be completed.
563 _COMP_WORDLIST
="$(ovs_comp_helper \
564 ${COMP_WORDS[@]:1:COMP_CWORD-1})"
566 # This is a hack to prevent autocompleting when there is only one
567 # available completion and printf disabled.
568 if [ -z "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then
569 _COMP_WORDLIST
="$_COMP_WORDLIST none void no-op"
572 if [ -n "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then
573 if [ -n "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sed -e '/NO_EXPAN/d' | grep -- "^
$cur")" ]; then
574 printf_stderr
"\nAvailable completions:\n"
576 if [ "$1" != "debug" ]; then
577 # If there is no match between '$cur' and the '$_COMP_WORDLIST'
578 # prints a bash prompt since the 'complete' will not print it.
579 printf_stderr
"\n$_BASH_PROMPT${COMP_WORDS[@]}"
584 if [ "$1" = "debug" ]; then
585 printf_stderr
"$(echo $_COMP_WORDLIST | tr ' ' '\n' | sort -u | sed -e '/NO_EXPAN/d' | grep -- "$cur")\n"
587 if [ -n "$_COMP_WORDLIST" ]; then
588 COMPREPLY
=( $
(compgen
-W "$(echo $_COMP_WORDLIST | tr ' ' '\n' \
589 | sort -u | sed -e '/NO_EXPAN/d')" -- $cur) )
592 # If there is no completions, just complete on file path.
601 if [ "$1" = "debug" ]; then
605 COMP_CWORD
="$(expr $# - 1)"
607 # If the last argument is TAB, it means that the previous
608 # argument is already complete and script should complete
609 # next argument which is not input yet. This hack is for
610 # compromising the fact that bash cannot take unquoted
612 if [ "${COMP_WORDS[$COMP_CWORD]}" = "TAB" ]; then
613 COMP_WORDS
[$COMP_CWORD]=""
616 _ovs_command_complete
"debug"
617 # Normal compgen mode.
619 complete
-F _ovs_command_complete ovs-appctl
620 complete
-F _ovs_command_complete ovs-ofctl
621 complete
-F _ovs_command_complete ovs-dpctl
622 complete
-F _ovs_command_complete ovsdb-tool