AUTHORS: refresh with recent new contributors
[zfs.git] / contrib / bash_completion.d / zfs.in
blobdbeb10d8994bc574020a00df8abc800b9c984035
1 # Copyright (c) 2010-2016, Aneurin Price <aneurin.price@gmail.com>
3 # Permission is hereby granted, free of charge, to any person
4 # obtaining a copy of this software and associated documentation
5 # files (the "Software"), to deal in the Software without
6 # restriction, including without limitation the rights to use,
7 # copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the
9 # Software is furnished to do so, subject to the following
10 # conditions:
12 # The above copyright notice and this permission notice shall be
13 # included in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 # OTHER DEALINGS IN THE SOFTWARE.
24 __ZFS_CMD="@sbindir@/zfs"
25 __ZPOOL_CMD="@sbindir@/zpool"
27 # Disable bash's built-in hostname completion, as this makes it impossible to
28 # provide completions containing an @-sign, which is necessary for completing
29 # snapshot names. If bash_completion is in use, this will already be disabled
30 # and replaced with better completions anyway.
31 shopt -u hostcomplete
33 __zfs_get_commands()
35     $__ZFS_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | cut -f1 -d '|' | uniq
38 __zfs_get_properties()
40     $__ZFS_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all name space
43 __zfs_get_editable_properties()
45     $__ZFS_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}'
48 __zfs_get_inheritable_properties()
50     $__ZFS_CMD get 2>&1 | awk '$3 == "YES" {print $1}'
53 __zfs_list_datasets()
55     $__ZFS_CMD list -H -o name -s name -t filesystem,volume "$@"
58 __zfs_list_filesystems()
60     $__ZFS_CMD list -H -o name -s name -t filesystem
63 __zfs_match_snapshot()
65     local base_dataset="${cur%@*}"
66     if [[ "$base_dataset" != "$cur" ]]
67     then
68         $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 "$base_dataset"
69     else
70         if [[ "$cur" != "" ]] && __zfs_list_datasets "$cur" &> /dev/null
71         then
72             $__ZFS_CMD list -H -o name -s name -t filesystem,volume -r "$cur" | tail -n +2
73             # We output the base dataset name even though we might be
74             # completing a command that can only take a snapshot, because it
75             # prevents bash from considering the completion finished when it
76             # ends in the bare @.
77             echo "$cur"
78             echo "$cur@"
79         else
80             local datasets
81             datasets="$(__zfs_list_datasets)"
82             # As above
83             echo "$datasets"
84             if [[ "$cur" == */ ]]
85             then
86                 # If the current command ends with a slash, then the only way
87                 # it can be completed with a single tab press (ie. in this pass)
88                 # is if it has exactly one child, so that's the only time we
89                 # need to offer a suggestion with an @ appended.
90                 local num_children
91                 # This is actually off by one as zfs list includes the named
92                 # dataset in addition to its children
93                 num_children=$(__zfs_list_datasets -d 1 "${cur%/}" 2> /dev/null | wc -l)
94                 if [[ $num_children != 2 ]]
95                 then
96                     return 0
97                 fi
98             fi
99             echo "$datasets" | awk '{print $1 "@"}'
100         fi
101     fi
104 __zfs_match_snapshot_or_bookmark()
106     local base_dataset="${cur%[#@]*}"
107     if [[ "$base_dataset" != "$cur" ]]
108     then
109         if [[ $cur == *@* ]]
110         then
111             $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 "$base_dataset"
112         else
113             $__ZFS_CMD list -H -o name -s name -t bookmark -d 1 "$base_dataset"
114         fi
115     else
116         $__ZFS_CMD list -H -o name -s name -t filesystem,volume
117         if [[ -e "$cur" ]] && $__ZFS_CMD list -H -o name -s name -t filesystem,volume "$cur" &> /dev/null
118         then
119             echo "$cur@"
120             echo "$cur#"
121         fi
122     fi
125 __zfs_match_multiple_snapshots()
127     local existing_opts
128     existing_opts="$(expr "$cur" : '\(.*\)[%,]')"
129     if [[ -e "$existing_opts" ]]
130     then
131         local base_dataset="${cur%@*}"
132         if [[ "$base_dataset" != "$cur" ]]
133         then
134             local cur="${cur##*,}"
135             if [[ $cur =~ ^%|%.*% ]]
136             then
137                 # correct range syntax is start%end
138                 return 1
139             fi
140             local range_start
141             range_start="$(expr "$cur" : '\(.*%\)')"
142             # shellcheck disable=SC2016
143             $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 "$base_dataset" | sed 's$.*@$'"$range_start"'$g'
144         fi
145     else
146         __zfs_match_snapshot_or_bookmark
147     fi
150 __zfs_list_volumes()
152     $__ZFS_CMD list -H -o name -s name -t volume
155 __zfs_argument_chosen()
157     local word property
158     for word in $(seq $((COMP_CWORD-1)) -1 2 2>/dev/null)
159     do
160         local prev="${COMP_WORDS[$word]}"
161         if [[ ${COMP_WORDS[$word-1]} != -[tos] ]]
162         then
163             if [[ "$prev" == [^,]*,* ]] || [[ "$prev" == *[@:\#]* ]]
164             then
165                 return 0
166             fi
167             for property in "$@"
168             do
169                 if [[ $prev == "$property"* ]]
170                 then
171                     return 0
172                 fi
173             done
174         fi
175     done
176     return 1
179 __zfs_complete_ordered_arguments()
181     local list1=$1
182     local list2=$2
183     local cur=$3
184     local extra=$4
185     # shellcheck disable=SC2086
186     if __zfs_argument_chosen $list1
187     then
188         mapfile -t COMPREPLY < <(compgen -W "$list2 $extra" -- "$cur")
189     else
190         mapfile -t COMPREPLY < <(compgen -W "$list1 $extra" -- "$cur")
191     fi
194 __zfs_complete_multiple_options()
196     local options=$1
197     local cur=$2
198     local existing_opts
200     mapfile -t COMPREPLY < <(compgen -W "$options" -- "${cur##*,}")
201     existing_opts=$(expr "$cur" : '\(.*,\)')
202     if [[ -n "$existing_opts" ]]
203     then
204         COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" )
205     fi
208 __zfs_complete_switch()
210     local options=$1
211     if [[ ${cur:0:1} == - ]]
212     then
213         mapfile -t COMPREPLY < <(compgen -W "-{$options}" -- "$cur")
214         return 0
215     else
216         return 1
217     fi
220 __zfs_complete_nospace()
222     # Google indicates that there may still be bash versions out there that
223     # don't have compopt.
224     if type compopt &> /dev/null
225     then
226         compopt -o nospace
227     fi
230 __zfs_complete()
232     local cur prev cmd cmds
233     COMPREPLY=()
234     if type _get_comp_words_by_ref &> /dev/null
235     then
236         # Don't split on colon
237         _get_comp_words_by_ref -n : -c cur -p prev -w COMP_WORDS -i COMP_CWORD
238     else
239         cur="${COMP_WORDS[COMP_CWORD]}"
240         prev="${COMP_WORDS[COMP_CWORD-1]}"
241     fi
242     cmd="${COMP_WORDS[1]}"
244     if [[ ${prev##*/} == zfs ]]
245     then
246         cmds=$(__zfs_get_commands)
247         mapfile -t COMPREPLY < <(compgen -W "$cmds -?" -- "$cur")
248         return 0
249     fi
251     case "${cmd}" in
252         bookmark)
253             if __zfs_argument_chosen
254             then
255                 mapfile -t COMPREPLY < <(compgen -W "${prev%@*}# ${prev/@/#}" -- "$cur")
256             else
257                 mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
258             fi
259             ;;
260         clone)
261             case "${prev}" in
262                 -o)
263                     mapfile -t COMPREPLY < <(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")
264                     __zfs_complete_nospace
265                     ;;
266                 *)
267                     if ! __zfs_complete_switch "o,p"
268                     then
269                         if __zfs_argument_chosen
270                         then
271                             mapfile -t COMPREPLY < <(compgen -W "$(__zfs_list_datasets)" -- "$cur")
272                         else
273                             mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
274                         fi
275                     fi
276                     ;;
277             esac
278             ;;
279         get)
280             case "${prev}" in
281                 -d)
282                     mapfile -t COMPREPLY < <(compgen -W "" -- "$cur")
283                     ;;
284                 -t)
285                     __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur"
286                     ;;
287                 -s)
288                     __zfs_complete_multiple_options "local default inherited temporary received none" "$cur"
289                     ;;
290                 -o)
291                     __zfs_complete_multiple_options "name property value source received all" "$cur"
292                     ;;
293                 *)
294                     if ! __zfs_complete_switch "H,r,p,d,o,t,s"
295                     then
296                         # shellcheck disable=SC2046
297                         if __zfs_argument_chosen $(__zfs_get_properties)
298                         then
299                             mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
300                         else
301                             __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur"
302                         fi
303                     fi
304                     ;;
305             esac
306             ;;
307         inherit)
308             if ! __zfs_complete_switch "r"
309             then
310                 __zfs_complete_ordered_arguments "$(__zfs_get_inheritable_properties)" "$(__zfs_match_snapshot)" "$cur"
311             fi
312             ;;
313         list)
314             case "${prev}" in
315                 -d)
316                     mapfile -t COMPREPLY < <(compgen -W "" -- "$cur")
317                     ;;
318                 -t)
319                     __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur"
320                     ;;
321                 -o)
322                     __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur"
323                     ;;
324                 -s|-S)
325                     mapfile -t COMPREPLY < <(compgen -W "$(__zfs_get_properties)" -- "$cur")
326                     ;;
327                 *)
328                     if ! __zfs_complete_switch "H,r,d,o,t,s,S"
329                     then
330                         mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
331                     fi
332                     ;;
333             esac
334             ;;
335         promote)
336             mapfile -t COMPREPLY < <(compgen -W "$(__zfs_list_filesystems)" -- "$cur")
337             ;;
338         rollback)
339             if ! __zfs_complete_switch "r,R,f"
340             then
341                 mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
342             fi
343             ;;
344         send)
345             if ! __zfs_complete_switch "D,n,P,p,R,v,e,L,i,I"
346             then
347                 if __zfs_argument_chosen
348                 then
349                     mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
350                 else
351                     if [[ $prev == -*i* ]]
352                     then
353                         mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot_or_bookmark)" -- "$cur")
354                     else
355                         mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
356                     fi
357                 fi
358             fi
359             ;;
360         snapshot)
361             case "${prev}" in
362                 -o)
363                     mapfile -t COMPREPLY < <(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")
364                     __zfs_complete_nospace
365                     ;;
366                 *)
367                     if ! __zfs_complete_switch "o,r"
368                     then
369                         mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
370                         __zfs_complete_nospace
371                     fi
372                     ;;
373             esac
374             ;;
375         set)
376             __zfs_complete_ordered_arguments "$(__zfs_get_editable_properties)" "$(__zfs_match_snapshot)" "$cur"
377             __zfs_complete_nospace
378             ;;
379         upgrade)
380             case "${prev}" in
381                 -a|-V|-v)
382                     mapfile -t COMPREPLY < <(compgen -W "" -- "$cur")
383                     ;;
384                 *)
385                     if ! __zfs_complete_switch "a,V,v,r"
386                     then
387                         mapfile -t COMPREPLY < <(compgen -W "$(__zfs_list_filesystems)" -- "$cur")
388                     fi
389                     ;;
390             esac
391             ;;
392         destroy)
393             if ! __zfs_complete_switch "d,f,n,p,R,r,v"
394             then
395                 __zfs_complete_multiple_options "$(__zfs_match_multiple_snapshots)" "$cur"
396                 __zfs_complete_nospace
397             fi
398             ;;
399         *)
400             mapfile -t COMPREPLY < <(compgen -W "$(__zfs_match_snapshot)" -- "$cur")
401             ;;
402     esac
403     if type __ltrim_colon_completions &> /dev/null
404     then
405         __ltrim_colon_completions "$cur"
406     fi
407     return 0
410 __zpool_get_commands()
412     $__ZPOOL_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | uniq
415 __zpool_get_properties()
417     $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all
420 __zpool_get_editable_properties()
422     $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}'
425 __zpool_list_pools()
427     $__ZPOOL_CMD list -H -o name
430 __zpool_complete()
432     local cur prev cmd cmds pools
433     COMPREPLY=()
434     cur="${COMP_WORDS[COMP_CWORD]}"
435     prev="${COMP_WORDS[COMP_CWORD-1]}"
436     cmd="${COMP_WORDS[1]}"
438     if [[ ${prev##*/} == zpool ]]
439     then
440         cmds=$(__zpool_get_commands)
441         mapfile -t COMPREPLY < <(compgen -W "$cmds" -- "$cur")
442         return 0
443     fi
445     case "${cmd}" in
446         get)
447             __zfs_complete_ordered_arguments "$(__zpool_get_properties)" "$(__zpool_list_pools)" "$cur"
448             return 0
449             ;;
450         import)
451             if [[ $prev == -d ]]
452             then
453                 _filedir -d
454             else
455                 mapfile -t COMPREPLY < <(compgen -W "$(__zpool_list_pools) -d" -- "$cur")
456             fi
457             return 0
458             ;;
459         set)
460             __zfs_complete_ordered_arguments "$(__zpool_get_editable_properties)" "$(__zpool_list_pools)" "$cur"
461             __zfs_complete_nospace
462             return 0
463             ;;
464         add|attach|clear|create|detach|offline|online|remove|replace)
465             pools="$(__zpool_list_pools)"
466             # shellcheck disable=SC2086
467             if __zfs_argument_chosen $pools
468             then
469                 _filedir
470             else
471                 mapfile -t COMPREPLY < <(compgen -W "$pools" -- "$cur")
472             fi
473             return 0
474             ;;
475         *)
476             mapfile -t COMPREPLY < <(compgen -W "$(__zpool_list_pools)" -- "$cur")
477             return 0
478             ;;
479     esac
483 complete -F __zfs_complete zfs
484 complete -F __zpool_complete zpool