Comprobando si existe un subdirectorio usando glob en el script

Comprobando si existe un subdirectorio usando glob en el script

Estoy intentando comprobar si binhay 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 findporque 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 zshguió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 Ngarantiza que el resultado sea una matriz vacía en lugar de un error si no hay coincidencia. Los calificadores globales ny Onordenan 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: 10está 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 zshen bash, primero debe averiguar qué shell se está ejecutando actualmente. Eso le dirá si el primer índice de una matriz es 0o 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/*/bincadena en $ruby_gem_homeuna 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 multiosestá 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 Ncalificador 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 nse enciende numericglobsorty On oordena a la inversa por name, así $1contendrá el que tenga el número más alto.


¹ Como se explica eninfo zsh 'Conditional Expressions', también hay un caso especial cuando la extendedglobopción está activada, en cuyo caso puede usar globos dentro [[ ... ]], generalmente con los operadores -n/ . -zPor lo tanto, podría utilizar [[ -n ~/.gem/ruby/*/bin(#qN-/) ]]para comprobar si hay al menos un directorio de este tipo aquí.

información relacionada