ディレクトリが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
}
バッシュの場合
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
、まず現在どのシェルが実行されているかを調べる必要があります。これにより、配列の最初のインデックスが か かがわかります0
。1
また、glob で を使って、glob に一致するものがない場合に zsh が終了しないように指示する必要があるかどうかもわかります(N)
(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
ワイルドカードはリスト コンテキストでのみ展開されます。つまり、ワイルドカードが複数のものに展開される可能性があります。スカラー変数への割り当てでは、スカラー変数は 1 つの値しか保持できないため、ワイルドカードは展開できません。
を使用すると、リスト コンテキストの場合に glob が展開するファイルのリストではなく、文字列をスカラー変数にruby_gem_home="$HOME/.gem/ruby/*/bin"
格納するだけです。/home/user/.gem/ruby/*/bin
$ruby_gem_home
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が空のリストに展開されるようにし、-/
ファイルタイプを制限します。ディレクトリ( によるシンボリックリンク解決後に決定されます-
)。
複数の 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
、On
o
は で逆順に並べられるためn
、 には$1
最も高い数値を持つものが含まれます。
¹ で説明したようにinfo zsh 'Conditional Expressions'
オプションがオンになっている特別なケースもあり、その場合、通常は/演算子を使用して、extendedglob
内で glob を使用できます。したがって、 を使用して、ここにそのようなディレクトリが少なくとも 1 つあるかどうかを確認できます。[[ ... ]]
-n
-z
[[ -n ~/.gem/ruby/*/bin(#qN-/) ]]