]> git.proxmox.com Git - mirror_zfs.git/blame - contrib/bash_completion.d/zfs
Merge bash_completions changes from upstream
[mirror_zfs.git] / contrib / bash_completion.d / zfs
CommitLineData
fccaea45 1# Copyright (c) 2010-2016, Aneurin Price <aneurin.price@gmail.com>
1ffe90c5
TF
2
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:
11
12# The above copyright notice and this permission notice shall be
13# included in all copies or substantial portions of the Software.
14
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.
23
24if [[ -w /dev/zfs ]]; then
25 __ZFS_CMD="zfs"
26 __ZPOOL_CMD="zpool"
27else
28 __ZFS_CMD="sudo zfs"
29 __ZPOOL_CMD="sudo zpool"
30fi
31
fccaea45
JCML
32# Disable bash's built-in hostname completion, as this makes it impossible to
33# provide completions containing an @-sign, which is necessary for completing
34# snapshot names. If bash_completion is in use, this will already be disabled
35# and replaced with better completions anyway.
36shopt -u hostcomplete
37
1ffe90c5
TF
38__zfs_get_commands()
39{
40 $__ZFS_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | cut -f1 -d '|' | uniq
41}
42
43__zfs_get_properties()
44{
45 $__ZFS_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all name space
46}
47
48__zfs_get_editable_properties()
49{
50 $__ZFS_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}'
51}
52
53__zfs_get_inheritable_properties()
54{
55 $__ZFS_CMD get 2>&1 | awk '$3 == "YES" {print $1}'
56}
57
58__zfs_list_datasets()
59{
fccaea45 60 $__ZFS_CMD list -H -o name -s name -t filesystem,volume "$@"
1ffe90c5
TF
61}
62
63__zfs_list_filesystems()
64{
fccaea45 65 $__ZFS_CMD list -H -o name -s name -t filesystem
1ffe90c5
TF
66}
67
68__zfs_match_snapshot()
69{
70 local base_dataset=${cur%@*}
71 if [[ $base_dataset != $cur ]]
72 then
fccaea45 73 $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 $base_dataset
1ffe90c5 74 else
fccaea45
JCML
75 if [[ $cur != "" ]] && __zfs_list_datasets $cur &> /dev/null
76 then
77 $__ZFS_CMD list -H -o name -s name -t filesystem -r $cur | tail -n +2
78 # We output the base dataset name even though we might be
79 # completing a command that can only take a snapshot, because it
80 # prevents bash from considering the completion finished when it
81 # ends in the bare @.
82 echo $cur
83 echo $cur@
84 else
85 local datasets=$(__zfs_list_datasets)
86 # As above
87 echo $datasets
88 if [[ "$cur" == */ ]]
89 then
90 # If the current command ends with a slash, then the only way
91 # it can be completed with a single tab press (ie. in this pass)
92 # is if it has exactly one child, so that's the only time we
93 # need to offer a suggestion with an @ appended.
94 local num_children
95 # This is actually off by one as zfs list includes the named
96 # dataset in addition to its children
97 num_children=$(__zfs_list_datasets -d 1 ${cur%/} 2> /dev/null | wc -l)
98 if [[ $num_children != 2 ]]
99 then
100 return 0
101 fi
102 fi
103 echo "$datasets" | awk '{print $1"@"}'
104 fi
1ffe90c5
TF
105 fi
106}
107
fccaea45 108__zfs_match_snapshot_or_bookmark()
1ffe90c5 109{
fccaea45 110 local base_dataset=${cur%[#@]*}
1ffe90c5
TF
111 if [[ $base_dataset != $cur ]]
112 then
fccaea45
JCML
113 if [[ $cur == *@* ]]
114 then
115 $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 $base_dataset
116 else
117 $__ZFS_CMD list -H -o name -s name -t bookmark -d 1 $base_dataset
118 fi
119 else
120 $__ZFS_CMD list -H -o name -s name -t filesystem,volume
121 if [[ $cur != "" ]] && $__ZFS_CMD list -H -o name -s name -t filesystem,volume $cur &> /dev/null
122 then
123 echo $cur@
124 echo $cur#
125 fi
1ffe90c5
TF
126 fi
127}
128
129__zfs_match_multiple_snapshots()
130{
131 local existing_opts=$(expr "$cur" : '\(.*\)[%,]')
132 if [[ $existing_opts ]]
133 then
134 local base_dataset=${cur%@*}
135 if [[ $base_dataset != $cur ]]
136 then
137 local cur=${cur##*,}
138 if [[ $cur =~ ^%|%.*% ]]
139 then
140 # correct range syntax is start%end
141 return 1
142 fi
143 local range_start=$(expr "$cur" : '\(.*%\)')
fccaea45 144 $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 $base_dataset | sed 's$.*@$'$range_start'$g'
1ffe90c5
TF
145 fi
146 else
fccaea45 147 __zfs_match_snapshot_or_bookmark
1ffe90c5
TF
148 fi
149}
150
151__zfs_list_volumes()
152{
fccaea45 153 $__ZFS_CMD list -H -o name -s name -t volume
1ffe90c5
TF
154}
155
156__zfs_argument_chosen()
157{
158 local word property
159 for word in $(seq $((COMP_CWORD-1)) -1 2)
160 do
161 local prev="${COMP_WORDS[$word]}"
162 if [[ ${COMP_WORDS[$word-1]} != -[tos] ]]
163 then
fccaea45 164 if [[ "$prev" == [^,]*,* ]] || [[ "$prev" == *[@:\#]* ]]
1ffe90c5
TF
165 then
166 return 0
167 fi
168 for property in $@
169 do
fccaea45 170 if [[ $prev == "$property"* ]]
1ffe90c5
TF
171 then
172 return 0
173 fi
174 done
175 fi
176 done
177 return 1
178}
179
180__zfs_complete_ordered_arguments()
181{
182 local list1=$1
183 local list2=$2
184 local cur=$3
185 local extra=$4
186 if __zfs_argument_chosen $list1
187 then
188 COMPREPLY=($(compgen -W "$list2 $extra" -- "$cur"))
189 else
190 COMPREPLY=($(compgen -W "$list1 $extra" -- "$cur"))
191 fi
192}
193
194__zfs_complete_multiple_options()
195{
196 local options=$1
197 local cur=$2
198
199 COMPREPLY=($(compgen -W "$options" -- "${cur##*,}"))
200 local existing_opts=$(expr "$cur" : '\(.*,\)')
201 if [[ $existing_opts ]]
202 then
203 COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" )
204 fi
205}
206
207__zfs_complete_switch()
208{
209 local options=$1
210 if [[ ${cur:0:1} == - ]]
211 then
212 COMPREPLY=($(compgen -W "-{$options}" -- "$cur"))
213 return 0
214 else
215 return 1
216 fi
217}
218
fccaea45
JCML
219__zfs_complete_nospace()
220{
221 # Google indicates that there may still be bash versions out there that
222 # don't have compopt.
223 if type compopt &> /dev/null
224 then
225 compopt -o nospace
226 fi
227}
228
1ffe90c5
TF
229__zfs_complete()
230{
231 local cur prev cmd cmds
232 COMPREPLY=()
fccaea45
JCML
233 if type _get_comp_words_by_ref &> /dev/null
234 then
235 # Don't split on colon
236 _get_comp_words_by_ref -n : -c cur -p prev -w COMP_WORDS -i COMP_CWORD
237 else
238 cur="${COMP_WORDS[COMP_CWORD]}"
239 prev="${COMP_WORDS[COMP_CWORD-1]}"
240 fi
1ffe90c5
TF
241 cmd="${COMP_WORDS[1]}"
242
243 if [[ ${prev##*/} == zfs ]]
244 then
245 cmds=$(__zfs_get_commands)
246 COMPREPLY=($(compgen -W "$cmds -?" -- "$cur"))
247 return 0
248 fi
249
250 case "${cmd}" in
fccaea45
JCML
251 bookmark)
252 if __zfs_argument_chosen
253 then
254 COMPREPLY=($(compgen -W "${prev%@*}# ${prev/@/#}" -- "$cur"))
255 else
256 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
257 fi
258 ;;
1ffe90c5
TF
259 clone)
260 case "${prev}" in
261 -o)
262 COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur"))
fccaea45 263 __zfs_complete_nospace
1ffe90c5
TF
264 ;;
265 *)
266 if ! __zfs_complete_switch "o,p"
267 then
268 if __zfs_argument_chosen
269 then
270 COMPREPLY=($(compgen -W "$(__zfs_list_datasets)" -- "$cur"))
271 else
272 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
273 fi
274 fi
275 ;;
276 esac
277 ;;
278 get)
279 case "${prev}" in
280 -d)
281 COMPREPLY=($(compgen -W "" -- "$cur"))
282 ;;
283 -t)
2bc07c6d 284 __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur"
1ffe90c5
TF
285 ;;
286 -s)
2bc07c6d 287 __zfs_complete_multiple_options "local default inherited temporary received none" "$cur"
1ffe90c5
TF
288 ;;
289 -o)
290 __zfs_complete_multiple_options "name property value source received all" "$cur"
291 ;;
292 *)
293 if ! __zfs_complete_switch "H,r,p,d,o,t,s"
294 then
295 if __zfs_argument_chosen $(__zfs_get_properties)
296 then
fccaea45 297 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
1ffe90c5
TF
298 else
299 __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur"
300 fi
301 fi
302 ;;
303 esac
304 ;;
305 inherit)
306 if ! __zfs_complete_switch "r"
307 then
fccaea45 308 __zfs_complete_ordered_arguments "$(__zfs_get_inheritable_properties)" "$(__zfs_match_snapshot)" $cur
1ffe90c5
TF
309 fi
310 ;;
311 list)
312 case "${prev}" in
313 -d)
314 COMPREPLY=($(compgen -W "" -- "$cur"))
315 ;;
316 -t)
2bc07c6d 317 __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur"
1ffe90c5
TF
318 ;;
319 -o)
320 __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur"
321 ;;
322 -s|-S)
323 COMPREPLY=($(compgen -W "$(__zfs_get_properties)" -- "$cur"))
324 ;;
325 *)
326 if ! __zfs_complete_switch "H,r,d,o,t,s,S"
327 then
fccaea45 328 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
1ffe90c5
TF
329 fi
330 ;;
331 esac
332 ;;
333 promote)
334 COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur"))
335 ;;
336 rollback)
337 if ! __zfs_complete_switch "r,R,f"
338 then
339 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
340 fi
341 ;;
342 send)
fccaea45 343 if ! __zfs_complete_switch "D,n,P,p,R,v,e,L,i,I"
1ffe90c5 344 then
fccaea45
JCML
345 if __zfs_argument_chosen
346 then
347 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
348 else
349 if [[ $prev == -*i* ]]
350 then
351 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot_or_bookmark)" -- "$cur"))
352 else
353 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
354 fi
355 fi
1ffe90c5
TF
356 fi
357 ;;
358 snapshot)
359 case "${prev}" in
360 -o)
361 COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur"))
fccaea45 362 __zfs_complete_nospace
1ffe90c5
TF
363 ;;
364 *)
365 if ! __zfs_complete_switch "o,r"
366 then
fccaea45
JCML
367 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
368 __zfs_complete_nospace
1ffe90c5
TF
369 fi
370 ;;
371 esac
372 ;;
373 set)
fccaea45
JCML
374 __zfs_complete_ordered_arguments "$(__zfs_get_editable_properties)" "$(__zfs_match_snapshot)" $cur
375 __zfs_complete_nospace
1ffe90c5
TF
376 ;;
377 upgrade)
378 case "${prev}" in
379 -a|-V|-v)
380 COMPREPLY=($(compgen -W "" -- "$cur"))
381 ;;
382 *)
383 if ! __zfs_complete_switch "a,V,v,r"
384 then
385 COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur"))
386 fi
387 ;;
388 esac
389 ;;
390 destroy)
391 if ! __zfs_complete_switch "d,f,n,p,R,r,v"
392 then
393 __zfs_complete_multiple_options "$(__zfs_match_multiple_snapshots)" $cur
fccaea45 394 __zfs_complete_nospace
1ffe90c5
TF
395 fi
396 ;;
397 *)
fccaea45 398 COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur"))
1ffe90c5
TF
399 ;;
400 esac
fccaea45
JCML
401 if type __ltrim_colon_completions &> /dev/null
402 then
403 __ltrim_colon_completions "$cur"
404 fi
1ffe90c5
TF
405 return 0
406}
407
408__zpool_get_commands()
409{
410 $__ZPOOL_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | uniq
411}
412
413__zpool_get_properties()
414{
415 $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all
416}
417
418__zpool_get_editable_properties()
419{
420 $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}'
421}
422
423__zpool_list_pools()
424{
425 $__ZPOOL_CMD list -H -o name
426}
427
428__zpool_complete()
429{
430 local cur prev cmd cmds
431 COMPREPLY=()
432 cur="${COMP_WORDS[COMP_CWORD]}"
433 prev="${COMP_WORDS[COMP_CWORD-1]}"
434 cmd="${COMP_WORDS[1]}"
435
436 if [[ ${prev##*/} == zpool ]]
437 then
438 cmds=$(__zpool_get_commands)
439 COMPREPLY=($(compgen -W "$cmds" -- "$cur"))
440 return 0
441 fi
442
443 case "${cmd}" in
444 get)
445 __zfs_complete_ordered_arguments "$(__zpool_get_properties)" "$(__zpool_list_pools)" $cur
446 return 0
447 ;;
448 import)
449 if [[ $prev == -d ]]
450 then
451 _filedir -d
452 else
453 COMPREPLY=($(compgen -W "$(__zpool_list_pools) -d" -- "$cur"))
454 fi
455 return 0
456 ;;
457 set)
458 __zfs_complete_ordered_arguments "$(__zpool_get_editable_properties)" "$(__zpool_list_pools)" $cur
fccaea45 459 __zfs_complete_nospace
1ffe90c5
TF
460 return 0
461 ;;
462 add|attach|clear|create|detach|offline|online|remove|replace)
463 local pools="$(__zpool_list_pools)"
464 if __zfs_argument_chosen $pools
465 then
466 _filedir
467 else
468 COMPREPLY=($(compgen -W "$pools" -- "$cur"))
469 fi
470 return 0
471 ;;
472 *)
473 COMPREPLY=($(compgen -W "$(__zpool_list_pools)" -- "$cur"))
474 return 0
475 ;;
476 esac
477
478}
479
480complete -F __zfs_complete zfs
481complete -F __zpool_complete zpool