goto shell 实用程序允许用户导航到别名目录,并且还支持自动完成。
工作原理
在使用 goto 之前,您需要注册您的目录别名。例如
goto -r dev /home/iridakos/development
然后更改到该目录,例如
goto dev

goto 中的自动完成
goto 附带一个不错的自动完成脚本——每当您在 goto 命令后按下 Tab 键时,Bash 或 Zsh 将提示您可用的别名建议
$ goto <tab>
bc /etc/bash_completion.d
dev /home/iridakos/development
rubies /home/iridakos/.rvm/rubies
安装 goto
有几种方法可以安装 goto。
通过脚本
克隆存储库并以超级用户或 root 身份运行安装脚本
git clone https://github.com/iridakos/goto.git
cd goto
sudo ./install
手动
将文件 goto.sh 复制到您文件系统中的某个位置,并在您的 .zshrc 或 .bashrc 中添加一行来 source 它。
例如,如果您将文件放在您的主文件夹中,您只需将以下行添加到您的 .zshrc 或 .bashrc 文件中
source ~/goto.sh
MacOS Homebrew
MacOS 中的 Bash shell 有一个名为 goto 的公式
brew install goto
添加彩色输出
echo -e "\$include /etc/inputrc\nset colored-completion-prefix on" >> ~/.inputrc
注意
- 安装后您需要重启您的 shell。
- 您需要在 MacOS 中为 Bash 启用 Bash 补全功能(请参阅此问题)。
- 如果您没有启用它,您可以使用 brew install bash-completion 安装它。
使用 goto 的方法
更改为别名目录
要更改为别名目录,请键入
goto <alias>
例如
goto dev
注册别名
要注册目录别名,请键入
goto -r <alias> <directory>
或
goto --register <alias> <directory>
例如
goto -r blog /mnt/external/projects/html/blog
或
goto --register blog /mnt/external/projects/html/blog
注意
- goto 扩展目录,因此您可以轻松地使用以下命令别名您当前的目录,它将自动别名为整个路径
goto -r last_release .
- 在别名名称后按 Tab 键会提供 shell 的默认目录建议。
注销别名
要注销别名,请使用
goto -u <alias>
或
goto --unregister <alias>
例如
goto -u last_release
或
goto --unregister last_release
注意: 通过在命令(-u 或 --unregister)后按 Tab 键,补全脚本将提示您已注册别名的列表。
列出别名
要获取当前注册别名的列表,请使用
goto -l
或
goto --list
展开别名
要将别名展开为其值,请使用
goto -x <alias>
或
goto --expand <alias>
例如
goto -x last_release
或
goto --expand last_release
清理别名
要从文件系统中不再可访问的目录中清理别名,请使用
goto -c
或
goto --cleanup
获取帮助
要查看该工具的帮助信息,请使用
goto -h
或
goto --help
检查版本
要查看该工具的版本,请使用
goto -v
或
goto --version
在更改目录之前推送
要在更改目录之前将当前目录推送到目录堆栈上,请键入
goto -p <alias>
或
goto --push <alias>
恢复到推送的目录
要返回到推送的目录,请键入
goto -o
或
goto --pop
注意: 此命令等效于 popd,但在 goto 命令中。
故障排除
如果您在 Zsh 中看到错误 command not found: compdef,这意味着您需要加载 bashcompinit。为此,请将此附加到您的 .zshrc 文件
autoload bashcompinit
bashcompinit
参与其中
goto 工具在 MIT 许可证条款下开源,欢迎贡献。要了解更多信息,请访问 goto 的 GitHub 存储库中的 Contributing 部分。
goto 脚本
goto()
{
local target
_goto_resolve_db
if [ -z "$1" ]; then
# display usage and exit when no args
_goto_usage
return
fi
subcommand="$1"
shift
case "$subcommand" in
-c|--cleanup)
_goto_cleanup "$@"
;;
-r|--register) # Register an alias
_goto_register_alias "$@"
;;
-u|--unregister) # Unregister an alias
_goto_unregister_alias "$@"
;;
-p|--push) # Push the current directory onto the pushd stack, then goto
_goto_directory_push "$@"
;;
-o|--pop) # Pop the top directory off of the pushd stack, then change that directory
_goto_directory_pop
;;
-l|--list)
_goto_list_aliases
;;
-x|--expand) # Expand an alias
_goto_expand_alias "$@"
;;
-h|--help)
_goto_usage
;;
-v|--version)
_goto_version
;;
*)
_goto_directory "$subcommand"
;;
esac
return $?
}
_goto_resolve_db()
{
GOTO_DB="${GOTO_DB:-$HOME/.goto}"
touch -a "$GOTO_DB"
}
_goto_usage()
{
cat <<\USAGE
usage: goto [<option>] <alias> [<directory>]
default usage:
goto <alias> - changes to the directory registered for the given alias
OPTIONS:
-r, --register: registers an alias
goto -r|--register <alias> <directory>
-u, --unregister: unregisters an alias
goto -u|--unregister <alias>
-p, --push: pushes the current directory onto the stack, then performs goto
goto -p|--push <alias>
-o, --pop: pops the top directory from the stack, then changes to that directory
goto -o|--pop
-l, --list: lists aliases
goto -l|--list
-x, --expand: expands an alias
goto -x|--expand <alias>
-c, --cleanup: cleans up non existent directory aliases
goto -c|--cleanup
-h, --help: prints this help
goto -h|--help
-v, --version: displays the version of the goto script
goto -v|--version
USAGE
}
# Displays version
_goto_version()
{
echo "goto version 1.2.4.1"
}
# Expands directory.
# Helpful for ~, ., .. paths
_goto_expand_directory()
{
builtin cd "$1" 2>/dev/null && pwd
}
# Lists registered aliases.
_goto_list_aliases()
{
local IFS=$' '
if [ -f "$GOTO_DB" ]; then
while read -r name directory; do
printf '\e[1;36m%20s \e[0m%s\n' "$name" "$directory"
done < "$GOTO_DB"
else
echo "You haven't configured any directory aliases yet."
fi
}
# Expands a registered alias.
_goto_expand_alias()
{
if [ "$#" -ne "1" ]; then
_goto_error "usage: goto -x|--expand <alias>"
return
fi
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -z "$resolved" ]; then
_goto_error "alias '$1' does not exist"
return
fi
echo "$resolved"
}
# Lists duplicate directory aliases
_goto_find_duplicate()
{
local duplicates=
duplicates=$(sed -n 's:[^ ]* '"$1"'$:&:p' "$GOTO_DB" 2>/dev/null)
echo "$duplicates"
}
# Registers and alias.
_goto_register_alias()
{
if [ "$#" -ne "2" ]; then
_goto_error "usage: goto -r|--register <alias> <directory>"
return 1
fi
if ! [[ $1 =~ ^[[:alnum:]]+[a-zA-Z0-9_-]*$ ]]; then
_goto_error "invalid alias - can start with letters or digits followed by letters, digits, hyphens or underscores"
return 1
fi
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -n "$resolved" ]; then
_goto_error "alias '$1' exists"
return 1
fi
local directory
directory=$(_goto_expand_directory "$2")
if [ -z "$directory" ]; then
_goto_error "failed to register '$1' to '$2' - can't cd to directory"
return 1
fi
local duplicate
duplicate=$(_goto_find_duplicate "$directory")
if [ -n "$duplicate" ]; then
_goto_warning "duplicate alias(es) found: \\n$duplicate"
fi
# Append entry to file.
echo "$1 $directory" >> "$GOTO_DB"
echo "Alias '$1' registered successfully."
}
# Unregisters the given alias.
_goto_unregister_alias()
{
if [ "$#" -ne "1" ]; then
_goto_error "usage: goto -u|--unregister <alias>"
return 1
fi
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -z "$resolved" ]; then
_goto_error "alias '$1' does not exist"
return 1
fi
# shellcheck disable=SC2034
local readonly GOTO_DB_TMP="$HOME/.goto_"
# Delete entry from file.
sed "/^$1 /d" "$GOTO_DB" > "$GOTO_DB_TMP" && mv "$GOTO_DB_TMP" "$GOTO_DB"
echo "Alias '$1' unregistered successfully."
}
# Pushes the current directory onto the stack, then goto
_goto_directory_push()
{
if [ "$#" -ne "1" ]; then
_goto_error "usage: goto -p|--push <alias>"
return
fi
{ pushd . || return; } 1>/dev/null 2>&1
_goto_directory "$@"
}
# Pops the top directory from the stack, then goto
_goto_directory_pop()
{
{ popd || return; } 1>/dev/null 2>&1
}
# Unregisters aliases whose directories no longer exist.
_goto_cleanup()
{
if ! [ -f "$GOTO_DB" ]; then
return
fi
while IFS= read -r i && [ -n "$i" ]; do
echo "Cleaning up: $i"
_goto_unregister_alias "$i"
done <<< "$(awk '{al=$1; $1=""; dir=substr($0,2);
system("[ ! -d \"" dir "\" ] && echo " al)}' "$GOTO_DB")"
}
# Changes to the given alias' directory
_goto_directory()
{
local target
target=$(_goto_resolve_alias "$1") || return 1
builtin cd "$target" 2> /dev/null || \
{ _goto_error "Failed to goto '$target'" && return 1; }
}
# Fetches the alias directory.
_goto_find_alias_directory()
{
local resolved
resolved=$(sed -n "s/^$1 \\(.*\\)/\\1/p" "$GOTO_DB" 2>/dev/null)
echo "$resolved"
}
# Displays the given error.
# Used for common error output.
_goto_error()
{
(>&2 echo -e "goto error: $1")
}
# Displays the given warning.
# Used for common warning output.
_goto_warning()
{
(>&2 echo -e "goto warning: $1")
}
# Displays entries with aliases starting as the given one.
_goto_print_similar()
{
local similar
similar=$(sed -n "/^$1[^ ]* .*/p" "$GOTO_DB" 2>/dev/null)
if [ -n "$similar" ]; then
(>&2 echo "Did you mean:")
(>&2 column -t <<< "$similar")
fi
}
# Fetches alias directory, errors if it doesn't exist.
_goto_resolve_alias()
{
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -z "$resolved" ]; then
_goto_error "unregistered alias $1"
_goto_print_similar "$1"
return 1
else
echo "${resolved}"
fi
}
# Completes the goto function with the available commands
_complete_goto_commands()
{
local IFS=$' \t\n'
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "-r --register -u --unregister -p --push -o --pop -l --list -x --expand -c --cleanup -v --version" -- "$1"))
}
# Completes the goto function with the available aliases
_complete_goto_aliases()
{
local IFS=$'\n' matches
_goto_resolve_db
# shellcheck disable=SC2207
matches=($(sed -n "/^$1/p" "$GOTO_DB" 2>/dev/null))
if [ "${#matches[@]}" -eq "1" ]; then
# remove the filenames attribute from the completion method
compopt +o filenames 2>/dev/null
# if you find only one alias don't append the directory
COMPREPLY=("${matches[0]// *}")
else
for i in "${!matches[@]}"; do
# remove the filenames attribute from the completion method
compopt +o filenames 2>/dev/null
if ! [[ $(uname -s) =~ Darwin* ]]; then
matches[$i]=$(printf '%*s' "-$COLUMNS" "${matches[$i]}")
COMPREPLY+=("$(compgen -W "${matches[$i]}")")
else
COMPREPLY+=("${matches[$i]// */}")
fi
done
fi
}
# Bash programmable completion for the goto function
_complete_goto_bash()
{
local cur="${COMP_WORDS[$COMP_CWORD]}" prev
if [ "$COMP_CWORD" -eq "1" ]; then
# if we are on the first argument
if [[ $cur == -* ]]; then
# and starts like a command, prompt commands
_complete_goto_commands "$cur"
else
# and doesn't start as a command, prompt aliases
_complete_goto_aliases "$cur"
fi
elif [ "$COMP_CWORD" -eq "2" ]; then
# if we are on the second argument
prev="${COMP_WORDS[1]}"
if [[ $prev = "-u" ]] || [[ $prev = "--unregister" ]]; then
# prompt with aliases if user tries to unregister one
_complete_goto_aliases "$cur"
elif [[ $prev = "-x" ]] || [[ $prev = "--expand" ]]; then
# prompt with aliases if user tries to expand one
_complete_goto_aliases "$cur"
elif [[ $prev = "-p" ]] || [[ $prev = "--push" ]]; then
# prompt with aliases only if user tries to push
_complete_goto_aliases "$cur"
fi
elif [ "$COMP_CWORD" -eq "3" ]; then
# if we are on the third argument
prev="${COMP_WORDS[1]}"
if [[ $prev = "-r" ]] || [[ $prev = "--register" ]]; then
# prompt with directories only if user tries to register an alias
local IFS=$' \t\n'
# shellcheck disable=SC2207
COMPREPLY=($(compgen -d -- "$cur"))
fi
fi
}
# Zsh programmable completion for the goto function
_complete_goto_zsh()
{
local all_aliases=()
while IFS= read -r line; do
all_aliases+=("$line")
done <<< "$(sed -e 's/ /:/g' ~/.goto 2>/dev/null)"
local state
local -a options=(
'(1)'{-r,--register}'[registers an alias]:register:->register'
'(- 1 2)'{-u,--unregister}'[unregisters an alias]:unregister:->unregister'
'(: -)'{-l,--list}'[lists aliases]'
'(*)'{-c,--cleanup}'[cleans up non existent directory aliases]'
'(1 2)'{-x,--expand}'[expands an alias]:expand:->aliases'
'(1 2)'{-p,--push}'[pushes the current directory onto the stack, then performs goto]:push:->aliases'
'(*)'{-o,--pop}'[pops the top directory from stack, then changes to that directory]'
'(: -)'{-h,--help}'[prints this help]'
'(* -)'{-v,--version}'[displays the version of the goto script]'
)
_arguments -C \
"${options[@]}" \
'1:alias:->aliases' \
'2:dir:_files' \
&& ret=0
case ${state} in
(aliases)
_describe -t aliases 'goto aliases:' all_aliases && ret=0
;;
(unregister)
_describe -t aliases 'unregister alias:' all_aliases && ret=0
;;
esac
return $ret
}
goto_aliases=($(alias | sed -n "s/.*\s\(.*\)='goto'/\1/p"))
goto_aliases+=("goto")
for i in "${goto_aliases[@]}"
do
# Register the goto completions.
if [ -n "${BASH_VERSION}" ]; then
if ! [[ $(uname -s) =~ Darwin* ]]; then
complete -o filenames -F _complete_goto_bash $i
else
complete -F _complete_goto_bash $i
fi
elif [ -n "${ZSH_VERSION}" ]; then
compdef _complete_goto_zsh $i
else
echo "Unsupported shell."
exit 1
fi
done
这最初作为 goto 的 GitHub 存储库中的 README 发布,并经许可重复使用。
评论已关闭。