Я пытаюсь проверить, bin
находится ли каталог внутри каталога, который иногда может меняться. В этом конкретном случае может измениться номер версии ruby (например $HOME/.gem/ruby/2.6.0/bin
). Вот что я сделал на данный момент:
#!/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
Я не хочу использовать, find
так как это часть процесса входа в систему. Какой самый элегантный способ, используя встроенные команды zsh/bash, добиться этого?
Спасибо!
РЕДАКТИРОВАТЬ:Забыл упомянуть, что это сценарий zsh
.
решение1
Для этого случая конкретно
Я не думаю, что имеет смысл искать какой-либо каталог. Какой смысл использовать каталог, который не соответствует установленной версии Ruby? И что, если пользователь настроил другой каталог в ~/.gemrc
?
Я не очень хорошо знаком с Ruby, но думаю, что правильным решением будет взять первую запись в gempath
.
ruby_gem_home=$(gem environment gempath | sed 's/:.*//')
if [ -z "$ruby_gem_home" ]; then unset ruby_gem_home; fi
В зш
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
}
Theквалификатор glob N
гарантирует, что результатом будет пустой массив, а не ошибка, если совпадений нет. Квалификаторы glob n
и On
сортируют список по убыванию номеров версий; настройте это, чтобы выбрать другой элемент при наличии нескольких совпадений.
В Баше:
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
}
В Баше
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"
}
Здесь я беру последний элемент массива, который часто, но не всегда, является самой последней версией: 10
сортируется до 9
.
решение2
Используйте массив и проверьте, является ли первый элемент массива (т. е. [0] в bash, [1] в zsh) каталогом. Например 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
Чтобы работать и в , zsh
и bash
, вам нужно сначала выяснить, какая оболочка запущена в данный момент. Это скажет вам, является ли первый индекс массива 0
или 1
. Это также определит, нужно ли вам использовать (N)
with glob, чтобы сообщить zsh НЕ выходить, если для glob нет совпадений (zsh по умолчанию выйдет при ошибках соответствия glob).
(в этот момент должно стать очевидным, что написание скрипта, который будет надежно работать вобаbash и zsh, вероятно, принесут больше проблем, чем пользы)
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
...
При необходимости вы также можете выполнить итерацию по массиву, чтобы проверить, соответствует ли элемент определенной версии. Например:
for d in "${rubygemdirs[@]}" ; do
[[ $d =~ /2\.6\.0/ ]] && echo found gem dir for ruby 2.6.0
done
Примечание: /
s в регулярном выражении — это буквальные прямые слеши. Они не отмечают начало и конец регулярного выражения, как в sed
.
решение3
Подстановочные знаки расширяются только в контекстах списков, то есть там, где они могут быть расширены до нескольких вещей. При назначении скалярной переменной они не могут быть расширены, поскольку скалярная переменная может содержать только одно значение.
При использовании ruby_gem_home="$HOME/.gem/ruby/*/bin"
вы просто сохраните /home/user/.gem/ruby/*/bin
строку в $ruby_gem_home
скалярной переменной, а не список файлов, в которые будет расширяться glob в списочном контексте.
Списочные контексты в zsh могут быть аргументами простых команд или анонимных функций, после for/select x in
, в целях перенаправлений (когда multios
включено), в массивах или ассоциативных массивах, а также в некоторых других особых случаях¹.
Итак, здесь можно использовать анонимную функцию:
() {
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-/)
Здесь также добавлен N
квалификатор glob, чтобы при отсутствии совпадений glob расширялся до пустого списка вместо сообщения об ошибке, а также -/
для ограничения файлов типакаталог(определяется после разрешения символической ссылки с помощью -
).
Если существует несколько каталогов gem-файлов Ruby и вы хотите выбрать тот, у которого самый высокий номер версии, вы можете сделать следующее:
() {
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)
Где n
включается numericglobsort
, и On
o
упорядочивает в обратном порядке по n
имени, поэтому $1
будет содержать тот, у которого самый высокий номер.
¹ Как объяснено вinfo zsh 'Conditional Expressions'
, есть также особый случай, когда extendedglob
опция включена, в этом случае вы можете использовать globs внутри [[ ... ]]
, обычно с операторами -n
/ -z
. Таким образом, вы можете использовать , [[ -n ~/.gem/ruby/*/bin(#qN-/) ]]
чтобы проверить, есть ли здесь хотя бы один такой каталог.