Estoy intentando comprobar si bin
hay un directorio dentro de un directorio que a veces puede cambiar. En este caso particular, el número de versión de Ruby puede cambiar (por ejemplo $HOME/.gem/ruby/2.6.0/bin
). Esto es lo que hice hasta ahora:
#!/usr/bin/env zsh
ruby_gem_home="$HOME/.gem/ruby/*/bin"
if [[ -d $ruby_gem_home ]]; then
echo "The ruby gems directory exists!"
else
echo "Ruby gems directory missing!"
fi
No quiero usarlo find
porque es parte de un proceso de inicio de sesión. ¿Cuál es la forma más elegante, utilizando los comandos zsh/bash integrados, para lograr esto?
¡Gracias!
EDITAR:Olvidé mencionar que esto es para un zsh
guión.
Respuesta1
Para este caso específicamente
No creo que tenga sentido buscar ningún directorio. ¿Cuál es el punto de utilizar un directorio que no coincide con la versión instalada de Ruby? ¿Y si el usuario ha configurado un directorio diferente en ~/.gemrc
?
No estoy muy familiarizado con Ruby, pero creo que la solución correcta es tomar la primera entrada en gempath
.
ruby_gem_home=$(gem environment gempath | sed 's/:.*//')
if [ -z "$ruby_gem_home" ]; then unset ruby_gem_home; fi
en zsh
function set_ruby_gem_home {
emulate -LR zsh
local dirs
dirs=($HOME/.gem/ruby/*/bin(NnOn))
if (($#dirs == 0)); then
unset ruby_gem_home
else
ruby_gem_home=$dirs[0]
fi
}
Elcalificador global N
garantiza que el resultado sea una matriz vacía en lugar de un error si no hay coincidencia. Los calificadores globales n
y On
ordenan la lista disminuyendo los números de versión; modifique esto para elegir un elemento diferente cuando haya varias coincidencias.
En fiesta:
function set_ruby_gem_home {
local dirs
dirs=(~/.gem/ruby/*/bin(NnOn))
if (($#dirs == 0)); then
unset ruby_gem_home
else
ruby_gem_home=$dirs[0]
fi
}
en fiesta
function set_ruby_gem_home {
local dirs restore_options
restore_options=$(setopt +o)
shopt -s nullglob
dirs=(~/.gem/ruby/*/bin)
if (($#dirs == 0)); then
unset ruby_gem_home
else
ruby_gem_home=${dirs[${#dirs[@]}-1]}
fi
eval "$restore_options"
}
Aquí tomo el último elemento de la matriz, que a menudo, aunque no siempre, es la versión más reciente: 10
está ordenado antes 9
.
Respuesta2
Utilice una matriz y compruebe si el primer elemento (es decir, [0] en bash, [1] en zsh) de la matriz es un directorio. por ejemplo en bash
:
# the trailing slash in "$HOME"/.gem/ruby/*/bin/ ensures that
# the glob matches only directories.
rubygemdirs=( "$HOME"/.gem/ruby/*/bin/ )
if [ -d "${rubygemdirs[0]}" ] ; then
echo "At least one ruby gem dir exists"
else
echo "No ruby gem dirs were found"
fi
Para trabajar tanto en como zsh
en bash
, primero debe averiguar qué shell se está ejecutando actualmente. Eso le dirá si el primer índice de una matriz es 0
o 1
. También determinará si necesita usar (N)
con glob para decirle a zsh que NO salga si no hay coincidencias para el glob (zsh saldrá de forma predeterminada en caso de errores de coincidencia de glob).
(En este punto, debería empezar a ser obvio que escribir un guión para que funcione de manera confiable enambosbash y zsh probablemente causarán más problemas de los que valen la pena)
if [ -n "$BASH_VERSION" ] ; then
first_idx=0
rubygemdirs=( "$HOME"/.gem/ruby/*/bin/ )
elif [ -n "$ZSH_VERSION" ] ; then
first_idx=1
emulate sh
rubygemdirs=( "$HOME"/.gem/ruby/*/bin/ )
emulate zsh
fi
if [ -d "${rubygemdirs[$first_idx]}" ] ; then
...
Si es necesario, también puede iterar sobre la matriz para comprobar si un elemento coincide con una versión particular. p.ej
for d in "${rubygemdirs[@]}" ; do
[[ $d =~ /2\.6\.0/ ]] && echo found gem dir for ruby 2.6.0
done
Nota: las /
s en la expresión regular son barras diagonales literales. No marcan el principio y el final de la expresión regular como en sed
.
Respuesta3
Los comodines sólo se expanden en contextos de lista, es decir, donde se pueden expandir a varias cosas. En una asignación a una variable escalar, no se pueden expandir ya que una variable escalar solo puede contener un valor.
Con ruby_gem_home="$HOME/.gem/ruby/*/bin"
, simplemente estaría almacenando la /home/user/.gem/ruby/*/bin
cadena en $ruby_gem_home
una variable escalar, no la lista de archivos a los que global se expandiría si estuviera en el contexto de la lista.
Los contextos de lista en zsh pueden ser argumentos para comandos simples o funciones anónimas, después de for/select x in
, en los objetivos de las redirecciones (cuando multios
está activado), en asignaciones de matrices o matrices asociativas y algunos otros casos extremos¹.
Entonces, aquí podrías usar una función anónima:
() {
unset ruby_gem_home
case $# in
(0) print -u2 "Ruby gems directory missing!";;
(1) print -u2 "The ruby gems directory exists!"
ruby_gem_home=$1;;
(*) print -u2 "More than one ruby gem directory, don't know which to choose!";;
esac
} ~/.gem/ruby/*/bin(N-/)
Aquí también se agrega el N
calificador glob para que el glob se expanda a una lista vacía cuando no hay coincidencias en lugar de informar un error, y -/
para restringir a archivos de tipodirectorio(determinado después de la resolución del enlace simbólico con -
).
Si hay más de un directorio de Ruby Gem y desea elegir el que tenga el número de versión más alto, puede hacer lo siguiente:
() {
unset ruby_gem_home
case $# in
(0) print -u2 "Ruby gems directory missing!";;
(1) print -u2 "The ruby gems directory exists!"
ruby_gem_home=$1;;
(*) print -ru2 "More than one ruby gem directory, picking $1!"
ruby_gem_home=$1;;
esac
} ~/.gem/ruby/*/bin(N-/nOn)
Donde n
se enciende numericglobsort
y On
o
ordena a la inversa por n
ame, así $1
contendrá el que tenga el número más alto.
¹ Como se explica eninfo zsh 'Conditional Expressions'
, también hay un caso especial cuando la extendedglob
opción está activada, en cuyo caso puede usar globos dentro [[ ... ]]
, generalmente con los operadores -n
/ . -z
Por lo tanto, podría utilizar [[ -n ~/.gem/ruby/*/bin(#qN-/) ]]
para comprobar si hay al menos un directorio de este tipo aquí.