如何在command_not_found_handle函数中改变当前工作目录

5

我正在尝试编写一个Bash中的未找到处理程序,它执行以下操作:

  1. 如果$1存在且是一个目录,则cd进入该目录。
  2. 如果$1存在于用户定义的目录$DEV_DIR中,则cd进入该目录。
  3. 如果前面的条件都不适用,则失败。

现在我的代码大致如下:

export DEV_DIR=/Users/federico/programacion/

function command_not_found_handle () {
    if [ -d $1 ]; then          # the dir exists in '.'
        cd $1
    else
        to=$DEV_DIR$1
        if [ -d $to ]; then
            cd $to
            echo `pwd`
        else
            echo "${1}: command not found"
        fi
    fi
}

尽管看起来它正在工作(echo pwd命令打印了预期的目录),但实际shell中的目录并没有改变。

我原本以为,由于这是我.bashrc内的函数,因此shell不会fork,我可以执行cd,但显然这并没有起作用。如果您有解决方法,请告诉我。


为什么你需要这样做?很可能有更好的方法来实现你所想要的。 - Dennis Williamson
2
我想以一种简单的方式在存放所有项目的目录中移动子目录。做cd ~/prog; cd something有点烦人。我很乐意听取其他建议。 - Federico Builes
4个回答

3

我认为所发生的情况是,在设置任何重定向但在查找命令之前,shell会fork(),因此command_not_found_handle无法影响交互式shell进程。


1
+1 比较:f() { echo "$$ $BASHPID"; }; f; command_not_found_handle() { echo "$$ $BASHPID"; }; fooble(假设您没有任何名为“fooble”的内容)。将分叉出一个新的shell。(此外,请注意,如果在函数中包括$SHLVL$BASH_SUBSHELL进行额外比较,则这些变量不会变化。) - Dennis Williamson

1

您似乎想要做的事情可能部分地可以使用autocd功能实现:

shopt -s autocd

来自man bash

autocd - 如果设置了,命令名称是一个目录的名称,则会执行该命令,就像它是cd命令的参数一样。此选项仅由交互式shell使用。

否则,只需创建一个函数,通过名称调用该函数执行您尝试使用command_not_found_handle进行的操作。


这对于在 . 内的目录完美运作,但我最想做的是能够快速移动到其他位置的目录。不过我会记住的,谢谢。 - Federico Builes
1
@FedericoBuiles:关于我在最后一句提出的建议,您怎么看? - Dennis Williamson
这需要我每次想要切换时输入函数名称(这实际上与设置 autocd 相同)。但我很感激你的努力。 - Federico Builes

0

如果您在主shell中将此程序作为脚本运行,它不会更改目录,因为它在执行时创建了一个子shell。如果您在当前shell中源化脚本,则会产生所需的效果。

~/wbailey> source command_not_found.sh

话虽如此,我认为以下代码可以实现相同的结果:

wesbailey@feynman:~/code_katas> cd xxx 2> /dev/null || cd ..; pwd
/Users/wesbailey

只需将“..”替换为您定义的环境变量目录,并在您的.bashrc文件中创建别名即可。


这不是它不工作的原因。该函数在当前 shell 中定义,但 Bash 分叉一个新的 shell 来执行该函数(这通常不是这种情况)。 - Dennis Williamson
定义别名的想法正是我不想做的。我的$DEV_DIR中有82个目录,所以我不想为每个目录创建一个新的别名。 - Federico Builes
@FedericoBuiles,他谈论的是用别名覆盖cd命令,该别名还尝试cd到您的项目目录,而不是每个条目都有一个单独的别名。您可能希望实际上用shell函数替换cd命令,这样您可以在尝试内置cd命令之前检查例如[ -d“$mydir/$1”]。此外,pwd不接受目录作为参数,所以我不确定Wes的意图是什么。 - Peter Cordes

0

我曾经有过同样的愿望,而我一直在使用的解决方案是通过在command_not_found处理程序内部发出命令gnome-terminal --tab --working-directory="$FOLDER"来打开一个新的gnome终端选项卡。

但今天我想到了一种不与特定终端应用程序绑定,但确切具有预期行为的解决方案。

该解决方案使用PROMPT_COMMAND,在每个提示符之前运行。 PROMPT_COMMAND绑定到负责检查与当前shell相关的文件并cd到指定目录的函数。

然后,当需要更改目录时,command_not_found_handle填写该文件。我的原始command_not_found_handle还会检出git分支,如果当前目录是git存储库并且名称与现有分支匹配。但为了专注于回答当前问题,我已剥离了该代码部分。

command_not_found_handle使用find搜索与给定名称匹配的目录,并仅在目录树中向下2级,从配置列表开始。

要添加到 bash_rc 中的代码如下:
PROMPT_COMMAND=current_shell_cd
CD_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/bash-cd/$$.cd"

current_shell_cd() {
    if [ -r "$CD_FILE" ]; then
        local CD_TARGET="$( cat "$CD_FILE" )"
        [ ! -z "$CD_TARGET" ] && cd "$CD_TARGET" 2>/dev/null
        rm "$CD_FILE"
    fi
}

command_not_found_handle () { 
    local COMMAND="$1";
    # List folders which are going to be checked
    local BASE_FOLDER_LIST=(
        "$HOME/Desenvolvimento"
        "/var/www/html"  
        "$HOME/.local/opt/"
    )
    local FOLDER=$(
        find "${BASE_FOLDER_LIST[@]}" \
             -maxdepth 2 -type d \
             -iname "$COMMAND" -print -quit )
    if [ ! -z "$FOLDER" -a -d "$FOLDER" ]
    then
        mkdir -p "$( dirname "$CD_FILE" )"
        echo "$FOLDER" > "$CD_FILE"
    else
        printf "%s: command not found\n" "$1" 1>&2
        return 127
    fi
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接