运行 *.sh 文件而不输入其 .sh 扩展名

运行 *.sh 文件而不输入其 .sh 扩展名

有没有办法设置 bash 以便它执行任何带有扩展名的文件.sh,例如mycommand.sh,只需键入mycommand,就像在 Windows 中mycommand.bat只需键入mycommand?即可执行一样。

我已经使用chmod +x并将包含的目录放在mycommand.shPATH,因此无需说明这些内容。

一种显而易见的方法是重命名mycommand.shmycommand。我不想这样做。一些第三方产品(例如 Kafka 和一些数据库系统)提供的 shell 脚本都有扩展名.sh,我不想重命名不属于我的东西。

我特别想问的是是否可以制作.sh某种“已知”的可执行扩展,类似于 Windows 的PATHEXT环境变量。

答案1

Bash 为您提供了一种定义自己的“未找到命令”处理程序的方法:

如果名称既不是 shell 函数也不是内置函数,并且不包含斜杠,Bash 会在 的每个元素中搜索$PATH包含该名称的可执行文件的目录。[…] 如果搜索不成功,shell 会搜索名为 的已定义 shell 函数command_not_found_handle。如果该函数存在,则会在单独的执行环境中调用它,并使用原始命令和原始命令的参数作为其参数,并且该函数的退出状态将成为该子 shell 的退出状态。如果该函数未定义,shell 会打印错误消息并返回退出状态127

来源

这是一个可以解决您的问题的基本处理程序:

command_not_found_handle () {
   unset -f command_not_found_handle
   cmmnd="$1".sh
   shift
   exec "$cmmnd" "$@"
}

定义此函数后,如果您尝试运行mycommand但未找到命令,则 shell 将尝试使用应该获取的mycommand.sh所有命令行参数。mycommand

笔记:

  • 其他 shell 可能提供类似的功能。
  • unset如果找不到新命令,则阻止处理程序再次触发自身。
  • 您可能已经定义了一些处理程序。例如,在 Debian 中,软件包command-not-found依赖于其处理程序。如果您只是定义自己的处理程序,您将失去它。要保留旧功能,您需要模仿(或恢复)旧处理程序并在未找到新命令后运行其代码(但要确保它有效地工作,就像它得到了原始命令一样,即mycommand,不是mycommand.sh)。旧处理程序可能看起来像这是我的另一个答案

答案2

这是一个老问题,但它在我遇到这个问题时帮助了我,并且从那时起我对所选答案进行了改进,我认为值得分享。我的实现适用于所有可执行文件类型,而不仅仅是 .sh,并且当找不到命令时它不会弄乱错误消息:

command_not_found_handle() {
    unset -f command_not_found_handle
    found=false

    # Iterate over possible autocompletions.
    while IFS='' read -r cmmnd; do
        # If the only thing that was autocompleted was the extension, execute it,
        # but outside the loop because STDIN is redirected inside here.
        if [[ "$cmmnd" == "$1".* ]]; then
            found=true
            break
        fi
    done < <(compgen -c -- "$1") # Generate possible autocompletions for the command.

    $found && exec -- "$cmmnd" "${@:2}"
    echo "bash: '$1': command not found" >&2
    return 127
}

相关内容