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:
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
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)"`