运行 Bash from Ruby with parentheses: unexpected token near '('

Running Bash from Ruby with parentheses: unexpected token near '('

我正在尝试编写一个 Ruby 脚本来执行一些简单的 git 命令。我想列出所有不在我的应用程序目录中的文件更改。

运行 以下获取终端中的所有更改:

git ls-files -m

产量:

config/environment.rb
app/models/site.rb

运行 终端中出现通配符异常:

git ls-files -m !(app)

产量:

config/environment.rb

在 Ruby 中使用:

做同样的事情
`git ls-files -m !(app)`

产量:

sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `git ls-files -m !(app)'

我试过添加双转义:

`git ls-files -m \!\(app\)`

没有产生错误,但结果为空。

我也尝试过使用 Shellwords:

require 'shellwords'
`git ls-files -m #{Shellwords.escape("!(app)")}`

没有产生错误,但结果为空。

使用 %x[] 语法的行为也与反引号完全一样,但似乎并不像我希望的那样工作。

关于如何从 运行 git ls-files -m !(app) 在 Ruby 中获得相同结果的任何想法?

!(app) 是一个 bash 扩展 glob。所以有两个问题:

  1. ruby 使用 sh 来执行系统命令,而不是 bash。 (您可以在声称来自 sh: 的错误消息中看到这一点。)但是,由于您的 sh 是由 bash 实现的,您确实有 bash 可用,有一个小问题:当 bash 被 运行 宁为 sh 时,它会禁用很多 bash 特定的功能。

  2. 即使是bash,也很有可能shopt -s extglob没有被执行,这是!(app)和其他扩展globs工作所必需的.默认情况下不启用扩展 glob,但某些发行版(例如 ubuntu)启用它们以实现制表符完成,因此您可能在交互式 shell 中启用了它们。但是,由于 ruby 没有调用交互式 shell,很可能 bash 带有 shopt -s extglob 命令的启动文件没有被执行。

None 使得无法通过 ruby 使用命令,但这确实使生活复杂化。例如,一种可能的解决方法是启用 BASHOPTS 并强制 bash 到 运行:

`env BASHOPTS=$BASHOPTS:extglob bash -c 'git ls-files -m !(app)'`

这可能比使用 grep -v.

过滤不需要的目录名称的替代解决方案更复杂

使用 env 设置 BASHOPTS 实际上只有在 /bin/sh 确实是 bash 时才有效,所以 shopt 可能会更好直接在执行的命令中。这是直截了当的,但有一点小问题;您需要在 shopt 命令和扩展 glob 的使用之间引入一个换行符,因为 bash 一次标记一行命令(如果在标记化时未设置 extglob,则( 是元字符。)

这似乎有效:

`bash -c "shopt -s extglob\ngit ls-files -m !(app)"`