在 zsh 或 bash 中打印执行的别名

问题描述 投票:0回答:2

目前的场景是,我在

.zshrc
中定义了一些别名,例如

alias gco='git checkout'
alias cdp='cd ..'

还有很多类似的事情。 我的问题是 每次输入别名并按 Enter 键时如何打印出命令?

例如:

$> gco master
> Command: git checkout master
> Git process ...

类似的事情,如果解决方案也适用于 bash 会更好! 谢谢!

alias zsh zshrc
2个回答
17
投票

这是一个很好的问题。我们可以通过定义几个函数来扩展别名,然后在执行函数之前使用

preexec
钩子来运行这些函数。

我从这里得到了答案。


1.评估所有别名

_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'

2.检查是否输入了别名的函数。

如果输入了别名,我们现在可以检测到它,然后打印它:

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
}

3.每次输入命令时都运行它

preexec
功能非常适合此目的。它的功能是:

在命令被读取并即将执行后立即执行。如果历史机制处于活动状态(并且该行没有从历史缓冲区中丢弃),则用户键入的字符串将作为第一个参数传递,否则它是一个空字符串。将执行的实际命令(包括扩展别名)以两种不同的形式传递:第二个参数是命令的单行、大小受限版本(省略了函数体等内容);第三个参数包含正在执行的全文。

来自 zsh 手册,第 9 章

注意,我们可能只使用 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 别名对你来说至关重要,你应该接受并修改。


0
投票
  1. 创建脚本目录:

    运行以下命令为脚本创建目录:

    mkdir -p $HOME/.zsh_scripts
    
  2. 确保已安装 Node.js:

    确保您的系统上安装了 Node.js。您可以通过运行来检查:

    node -v
    

    如果未安装 Node.js,您可以使用包管理器(如

    nvm
    brew
    )安装它,或直接从 Node.js 网站安装。

  3. 创建

    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"
    }
    
  4. 创建

    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();
    
  5. 更新

    .zshrc

    将以下行添加到您的

    .zshrc
    文件中以获取脚本源并设置
    preexec
    函数:

    source $HOME/.zsh_scripts/expand_aliases.sh
    preexec() {
      expand_command_line $1
    }
    
  6. 重新加载您的外壳:

    进行这些更改后,通过运行重新加载 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的鼓舞人心的回答。

© www.soinside.com 2019 - 2024. All rights reserved.