In bash by default, pressing Tab will show all files and directories in the current directory. For example:
cat a<tab><tab>
would display something like aFile.txt apples.png aDirectory/
If you then completed to aDirectory
, it will show the contents of aDirectory
, for example:
cat aDirectory/<tab><tab>
might display file.txt file.mp4
It does this while keeping the cursor on the current argument. So pressing <tab><tab>
on cat aD
would end with the cursor like this cat aDirectory/|
(where the pipe is the cursor)
I've been trying to make my own bash completion scripts, and one is for a log
program. This program is broken down into months in individual files. So 2308
, 2309
etc. What I want is that when I press <tab><tab>
, until 4 characters are filled, it will display only the year and months, so 2308 2309
, but when that is filled (log 2308|
), pressing <tab><tab>
would then show the individual days in the file (so 230801 230803 230807
for example).
I managed to get this working, however the issue is if tab autocompletes (for example if the only options were 2308, 2309 and 2310
, then hitting <tab>
on 231
would autocomplete to 2310
), the cursor jumps to the next argument (log 2310 |
instead of log 2310|
) unlike the default behaviour with directories.
Then of course pressing tab does nothing, as it is no longer on ${COMP_WORDS[1]}
. How can you get the cursor to not jump to the next argument, until some condition is met (e.g. ${COMP_WORDS[1]}
is a file, or ${COMP_WORDS[1]}
is 6 characters long)?
This is the full function:
_logCompletion() {
if [ "${#COMP_WORDS[@]}" != "2" ]; then
if [ "${COMP_WORDS[1]}" == "-d" ] && [ "${#COMP_WORDS[@]}" == "3" ]; then # If -d was chosen, then display the avalible years/months
if [ ${#COMP_WORDS[2]} -ge 4 ]; then # If month already filled, start showing avalible days
curLogMonth=${COMP_WORDS[2]:0:4} # Get month in yymm format
[ -f ~/Programs/output/log/${curLogMonth}.json ] || return 0 # Check that month exists, if not, don't continue
curMonthSuggestions=$(cat ~/Programs/output/log/$curLogMonth.json | jq -r 'keys[]' | sed "s/\(.*\)/$curLogMonth\1/g") # Get the days avalible in given month
COMPREPLY=( $(compgen -W "$curMonthSuggestions" -- "${COMP_WORDS[2]}") )
return 0
else # Otherwise just show avalible months
COMPREPLY=( $(compgen -W "$(ls ~/Programs/output/log/ | grep -oE [0-9]*)" -- "${COMP_WORDS[2]}") )
return 0
fi
elif [ "${COMP_WORDS[1]}" == "-ds" ] && [ "${#COMP_WORDS[@]}" == "3" ]; then # If -ds was chosen, show avalible months
COMPREPLY=( $(compgen -W "$(ls ~/Programs/output/log/ | grep -oE [0-9]*)" -- "${COMP_WORDS[2]}") )
fi
return 0
fi
COMPREPLY=( $(compgen -W "-h -p -d -ds -s -f -fa" -- "${COMP_WORDS[1]}") )
}
complete -o nospace -F _logCompletion log
This way it would even not automatically append a SPACE when your date is already 6-digits long.
Update the else ...
block like this:
else # Otherwise just show avalible months
COMPREPLY=( $(compgen -W ... ) )
if [[ ${#COMPREPLY[@]} == 1 ]]; then
COMPREPLY[1]="${COMPREPLY[0]}/"
# ^ (other char is also fine)
fi
return 0
fi
This way yourself can control when you want it to auto append a SPACE.