我正在嘗試檢查目錄是否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-/) ]]
檢查這裡是否至少有一個這樣的目錄。