【问题标题】:Bash: Getting a command's completion output programmatically (e.g., in a variable)Bash:以编程方式获取命令的完成输出(例如,在变量中)
【发布时间】:2014-07-01 15:32:46
【问题描述】:

如何以编程方式完成另一个命令?

假设我的当前目录有文件a1a2a3,那么我怎样才能让我的命令调用ls a的自动补全来取回a1 a2 a3

这可能吗?


说明和理由

我选择了ls,因为人们可以与它产生共鸣。这是一个人为的例子,但让我试着举例说明这个功能的价值。我有一个名为build 的命令,给定一个目录,它可以自动完成可以在该目录中构建的目标。这些目标可能与该目录中的文件不对应。目标可能是由我不想解析的构建文件中的build 命令挖掘的。换句话说:

build path/to/dir/TABTAB

可能会给:

path/to/dir/a_target
path/to/dir/b_target

build 是一个预先存在的命令,我不能继续修改以适应我的目的。而且它提取目标的方式是我当然不想知道的。

现在假设我有一个完整的 buildable 项目存储库,我的大部分工作以及我的大部分 build 工作都只发生在一个项目中。也就是说,我总是buildmy/project/directory下的某个目标。

到目前为止一切顺利。

所以我想为构建命令编写一个包装器,不需要我每次运行它时都为其提供目录路径。我希望它知道我的首选项目目录(或多个目录,为什么不),让我在不限定它们的情况下引用目标:

所以假设我有:

my/project/directory/a_target
my/project/directoryb_target

我想要这个:

mybuildTABTAB

给我:

a_target
b_target

当然我需要为mybuild编写完成代码,但我希望它依赖于build的完成,因为我不能只去要求build的开发人员编写一个@ 987654342@ 命令只是为了让我开心。该功能已经存在,在 build 的完成中,我需要利用它。

(当然,当我运行mybuild a_target时,它会知道运行build my/project/directory/a_target,并且我知道如何实现并且不在这个问题的范围内。)

我希望这能说明为什么我需要利用 build 命令的完成并将其用作黑匣子。

【问题讨论】:

  • 为什么你认为你需要自动化shell的这个交互特性?你想做什么? (ls a* 将为您提供以“a”开头的所有文件的列表。)
  • 查看我新添加的理由。
  • 您可以在bash 中为mybuild 定义自己的补全,这样制表符补全只会建议存在于某个环境变量指定的目录中的文件。我建议阅读bash 手册页中的可编程完成(这不是最清晰的文档,但请尝试一下),并在您第一次尝试编码后再次询问。
  • build 命令的完成不适用于文件。它从一些 make 文件中提取构建目标。我将定义我自己的完成,但它需要使用build 的完成来知道要呈现给我的目标。

标签: bash shell tab-completion


【解决方案1】:

这有点奇怪,您需要执行的命令取决于目录中的文件数量 - 无、一个或多个。但此命令适用于示例案例:

echo echo a$'\t'$'\t' | bash -i 2>&1 | head -3 | tail -1

自动完成的命令是

echo a

所以把它作为一个字符流,后跟两个制表符,发送到一个交互式 bash shell 中。 bashstderr 上生成自动完成输出,因此将其重定向到stdout 并通过headtail 管道从整个输出中选择一行。在这种情况下,这会产生单行输出

a1  a2  a3 

但是,正如其他人所说,只是使用

echo a*

可能会更容易!

【讨论】:

  • 太酷了!你也可以说echo l$'\t\ty' | bash -i 来获取以l 开头的命令列表。我不确定这有什么用......但是:D
  • 在我最初的测试中它没有工作,但我会坐下来看看我是否可以让它工作。问题不在于完成文件。这是关于完成任何事情。这就是为什么我需要利用给定命令的完成。我的特定命令没有完成文件——它完成了从具有复杂结构的清单文件中提取的构建目标,(a) 我无法在 shell 脚本中合理解析,并且 (b) 我不想解析一个 shell 脚本,因为我不想重新发明轮子。
  • 嗯,它确实有效,但有几点需要注意:1. 我应该抑制 PS1(也许用特殊的 bashrc 启动 bash 就可以了),以及 2. shell 也不应该尝试执行命令(也许如果我可以发送 Ctrl+C - 在初始测试中不起作用)。
  • 我可以使用| PS1= bash -i 抑制 PS1,但我还没有运气阻止命令执行。
  • 我正在 iMac 上尝试这个,已经完成了“brew install bash_completion”,所以你的结果可能会有所不同。但是,我只能在登录 shell 中完成命令完成(而不是文件名扩展),而不是我的子 shell。如果我可以让它在 subshel​​l 中工作,我可以试试 'printf "PATH= brew ins\t" | bash -i' 作为一种无需执行即可完成命令的方式。当然我可以停止命令执行,但我不能同时扩展命令。
【解决方案2】:

Bash 有类似的东西叫做 globbing。

例如,在您的情况下,您可以运行命令

echo a*

这会产生:

a1 a2 a3

这在文件名中有空格的情况下非常有用

for i in a*
do
   echo $i
done

它适用于a1a 1

【讨论】:

  • 问题是特定于命令的完成是由某人编码的,并且在某处有代码可以完成。我想利用该代码。请参阅我在这个问题中的说明。
【解决方案3】:

自动完成是您的 shell(例如 bash)提供的一项功能。 shell 将尝试根据您尝试运行的命令的上下文和环境提供自动完成建议。例如。它知道某些命令将对文件起作用,并且可以根据文件路径提供一些自动完成功能。但是命令本身并不知道它所使用的参数是如何被指定的,无论是由用户还是在自动完成的帮助下。

【讨论】:

    猜你喜欢
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 2012-08-30
    • 1970-01-01
    相关资源
    最近更新 更多