在腳本中使用 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 限定符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。這將告訴您數組的第一個索引是否是01。它還將確定您是否需要與 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-/)

這裡還添加了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打開numericglobsort,並按ameOn o反向排序n,因此$1將包含數字最高的數字。


1 正如中所解釋的info zsh 'Conditional Expressions',還有一種特殊情況,當該extendedglob選項開啟時,您可以在內部使用 use globs [[ ... ]],通常與-n/-z運算符一起使用。所以你可以用來[[ -n ~/.gem/ruby/*/bin(#qN-/) ]]檢查這裡是否至少有一個這樣的目錄。

相關內容