如何将 zsh 别名函数传递给管道

如何将 zsh 别名函数传递给管道

我有一个 zsh 别名:

gitbs() {
    git branch | grep -- $1
}

我想将结果传递到 git checkout 中,例如: git checkout | gitbs state 我怎样才能使这个工作?

答案1

shell 管道将一个命令的输出传递到另一个命令的输入。这在这里对您没有帮助:您想要将命令的输出作为另一个命令的命令行参数传递。这样做的工具是命令替换。所以基本的想法是

git checkout "$(gitbs state)"

(它仍然是一个管道,但管道的读取端是 shell 本身:它读取输出,然后构造一个包含该输出的命令行。)

但是, 的输出gitbs state不是传递给 的正确格式git checkout:它在与分支名称相同的行上有额外的空格,有时还有标点符号。 (还有颜色格式化代码,但仅当输出是终端或 git 自动调用寻呼机时,而不是当输出是管道时。)另外,如果没有匹配的分支或多个分支,您会得到一些来自 的奇怪错误消息git checkout

要解决此问题,您可以更改gitbs以生成原始分支名称作为输出。这是一个版本,如果输出是终端,则保留适合人类的漂亮格式,否则仅每行打印一个分支名称。它用git for-each-ref枚举分支名称。这条件表达式 -t 1测试标准输出是否是终端。

gitbs () {
  if [[ -t 1 ]]; then
    git branch
  else
    git for-each-ref --format='%(refname:lstrip=2)' 'refs/heads/*'
  fi | grep -- "$1"
}

有了这个定义gitbsgit checkout "$(gitbs state)"就可以了。

请注意命令替换周围的双引号。如果没有双引号 ( git checkout $(gitbs state)),输出会在空格处分割成单独的参数,因此如果多个分支匹配,生成的命令将类似于git checkout foobar1 foobar2,它不会签出,而是会使用if中的foobar1版本覆盖文件的当前foobar2版本foobar1一个名为的文件foobar2存在。

为了避免这个陷阱,最好定义一个gitbs需要单个匹配分支的不同版本。如果有零个或多个匹配分支,您将获得更清晰的错误消息,尽管仍然有一条关于当前分支的额外消息git checkout。该函数将匹配分支的列表放入大批

gitbs1 () {
  local branches
  branches=($(git for-each-ref --format='%(refname:lstrip=2)' 'refs/heads/*' | grep "$1"))
  if ((#branches == 0)); then
    echo "No branch contains '$1'" >&2
    return 3
  fi
  if ((#branches > 1)); then
    echo "Multiple branches match '$1':" >&2
    print -lr $branches >&2
    return 3
  fi
  echo $branches
}

然后就可以放心地写了git checkout $(gitbs1 state)


如果您打开该选项glob_complete(即setopt glob_complete在您的.zshrc)中,然后您可以输入

git 分支 *foo*Tab

*foo*如果有的话,将被替换为匹配分支的名称。如果有多个匹配分支,您将获得与普通(前缀)完成相同类型的菜单或循环行为。

答案2

这不是别名,这是一个函数。

如果您想将命令输出中的单词作为参数传递给另一个命令,这就是命令替换的用途:

git checkout $(gitbs state)

将捕获 的输出gitbs state(这里,无论是函数、别名还是外部命令都无关紧要),删除尾随的换行符,分割 的字符,$IFS并将生成的单词作为单独的参数传递给git checkout¹

仅传递该拆分结果的第一个单词:

git checkout ${$(gitbs state)[1]}

将整个输出(减去尾随换行符)传递为的参数git checkout,引用它:

git checkout "$(gitbs state)"

要按任意字符串而不是$IFS字符进行拆分,请使用s参数扩展标志(或f按换行符进行拆分):

git checkout ${(f)"$(gitbs state)"}

(请注意,空元素将被删除)。

xargs命令可用于此目的,但请注意,拆分是以xargs非常特殊的方式完成的,并且xargs只能运行外部命令(不是 shell 内置命令或函数)。

gitbs state | xargs git checkout

xargs 如果输出大量单词(超过一个命令行的容纳范围),则可能会运行git checkout多次。gitbs state


1 在其他类似 Bourne 的 shell 中,您需要禁用通配符 ( set -o noglob) 以防止对结果单词执行该操作。这不适用于zsh除非处于 sh/ksh 模拟模式

相关内容