Estou tentando verificar se um diretório bin
está 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 find
porque 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 zsh
script.
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 N
garante que o resultado seja uma matriz vazia em vez de um erro se não houver correspondência. Os qualificadores glob n
e On
classificam 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 zsh
e bash
, você precisa primeiro descobrir qual shell está em execução no momento. Isso lhe dirá se o primeiro índice de um array é 0
ou 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/*/bin
string em $ruby_gem_home
uma 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 multios
está 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 N
qualificador 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 n
liga numericglobsort
e On
o
ordena ao contrário por n
nome, então $1
conterá aquele com o maior número.
¹ Conforme explicado eminfo zsh 'Conditional Expressions'
, também há um caso especial em que a extendedglob
opção está ativada, caso em que você pode usar use globs dentro de [[ ... ]]
, normalmente com os operadores -n
/ . -z
Então você pode usar [[ -n ~/.gem/ruby/*/bin(#qN-/) ]]
para verificar se existe pelo menos um desses diretórios aqui.