Zsh 使用 sed 自动完成功能

Zsh 使用 sed 自动完成功能

我正在努力编写 ZSH 自动完成功能。我的目标是使用和中列出的项目自动完成ta和的第一个参数。tktmux list-sessionstmuxinator list

以下是我目前所掌握的信息:

tmux list-sessions输出如下数据:

dotfiles: 1 windows (created Tue Apr 15 21:54:51 2014) [123x48]
goodbye: 1 windows (created Tue Apr 15 21:51:34 2014) [123x48]
hello: 1 windows (created Tue Apr 15 21:42:03 2014) [123x48]

tmuxinator list输出:

tmuxinator projects:
dotfiles           landonschropp.com

这是我的尝试:

tmux-list-sessions-autofill() {

  # get the tmux and tmuxinator sessions
  TMUX_SESSIONS=$( tmux list-sessions | cut -d: -f1 )
  TMUXINATOR_SESSIONS=$( tmuxinator list | tail -n +2 | gsed -e 's/\s\+/\n/g' )

  # remove duplicates
  SESSIONS=$( echo "$TMUX_SESSIONS\n$TMUXINATOR_SESSIONS" | sort | uniq )

  # set the autocomplete values
  reply=( $(echo $SESSIONS) )
}

compctl -K tmux-list-sessions-autofill ta
compctl -K tmux-list-sessions-autofill tk
  • tmux list-sessions | cut -d: -f1删除除项目名称之外的所有内容。
  • tmuxinator list | tail -n +2 | gsed -e 's/\s\+/\n/g'删除第一行并用换行符替换空格。
  • echo "$TMUX_SESSIONS\n$TMUXINATOR_SESSIONS" | sort | uniq删除重复的行。
  • 我使用它是gsed因为我在用 OS X,而且sed有点不稳定。

我的功能可以运行,但是真的很慢。我是 shell 脚本新手,所以我确信有更有效的方法来实现这一点。我的瓶颈在哪里?我该如何解决它?

答案1

  1. 要追踪延迟,您可以使用内置命令time,它可以列出管道中所有命令的贡献:

    zsh% time sleep 2 | date | sleep 1
    sleep 2  0.00s user 0.00s system 0% cpu 2.003 total
    date  0.00s user 0.00s system 0% cpu 0.004 total
    sleep 1  0.00s user 0.00s system 0% cpu 1.004 total
    
  2. 虽然(根据您的评论)是外部命令tmuxinator list 导致了明显的延迟,但您仍然可以优化完成功能。您使用了很多外部命令(sed,,cut...)来解析输出。在这里,我将展示一种仅使用 zsh 内置功能的方法。

    tmux-list-sessions-autofill() {
      local -aU SESSIONS
      SESSIONS=("${(@)${(f)$(tmux list-sessions)}/:*/}")
      SESSIONS+=("${(@)${(f)$(tmuxinator list-sessions)}[2,-1]/[ $'\t']*/}")
      reply=(${(i)SESSIONS})
    }
    

    乍一看这可能看起来很复杂,但是一旦你习惯了,zsh 的参数扩展就是非常功能强大。从上到下:

    • local -aU声明一个本地参数SESSIONS,它是一个数组(a)和U 保持数组值唯一+
    • 首先,获取sessions tmux list-sessions,从内到外进行解释:
      • $(tmux list-sessions)获取外部命令的输出
      • ${(f)$(tmux list-sessions)}将结果拆分为\n
      • ${(@)${(f)$(tmux list-sessions)}/:*/}对每个数组元素((@))进行操作并执行搜索和替换/from/to,即删除第一个冒号之后的所有内容。
      • 现在,$SESSIONS将包括(dotfiles goodbye hello)。用于print -l $SESSIONS 在一行上打印每个数组元素。
    • 接下来,对 执行几乎相同的程序tmuxinator list,但添加了以下内容:
      • 仅使用倒数第二个元素 ( [2,-1])
      • space删除 a或tab( $'\t') 字符之后(包括)的所有内容
      • 将这些元素(+=)添加到SESSIONS数组中
    • 最后,返回SESSIONS不区分大小写的数组(i)

所有这些扩展都使用更长的通用形式,${parameter}而不是简写符号$parameterman zshexpn有关语法和所谓的参数扩展标志(喜欢(f))。


+根据巴特·谢弗U标志是不必要的,因为完成代码使其内部的完成列表唯一,因此当仅使用它们进行完成时,提前从数组中删除冗余条目并没有真正的帮助。

答案2

这是另一种选择:)

tmux-list-sessions-autofill() {
    reply=( `tmux list-sessions | sed "s/:.*//g"` );
}

===== 编辑 =====

因此,我在这里重新表述我的答案,以回应@NetworkKingPin 的评论。

在@LandonSchropp 的原始帖子中,您应该将您的tmux-list-sessions-autofill()功能更改为以下内容:

tmux-list-sessions-autofill() {
    reply=( `tmux list-sessions -F '#{session_name}'` ); 
}

请注意,在我的原始帖子中,我使用sed删除除会话名称之外的任何其他信息。但这次我使用了一个-F参数来tmux告诉仅有的打印会话名称,并跳过任何其他信息。

这对我来说很有用。希望这足够清楚了。

相关内容