【问题标题】:bash expand cd with shortcuts like zshbash 用 zsh 之类的快捷方式扩展 cd
【发布时间】:2014-09-24 10:38:44
【问题描述】:

是否可以在 bash 中扩展类似的东西

cd /u/lo/b

cd /usr/local/bin

?

【问题讨论】:

  • 哇,这太酷了。如果 Zsh 的数组扩展机制很简单,我也会使用它。
  • 我认为开箱即用是不可行的。您可以使用内置的compgencomplete bash 开发自定义脚本,并在每次按tab 时使用bind -x '"\t":"/path/to/myscript"' 执行您的脚本。编辑:我会试一试(为了科学)并稍后在这里发布,除非有人想出了一个已经这样做的库/内置函数

标签: bash path expand cd


【解决方案1】:

对不起,我不能早点发帖,我被困在工作中,绑定功能比我最初想象的更容易出现问题。

这是我想出的:

绑定如下脚本:

#!/bin/bash
#$HOME/.bashrc.d/autocomplete.sh
autocomplete_wrapper() {
    BASE="${READLINE_LINE% *} "           #we save the line except for the last argument
    [[ "$BASE" == "$READLINE_LINE " ]] && BASE="";  #if the line has only 1 argument, we set the BASE to blank
    EXPANSION=($(autocomplete "${READLINE_LINE##* }"))
    [[ ${#EXPANSION[@]} -gt 1 ]] && echo "${EXPANSION[@]:1}"  #if there is more than 1 match, we echo them
    READLINE_LINE="$BASE${EXPANSION[0]}"  #the current line is now the base + the 1st element
    READLINE_POINT=${#READLINE_LINE}      #we move our cursor at the end of the current line
}

autocomplete() {
    LAST_CMD="$1"
    #Special starting character expansion for '~', './' and '/'
    [[ "${LAST_CMD:0:1}" == "~" ]] && LAST_CMD="$HOME${LAST_CMD:1}"
    S=1; [[ "${LAST_CMD:0:1}" == "/" || "${LAST_CMD:0:2}" == "./" ]] && S=2; #we don't expand those

    #we do the path expansion of the last argument here by adding a * before each /
    EXPANSION=($(echo "$LAST_CMD*" | sed s:/:*/:"$S"g))

    if [[ ! -e "${EXPANSION[0]}" ]];then #if the path cannot be expanded, we don't change the output
        echo "$LAST_CMD"
    elif [[ "${#EXPANSION[@]}" -eq 1 ]];then #else if there is only one match, we output it
        echo "${EXPANSION[0]}"
    else
        #else we expand the path as much as possible and return all the possible results
        while [[ $l -le "${#EXPANSION[0]}" ]]; do
            for i in "${EXPANSION[@]}"; do
                if [[ "${EXPANSION[0]:$l:1}" != "${i:$l:1}" ]]; then
                    CTRL_LOOP=1
                    break
                fi
            done
            [[ $CTRL_LOOP -eq 1 ]] && break
            ((l++))
        done
        #we add the partial solution at the beggining of the array of solutions
        echo "${EXPANSION[0]:0:$l} ${EXPANSION[@]}"
    fi
}

使用以下命令:

    source "$HOME/.bashrc.d/autocomplete.sh" 
    bind -x '"\t" : autocomplete_wrapper'

输出:

>$ cd /u/lo/b<TAB>
>$ cd /usr/local/bin


>$ cd /u/l<TAB>
/usr/local /usr/lib
>$ cd /usr/l

绑定行可以添加到您的~/.bashrc 文件中,执行如下操作:

if [[ -s "$HOME/.bashrc.d/autocomplete.sh" ]]; then
    source "$HOME/.bashrc.d/autocomplete.sh" 
    bind -x '"\t" : autocomplete_wrapper'
fi

(取自this answer

此外,我强烈建议不要将此命令绑定到您的 Tab 键,因为它会覆盖默认的自动完成功能。

注意:在某些情况下,这会出现问题,例如,如果您尝试自动完成 "/path/with spaces/something",因为要完成的最后一个参数由 ${READLINE_LINE##* } 确定。如果这是您的问题,您应该编写一个函数,在考虑引号时返回一行的最后一个参数

随时要求进一步澄清,我欢迎任何改进此脚本的建议

【讨论】:

  • 而且它似乎也破坏了命令(来自 $PATH)的竞争:(
【解决方案2】:

我想出了一个不会破坏现有 bash 的替代解决方案 其他地方的补全规则。

这个想法是在路径的每个元素上附加一个通配符(星号),然后 从那里调用正常的 bash 完成过程。所以当用户输入 /u/lo/b&lt;Tab&gt; 我的函数将其替换为 /u*/lo*/b* 并调用 bash 照常完成。

要启用描述的行为源this file 〜/ .bashrc。支持的功能有:

  • 完整路径中的特殊字符如果存在会自动转义
  • 波浪号表达式已正确扩展(根据bash documentation
  • 如果用户已经开始在引号中写入路径,则没有字符转义 应用。相反,引号在展开后用匹配字符关闭 路径。
  • 如果bash-completion 包已在使用中,此代码将安全地覆盖 它的_filedir 功能。无需额外配置。

观看演示截屏视频,了解此功能的实际效果:

下面的完整代码清单(不过,您应该查看GitHub repo 以获取最新更新):

#!/usr/bin/env bash
#
# Zsh-like expansion of incomplete file paths for Bash.
# Source this file from your ~/.bashrc to enable the described behavior.
#
# Example: `/u/s/a<Tab>` will be expanded into `/usr/share/applications`
#


# Copyright 2018 Vitaly Potyarkin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.




#
# Take a single incomplete path and fill it with wildcards
# e.g. /u/s/app/ -> /u*/s*/app*
#
_put_wildcards_into_path() {
    local PROCESSED TILDE_EXPANSION
    PROCESSED=$( \
        echo "$@" | \
        sed \
            -e 's:\([^\*\~]\)/:\1*/:g' \
            -e 's:\([^\/\*]\)$:\1*:g' \
            -e 's:\/$::g' \
            -e 's:^\(\~[^\/]*\)\*\/:\1/:' \
            -Ee 's:(\.+)\*/:\1/:g' \
    )
    eval "TILDE_EXPANSION=$(printf '%q' "$PROCESSED"|sed -e 's:^\\\~:~:g')"
    echo "$TILDE_EXPANSION"
}


#
# Bash completion function for expanding partial paths
#
# This is a generic worker. It accepts 'file' or 'directory' as the first
# argument to specify desired completion behavior
#
_complete_partial() {
    local WILDCARDS ACTION LINE OPTION INPUT UNQUOTED_INPUT QUOTE

    ACTION="$1"
    if [[ "_$1" == "_-d" ]]
    then  # _filedir compatibility
        ACTION="directory"
    fi
    INPUT="${COMP_WORDS[$COMP_CWORD]}"

    # Detect and strip opened quotes
    if [[ "${INPUT:0:1}" == "'" || "${INPUT:0:1}" == '"' ]]
    then
        QUOTE="${INPUT:0:1}"
        INPUT="${INPUT:1}"
    else
        QUOTE=""
    fi

    # Add wildcards to each path element
    WILDCARDS=$(_put_wildcards_into_path "$INPUT")

    # Collect completion options
    COMPREPLY=()
    while read -r -d $'\n' LINE
    do
        if [[ "_$ACTION" == "_directory" && ! -d "$LINE" ]]
        then  # skip non-directory paths when looking for directory
            continue
        fi
        if [[ -z "$LINE" ]]
        then  # skip empty suggestions
            continue
        fi
        if [[ -z "$QUOTE"  ]]
        then  # escape special characters unless user has opened a quote
            LINE=$(printf "%q" "$LINE")
        fi
        COMPREPLY+=("$LINE")
    done <<< $(compgen -G "$WILDCARDS" "$WILDCARDS" 2>/dev/null)
    return 0  # do not clutter $? value (last exit code)
}


# Wrappers
_complete_partial_dir() { _complete_partial directory; }
_complete_partial_file() { _complete_partial file; }

# Enable enhanced completion
complete -o bashdefault -o default -o nospace -D -F _complete_partial_file

# Optional. Make sure `cd` is autocompleted only with directories
complete -o bashdefault -o default -o nospace -F _complete_partial_dir cd

# Override bash-completion's _filedir (if it's in use)
# https://salsa.debian.org/debian/bash-completion
_filedir_original_code=$(declare -f _filedir|tail -n+2)
if [[ ! -z "$_filedir_original_code" ]]
then
    eval "_filedir_original() $_filedir_original_code"
    _filedir() {
        _filedir_original "$@"
        _complete_partial "$@"
    }
fi

# Readline configuration for better user experience
bind 'TAB:menu-complete'
bind 'set colored-completion-prefix on'
bind 'set colored-stats on'
bind 'set completion-ignore-case on'
bind 'set menu-complete-display-prefix on'
bind 'set show-all-if-ambiguous on'
bind 'set show-all-if-unmodified on'

【讨论】:

  • 到目前为止,这是一个非常棒的解决方案,永远。
猜你喜欢
  • 2019-03-17
  • 1970-01-01
  • 2014-06-01
  • 1970-01-01
  • 2016-10-25
  • 2021-08-25
  • 2021-01-09
  • 2012-05-10
  • 1970-01-01
相关资源
最近更新 更多