代码之家  ›  专栏  ›  技术社区  ›  Zoltan

如何绑定一个键来运行命令并将其输出插入bash命令行?

  •  1
  • Zoltan  · 技术社区  · 3 年前

    有各种命令行实用程序可以交互选择文件,然后将用户的选择打印到stdout,例如 fzf ranger 。例如,此功能允许运行 cat "$(fzf)" 命令,首先选择一个文件,然后显示其内容。但是,这样做的缺点是,命令以未展开的形式存储在命令历史记录中,因此无法查看选择了什么文件或再次运行它。此外,没有办法仔细检查将要运行的确切命令,这对于多个参数尤其有益,例如 cp "$(fzf)" "$(fzf)"

    为了解决这个问题, 我想绑定一个热键来打开文件选择器,并将选定的文件名直接插入正在编辑的命令行


    这是我能想到的最好的(这里我用 date 而不是 fzf ranger 因为不需要安装,所以每个人都可以尝试):

    bind -x '"\e\C-s":"selection=$(date)"'
    bind '"\e\C-e": shell-expand-line'
    bind '"\ef":" \"$selection\"\e\C-s\e\C-e"'
    

    示例用法:Type echo 然后按Alt-F。命令行变为 echo Sun Jul 30 19:55:16 CEST 2023

    说明:

    • 第一行为shell命令创建绑定,该命令设置名为 selection 到命令打印的值 日期 (或 fzf 护林员 )。
    • 第二行为执行shell扩展的readline函数创建绑定,用其值替换变量。(顺便说一句,这是一个默认绑定,我只是为了完整起见才添加它。)
    • 第三行为键入字符串的宏创建绑定 "$selection" 进入命令行,然后按下前两个绑定的热键,从而为 选择 变量,并将其替换为该值。

    我不喜欢这个解决方案的地方:

    • 需要三个单独的绑定,因为单个绑定只能包含shell命令、readline命令或keypress宏。

    • 如果一个名为 选择 被用户使用时,它会被覆盖。(这可以通过选择一个不太频繁的名称来缓解。不过,除非我再添加一个绑定来取消设置,否则它仍然会污染变量空间。)

    • shell扩展应用于整个命令行,而不仅仅是 $selection 变量引用。

    • 然而,最严重的问题是插入的文件名没有正确转义,因此如果它包含特殊字符,则命令行将不正确,并具有潜在的危险性。(在上面的绑定中,我附上了 $选择 引号中的变量引用。我希望这将导致 echo "Sun Jul 30 19:55:16 CEST 2023" echo Sun\ Jul\ 30\ 19:55:16\ CEST\ 2023 ,但事实并非如此。)

    有更好的方法吗?

    1 回复  |  直到 3 年前
        1
  •  3
  •   Freeman    3 年前

    我认为,与其使用多个绑定和一个shell变量,不如使用自定义shell函数,因此首先在shell配置文件中定义一个shell函数 ~/.bashrc !

    pick_file() {
      local file
      file=$(fzf --preview "cat {}")
      if [ -n "$file" ]; then
        file=$(printf "%q" "$file")  #for escape special characters in the filename
        READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$file${READLINE_LINE:$READLINE_POINT}"
        READLINE_POINT=$((READLINE_POINT + ${#file}))
      fi
    }
    

    然后将热键绑定到 pick_file 像这样:

    bind -x '"\C-x\C-f": pick_file'
    

    重新加载shell配置,如下所示:

    source ~/.bashrc
    

    现在,轻松按下指定的热键( Ctrl+X Ctrl+F )打开文件选择器,选择文件后,它将插入命令行中的当前光标位置!

    推荐文章