目前的场景是,我在
.zshrc
中定义了一些别名,例如
alias gco='git checkout'
alias cdp='cd ..'
还有很多类似的事情。 我的问题是 每次输入别名并按 Enter 键时如何打印出命令?
例如:
$> gco master
> Command: git checkout master
> Git process ...
类似的事情,如果解决方案也适用于 bash 会更好! 谢谢!
这是一个很好的问题。我们可以通过定义几个函数来扩展别名,然后在执行函数之前使用
preexec
钩子来运行这些函数。
我从这里得到了答案。
_aliases="$(alias -Lr 2>/dev/null || alias)"
alias_for() {
[[ $1 =~ '[[:punct:]]' ]] && return
local found="$( echo "$_aliases" | sed -nE "/^alias ${1}='?(.+)/s//\\1/p" )"
[[ -n $found ]] && echo "${found%\'}"
}
首先,将所有别名存储在变量中。
alias -r
打印所有 regular
别名(不是全局或后缀),并且 alias -L
“以适合在启动脚本中使用的方式”打印它们。
alias_for()
函数进行一些清理,删除引号并将 alias
放在行前面。当我们这样做 echo ${_aliases}
时,我们会得到这样的结果:
alias history='fc -l 1'
alias ls='ls -F -G'
alias lsdf='ls -1l ~/.*(@)'
alias mv='mv -v'
将此与
alias
的输出进行比较:
history='fc -l 1'
ls='ls -F -G'
lsdf='ls -1l ~/.*(@)'
mv='mv -v'
如果输入了别名,我们现在可以检测到它,然后打印它:
expand_command_line() {
[[ $# -eq 0 ]] && return # If there's no input, return. Else...
local found_alias="$(alias_for $1)" # Check if there's an alias for the comand.
if [[ -n $found_alias ]]; then # If there was
echo ${found_alias} # Print it.
fi
}
preexec
功能非常适合此目的。它的功能是:
在命令被读取并即将执行后立即执行。如果历史机制处于活动状态(并且该行没有从历史缓冲区中丢弃),则用户键入的字符串将作为第一个参数传递,否则它是一个空字符串。将执行的实际命令(包括扩展别名)以两种不同的形式传递:第二个参数是命令的单行、大小受限版本(省略了函数体等内容);第三个参数包含正在执行的全文。
注意,我们可能只使用 preeexec 函数来显示正在运行的内容。
要将我们的函数添加到 preexec,我们使用一个钩子 使用此示例:
autoload -U add-zsh-hook # Load the zsh hook module.
add-zsh-hook preexec expand_command_line # Adds the hook
稍后要删除钩子,我们可以使用:
# add-zsh-hook -d preexec expand_command_line # Remove it for this hook.
这就是我运行时 shell 的样子:
$ 1
cd -
$ rake
bundle exec rake
^C
$ chmod
usage: chmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...
chmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -N | -i | -I] file ...
$ git lg1
fatal: Not a git repository (or any of the parent directories): .git
正如我们从我的 shell 示例中看到的,当运行没有别名的命令时(如
chmod
),完整的命令不 显示。当运行别名命令(如 1
或 rake
)时,会显示完整命令。
运行
git
别名时(例如,git lg1
),git
别名不会展开。如果你看我的第一个链接,那里的完整示例确实使用了git
别名扩展 - 如果 git 别名对你来说至关重要,你应该接受并修改。
创建脚本目录:
运行以下命令为脚本创建目录:
mkdir -p $HOME/.zsh_scripts
确保已安装 Node.js:
确保您的系统上安装了 Node.js。您可以通过运行来检查:
node -v
如果未安装 Node.js,您可以使用包管理器(如
nvm
、brew
)安装它,或直接从 Node.js 网站安装。
创建
expand_aliases.sh
:
在
.zsh_scripts
目录中,创建一个名为 expand_aliases.sh
的文件,其中包含以下内容:
# Fetch all shell aliases and store them in a variable
_aliases="$(alias)"
# Function to expand the command line by passing it to the Node.js script
expand_command_line() {
local cmd="$1"
shift
local args="$@"
# Pass aliases and command line to the Node.js script
node $HOME/.zsh_scripts/expand_aliases.js "$cmd" "$args" "$_aliases"
}
创建
expand_aliases.js
:
在同一目录中,创建一个名为
expand_aliases.js
的文件,其中包含以下内容:
#!/usr/bin/env node
// Function to parse aliases from the shell output into a dict object
function parseAliases(aliasesOutput) {
const aliasMap = {};
const aliasLines = aliasesOutput.split("\n");
aliasLines.forEach((line) => {
const match = line.match(/^([^=:#]+)=(['"]?)(.+)\2$/);
if (match) {
let [, aliasName, , aliasValue] = match;
aliasMap[aliasName.trim()] = aliasValue.trim();
}
});
return aliasMap;
}
// Function to check if a command has an alias and return the expanded alias
function expandAlias(cmd, aliasMap) {
return aliasMap[cmd] || cmd;
}
// Function to expand the first argument (command) of a command line if it has an alias
function expandCommandLine(commandLine, aliasMap) {
const parts = commandLine.split(" ");
const cmd = parts[0];
const args = parts.slice(1).join(" ");
const expandedCmd = expandAlias(cmd, aliasMap);
if (expandedCmd === cmd) return null;
let alias = expandedCmd.trim();
if (alias.startsWith("'") && alias.endsWith("'")) {
// Remove leading and trailing single quotes if its still exists after regex matching
alias = alias.slice(1, -1);
}
return `${alias} ${args}`;
}
// Main function to expand the command passed from zsh
function main() {
// The last argument is the aliases from the shell, and the first are the command and arguments
const args = process.argv.slice(2);
const aliases = args.pop(); // The last argument is the aliases string
const commandLine = args.join(" ");
if (!commandLine) {
console.error("No command provided.");
process.exit(1);
}
// Parse the aliases passed from Bash
const aliasMap = parseAliases(aliases);
// Expand the command line using the aliases
const expanded = expandCommandLine(commandLine, aliasMap);
// Output the expanded command to be executed in the shell
if (expanded)
console.log(">", expanded);
}
main();
更新
.zshrc
:
将以下行添加到您的
.zshrc
文件中以获取脚本源并设置 preexec
函数:
source $HOME/.zsh_scripts/expand_aliases.sh
preexec() {
expand_command_line $1
}
重新加载您的外壳:
进行这些更改后,通过运行重新加载 shell 配置:
source ~/.zshrc
此设置将允许您在终端中执行命令时使用 Node.js 脚本自动扩展 shell 别名。
❯ ls
> eza-wrapper.sh
❯ ls -la
> eza-wrapper.sh -la
❯ noalias
noalias: command not found
❯ grep
> grep --color=auto --exclude-dir={.bzr,CVS,.git,.hg,.svn,.idea,.tox,.venv,venv}
Usage: grep [OPTION]... PATTERNS [FILE]...
Try 'grep --help' for more information.
❯ , apt update
> sudo apt update
感谢@simont的鼓舞人心的回答。