Ejecutando Bash desde Ruby con paréntesis: token inesperado cerca de '('

Intento escribir un script de Ruby que haga algunos commands simples de git. Quiero enumerar todos los cambios de files que no están en el directory de mi aplicación.

Ejecutando lo siguiente para get todos los cambios en la terminal:

git ls-files -m 

Rendimientos:

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

Ejecutando con una exception de comodín en la terminal:

 git ls-files -m !(app) 

Rendimientos:

 config/environment.rb 

Haciendo lo mismo en Ruby usando:

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

Rendimientos:

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

Intenté agregar dobles escapes:

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

No produce ningún error, sino un resultado vacío.

También intenté usar Shellwords:

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

No produce ningún error, sino un resultado vacío.

Usar la syntax %x[] comporta exactamente igual que los backticks, pero parece que no funciona como me gustaría.

¿Alguna idea de cómo get el mismo resultado al ejecutar git ls-files -m !(app) en Ruby?

!(app) es un glob extendido bash. Entonces hay dos problemas:

  1. ruby usa sh para ejecutar commands del sistema, no bash . (Puede ver eso en el post de error, que dice proceder de sh: ) Sin embargo, como su sh está implementado por bash , tiene bash disponible, con un pequeño problema: cuando bash se ejecuta como sh , desactiva un muchas características específicas de bash

  2. Incluso si fuera bash , es muy probable que shopt -s extglob no se haya ejecutado, lo cual es necesario para que !(app) y otros globs extendidos funcionen. Los globs extendidos no están habilitados de manera pnetworkingeterminada, pero algunas distribuciones (ubuntu, por ejemplo) los habilitan para implementar completuras de tabulación, por lo que probablemente los haya habilitado en su caparazón interactivo. Sin embargo, dado que ruby ​​no está invocando un shell interactivo, es bastante probable que el file de inicio de bash con el command shopt -s extglob no se haya ejecutado.

Nada de eso hace que sea imposible usar el command a través de ruby, pero ciertamente complica la vida. Por ejemplo, una posible solución alternativa sería habilitar BASHOPTS y forzar bash para que se ejecute:

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

que podría ser más complicado que la solución alternativa de filtrar los nombres de directory no deseados usando grep -v .

Establecer BASHOPTS usando env solo funcionará realmente si usted /bin/sh es realmente bash , por lo que probablemente sería mejor shopt directamente en el command ejecutado. Eso es directo, pero hay una pequeña arruga; necesita introducir una nueva línea entre el command shopt y el uso de glob extendido, porque bash tokenizes ordera una línea a la vez (y si extglob no está configurado cuando tokenizes, el ( es un metacarácter).

Esto parece funcionar:

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