Verificando se existe um subdiretório usando glob no script

Verificando se existe um subdiretório usando glob no script

Estou tentando verificar se um diretório binestá dentro de um diretório que às vezes pode mudar. Neste caso particular, o número da versão do Ruby pode mudar (por exemplo $HOME/.gem/ruby/2.6.0/bin). Aqui está o que eu fiz até agora:

#!/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

Não quero usar findporque isso faz parte de um processo de login. Qual é a maneira mais elegante, usando comandos zsh/bash integrados, para conseguir isso?

Obrigado!

EDITAR:Esqueci de mencionar que isso é para um zshscript.

Responder1

Para este caso especificamente

Não acho que faça sentido procurar nenhum diretório. Qual é o sentido de usar um diretório que não corresponde à versão instalada do Ruby? E se o usuário tiver configurado um diretório diferente em ~/.gemrc?

Não estou muito familiarizado com Ruby, mas acho que a solução correta é pegar a primeira entrada no gempath.

ruby_gem_home=$(gem environment gempath | sed 's/:.*//')
if [ -z "$ruby_gem_home" ]; then unset ruby_gem_home; fi

Em 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
}

Oqualificador global Ngarante que o resultado seja uma matriz vazia em vez de um erro se não houver correspondência. Os qualificadores glob ne Onclassificam a lista diminuindo os números de versão; ajuste isso para escolher um item diferente quando houver várias correspondências.

Na festa:

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
}

Na festa

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"
}

Aqui pego o último elemento da matriz, que muitas vezes, mas nem sempre, é a versão mais recente: 10é classificado antes de 9.

Responder2

Use um array e verifique se o primeiro elemento (ou seja, [0] no bash, [1] no zsh) do array é um diretório. por exemplo, em 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 trabalhar em ambos zshe bash, você precisa primeiro descobrir qual shell está em execução no momento. Isso lhe dirá se o primeiro índice de um array é 0ou 1. Ele também determinará se você precisa usar (N)o glob para dizer ao zsh para NÃO sair se não houver correspondências para o glob (o zsh será encerrado por padrão em caso de erros de correspondência do glob).

(neste ponto, deve estar começando a ficar óbvio que escrever um script para funcionar de forma confiável emambosbash e zsh provavelmente serão mais problemáticos do que valem)

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
...

Se necessário, você também pode iterar na matriz para verificar se um elemento corresponde a uma versão específica. por exemplo

for d in "${rubygemdirs[@]}" ; do
  [[ $d =~ /2\.6\.0/ ]] && echo found gem dir for ruby 2.6.0
done

Nota: os /s no regex são barras literais. Eles não marcam o início e o fim da regex como em sed.

Responder3

Os curingas só são expandidos em contextos de lista, ou seja, onde podem ser expandidos para diversas coisas. Em uma atribuição a uma variável escalar, eles não podem ser expandidos, pois uma variável escalar só pode conter um valor.

Com ruby_gem_home="$HOME/.gem/ruby/*/bin", você apenas armazenaria a /home/user/.gem/ruby/*/binstring em $ruby_gem_homeuma variável escalar, não na lista de arquivos para os quais o glob seria expandido se estivesse no contexto da lista.

Os contextos de lista em zsh podem ser argumentos para comandos simples ou funções anônimas, after for/select x in, nos alvos de redirecionamentos (quando multiosestá ativado), em array ou atribuições de array associativo e alguns outros casos extremos¹.

Então, aqui, você poderia usar uma função 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-/)

Aqui também adicionamos o Nqualificador glob para que o glob se expanda para uma lista vazia quando não houver correspondência em vez de relatar um erro e -/para restringir arquivos do tipodiretório(determinado após a resolução do link simbólico com -).

Se houver mais de um diretório Ruby Gem e você quiser escolher aquele com o número de versão mais alto, você pode fazer:

() {
  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)

Onde nliga numericglobsorte On oordena ao contrário por nnome, então $1conterá aquele com o maior número.


¹ Conforme explicado eminfo zsh 'Conditional Expressions', também há um caso especial em que a extendedglobopção está ativada, caso em que você pode usar use globs dentro de [[ ... ]], normalmente com os operadores -n/ . -zEntão você pode usar [[ -n ~/.gem/ruby/*/bin(#qN-/) ]]para verificar se existe pelo menos um desses diretórios aqui.

informação relacionada