スクリプトで glob を使用してサブディレクトリが存在するかどうかを確認する

スクリプトで glob を使用してサブディレクトリが存在するかどうかを確認する

ディレクトリが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 修飾子はnOnバージョン番号の降順でリストをソートします。複数の一致がある場合に別の項目を選択するようにこれを調整します。

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、まず現在どのシェルが実行されているかを調べる必要があります。これにより、配列の最初のインデックスが か かがわかります01また、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-/)

ここでもNglob修飾子を追加して、一致しない場合はエラーを報告する代わりに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がオンの場合numericglobsortOn oは で逆順に並べられるためn、 には$1最も高い数値を持つものが含まれます。


¹ で説明したようにinfo zsh 'Conditional Expressions'オプションがオンになっている特別なケースもあり、その場合、通常は/演算子を使用して、extendedglob内で glob を使用できます。したがって、 を使用して、ここにそのようなディレクトリが少なくとも 1 つあるかどうかを確認できます。[[ ... ]]-n-z[[ -n ~/.gem/ruby/*/bin(#qN-/) ]]

関連情報