diff --git a/completion/available/knife.completion.bash b/completion/available/knife.completion.bash index 191e428c..9b370209 100644 --- a/completion/available/knife.completion.bash +++ b/completion/available/knife.completion.bash @@ -19,7 +19,7 @@ _KNIFE_AUTOCOMPLETE_MAX_CACHE_AGE=86400 ### init _KAC_CACHE_TMP_DIR="$_KNIFE_AUTOCOMPLETE_CACHE_DIR/tmp" # make sure the cache dir exists -mkdir -p $_KAC_CACHE_TMP_DIR +mkdir -p "$_KAC_CACHE_TMP_DIR" ############################## ### Cache helper functions ### @@ -33,7 +33,7 @@ stat -c %Y /dev/null > /dev/null 2>&1 && _KAC_STAT_COMMAND="stat -c %Y" || _KAC_ # returns 1 otherwise _KAC_is_file_newer_than() { [ -f "$1" ] || return 1 - [ $(($(date +%s) - $($_KAC_STAT_COMMAND "$1"))) -gt $2 ] && return 1 || return 0 + [ $(($(date +%s) - $($_KAC_STAT_COMMAND "$1"))) -gt "$2" ] && return 1 || return 0 } # helper function for _KAC_get_and_regen_cache, see doc below @@ -42,10 +42,10 @@ _KAC_regen_cache() { local CACHE_PATH="$_KNIFE_AUTOCOMPLETE_CACHE_DIR/$CACHE_NAME" local TMP_FILE=$(mktemp "$_KAC_CACHE_TMP_DIR/$CACHE_NAME.XXXX") shift 1 - "$@" > $TMP_FILE 2> /dev/null + "$@" > "$TMP_FILE" 2> /dev/null # discard the temp file if it's empty AND the previous command didn't exit successfully, but still mark the cache as updated - [[ $? != 0 ]] && [[ $(cat $TMP_FILE | wc -l) == 0 ]] && rm -f $TMP_FILE && touch $CACHE_PATH && return 1 \ - || mv -f $TMP_FILE $CACHE_PATH + [[ $? != 0 ]] && [[ $(wc -l "$TMP_FILE") == 0 ]] && rm -f "$TMP_FILE" && touch "$CACHE_PATH" && return 1 \ + || mv -f "$TMP_FILE" "$CACHE_PATH" } # cached files can't have spaces in their names @@ -65,10 +65,14 @@ _KAC_get_command_from_cache_name() { _KAC_get_and_regen_cache() { # the cache name can't have space in it local CACHE_NAME=$(_KAC_get_cache_name_from_command "$@") - local REGEN_CMD="_KAC_regen_cache $CACHE_NAME $@" + local REGEN_CMD="_KAC_regen_cache $CACHE_NAME $*" _KAC_CACHE_PATH="$_KNIFE_AUTOCOMPLETE_CACHE_DIR/$CACHE_NAME" # no need to wait for the regen if the file already exists - [ -f $_KAC_CACHE_PATH ] && ($REGEN_CMD &) || $REGEN_CMD + if [[ -f "$_KAC_CACHE_PATH" ]]; then + ($REGEN_CMD &) + else + $REGEN_CMD + fi } # performs two things: first, deletes all obsolete temp files @@ -76,17 +80,18 @@ _KAC_get_and_regen_cache() { _KAC_clean_cache() { local FILE CMD # delete all obsolete temp files, could be lingering there for any kind of crash in the caching process - for FILE in $(ls $_KAC_CACHE_TMP_DIR); do - _KAC_is_file_newer_than $FILE $_KNIFE_AUTOCOMPLETE_MAX_CACHE_AGE || rm -f $FILE + for FILE in $(\ls "$_KAC_CACHE_TMP_DIR"); do + _KAC_is_file_newer_than "$FILE" "$_KNIFE_AUTOCOMPLETE_MAX_CACHE_AGE" || rm -f "$FILE" done # refresh really stale caches - for FILE in $(find $_KNIFE_AUTOCOMPLETE_CACHE_DIR -maxdepth 1 -type f -not -name '.*'); do - _KAC_is_file_newer_than $FILE $_KNIFE_AUTOCOMPLETE_MAX_CACHE_AGE && continue - # first let's get the original command - CMD=$(_KAC_get_command_from_cache_name $(basename "$FILE")) - # then regen the cache - _KAC_get_and_regen_cache "$CMD" > /dev/null - done + find "$_KNIFE_AUTOCOMPLETE_CACHE_DIR" -maxdepth 1 -type f -not -name '.*' \ + | while read -r FILE; do + _KAC_is_file_newer_than "$FILE" "$_KNIFE_AUTOCOMPLETE_MAX_CACHE_AGE" && continue + # first let's get the original command + CMD=$(_KAC_get_command_from_cache_name "$(basename "$FILE")") + # then regen the cache + _KAC_get_and_regen_cache "$CMD" > /dev/null + done } # perform a cache cleaning when loading this file @@ -109,32 +114,32 @@ _KAC_get_current_base_command() { local PREVIOUS="knife" local I=1 local CURRENT - while [ $I -le $COMP_CWORD ]; do + while [ $I -le "$COMP_CWORD" ]; do # command words are all lower-case - echo ${COMP_WORDS[$I]} | grep -E "^[a-z]+$" > /dev/null || break + echo "${COMP_WORDS[$I]}" | grep -E "^[a-z]+$" > /dev/null || break CURRENT="$PREVIOUS ${COMP_WORDS[$I]}" - cat $_KAC_CACHE_PATH | grep -E "^$CURRENT" > /dev/null || break + grep -E "^$CURRENT" "$_KAC_CACHE_PATH" > /dev/null || break PREVIOUS=$CURRENT - I=$(($I + 1)) + I=$((I + 1)) done _KAC_CURRENT_COMMAND=$PREVIOUS - [ $I -le $COMP_CWORD ] && _KAC_CURRENT_COMMAND_NB_WORDS=$I + [ $I -le "$COMP_CWORD" ] && _KAC_CURRENT_COMMAND_NB_WORDS=$I } # searches the position of the currently completed argument in the current base command # (i.e. handles "plural" arguments such as knife cookbook upload cookbook1 cookbook2 and so on...) # assumes the current base command is complete _KAC_get_current_arg_position() { - local CURRENT_ARG_POS=$(($_KAC_CURRENT_COMMAND_NB_WORDS + 1)) - local COMPLETE_COMMAND=$(cat $_KAC_CACHE_PATH | grep -E "^$_KAC_CURRENT_COMMAND") + local CURRENT_ARG_POS=$((_KAC_CURRENT_COMMAND_NB_WORDS + 1)) + local COMPLETE_COMMAND=$(grep -E "^$_KAC_CURRENT_COMMAND" "$_KAC_CACHE_PATH") local CURRENT_ARG - while [ $CURRENT_ARG_POS -le $COMP_CWORD ]; do - CURRENT_ARG=$(echo $COMPLETE_COMMAND | cut -d ' ' -f $CURRENT_ARG_POS) + while [ "$CURRENT_ARG_POS" -le "$COMP_CWORD" ]; do + CURRENT_ARG=$(echo "$COMPLETE_COMMAND" | cut -d ' ' -f "$CURRENT_ARG_POS") # we break if the current arg is a "plural" arg - echo $CURRENT_ARG | grep -E "^\\[[^]]+(\\.\\.\\.\\]|$)" > /dev/null && break - CURRENT_ARG_POS=$(($CURRENT_ARG_POS + 1)) + echo "$CURRENT_ARG" | grep -E "^\\[[^]]+(\\.\\.\\.\\]|$)" > /dev/null && break + CURRENT_ARG_POS=$((CURRENT_ARG_POS + 1)) done - echo $CURRENT_ARG_POS + echo "$CURRENT_ARG_POS" } # the actual auto-complete function @@ -143,8 +148,8 @@ _knife() { local RAW_LIST ITEM REGEN_CMD ARG_POSITION COMREPLY=() # get correct command & arg pos - _KAC_get_current_base_command && ARG_POSITION=$(_KAC_get_current_arg_position) || ARG_POSITION=$(($COMP_CWORD + 1)) - RAW_LIST=$(cat $_KAC_CACHE_PATH | grep -E "^$_KAC_CURRENT_COMMAND" | cut -d ' ' -f $ARG_POSITION | uniq) + _KAC_get_current_base_command && ARG_POSITION=$(_KAC_get_current_arg_position) || ARG_POSITION=$((COMP_CWORD + 1)) + RAW_LIST=$(grep -E "^$_KAC_CURRENT_COMMAND" "$_KAC_CACHE_PATH" | cut -d ' ' -f $ARG_POSITION | uniq) # we need to process that raw list a bit, most notably for placeholders # NOTE: I chose to explicitely fetch & cache _certain_ informations for the server (cookbooks & node names, etc) @@ -154,13 +159,13 @@ _knife() { LIST="" for ITEM in $RAW_LIST; do # always relevant if only lower-case chars : continuation of the base command - echo $ITEM | grep -E "^[a-z]+$" > /dev/null && LIST="$LIST $ITEM" && continue - case $ITEM in + echo "$ITEM" | grep -E "^[a-z]+$" > /dev/null && LIST="$LIST $ITEM" && continue + case "$ITEM" in *COOKBOOK*) # special case for cookbooks : from site or local [[ ${COMP_WORDS[2]} == 'site' ]] && REGEN_CMD="knife cookbook site list" || REGEN_CMD="knife cookbook list" - _KAC_get_and_regen_cache $REGEN_CMD - LIST="$LIST $(cat $_KAC_CACHE_PATH | cut -d ' ' -f 1)" + _KAC_get_and_regen_cache "$REGEN_CMD" + LIST="$LIST $(cut -d ' ' -f 1 < "$_KAC_CACHE_PATH")" continue ;; *ITEM*) @@ -182,8 +187,8 @@ _knife() { # not a generic argument we support... *) continue ;; esac - _KAC_get_and_regen_cache $REGEN_CMD - LIST="$LIST $(cat $_KAC_CACHE_PATH)" + _KAC_get_and_regen_cache "$REGEN_CMD" + LIST="$LIST $(cat "$_KAC_CACHE_PATH")" done COMPREPLY=($(compgen -W "${LIST}" -- ${COMP_WORDS[COMP_CWORD]})) }