我正在尝试检查目录是否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
在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
}
这全局限定符 N
确保结果是一个空数组,而不是在不匹配时出现错误。 glob 限定符n
并按On
版本号递减对列表进行排序;当有多个匹配项时,调整此选项以选择不同的项目。
在bash中:
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
}
在bash中
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
使用数组,检查数组的第一个元素(即bash中的[0],zsh中的[1])是否是目录。例如在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
,您需要首先找出当前正在运行的 shell。这将告诉您数组的第一个索引是否是0
或1
。它还将确定您是否需要与 glob 一起使用,(N)
以告诉 zsh 在 glob 没有匹配项时不要退出(如果 glob 匹配错误,zsh 将默认退出)。
(此时,编写一个能够可靠工作的脚本应该开始变得明显两个都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 中的列表上下文可以是简单命令或匿名函数的参数, after 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 扩展为空列表而不是报告错误,并-/
限制为文件类型目录(在符号链接解析后确定-
)。
如果有多个 ruby gem 目录,并且您想选择版本号最高的一个,您可以执行以下操作:
() {
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
,并按ameOn
o
反向排序n
,因此$1
将包含数字最高的那个。
1 正如中所解释的info zsh 'Conditional Expressions'
,还有一种特殊情况,当该extendedglob
选项打开时,您可以在内部使用 use globs [[ ... ]]
,通常与-n
/-z
运算符一起使用。所以你可以用来[[ -n ~/.gem/ruby/*/bin(#qN-/) ]]
检查这里是否至少有一个这样的目录。