Überprüfen, ob ein Unterverzeichnis mit Glob im Skript vorhanden ist

Überprüfen, ob ein Unterverzeichnis mit Glob im Skript vorhanden ist

Ich versuche zu prüfen, ob sich ein Verzeichnis binin einem Verzeichnis befindet, das sich manchmal ändern kann. In diesem speziellen Fall kann sich die Versionsnummer von Ruby ändern (z. B. $HOME/.gem/ruby/2.6.0/bin). Folgendes habe ich bisher getan:

#!/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

Ich möchte es nicht verwenden, findda dies Teil eines Anmeldevorgangs ist. Was ist der eleganteste Weg, dies mithilfe integrierter zsh/bash-Befehle zu erreichen?

Danke!

BEARBEITEN:Ich habe vergessen zu erwähnen, dass dies für ein zshSkript ist.

Antwort1

Für diesen Fall speziell

Ich glaube nicht, dass es Sinn macht, nach irgendeinem Verzeichnis zu suchen. Was bringt es, ein Verzeichnis zu verwenden, das nicht mit der installierten Version von Ruby übereinstimmt? Und was, wenn der Benutzer ein anderes Verzeichnis konfiguriert hat ~/.gemrc?

Ich kenne mich mit Ruby nicht so gut aus, aber ich glaube, die richtige Lösung besteht darin, den ersten Eintrag in zu nehmen gempath.

ruby_gem_home=$(gem environment gempath | sed 's/:.*//')
if [ -z "$ruby_gem_home" ]; then unset ruby_gem_home; fi

In 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
}

DerGlob-Qualifizierer Nstellt sicher, dass das Ergebnis ein leeres Array ist und kein Fehler, wenn keine Übereinstimmung vorliegt. Die Glob-Qualifizierer nsortieren Ondie Liste nach absteigenden Versionsnummern. Passen Sie dies an, um ein anderes Element auszuwählen, wenn mehrere Übereinstimmungen vorliegen.

In 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
}

In 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"
}

Hier nehme ich das letzte Array-Element, welches oft aber nicht immer die aktuellste Version ist: 10wird vor sortiert 9.

Antwort2

Verwenden Sie ein Array und prüfen Sie, ob das erste Element (also [0] in Bash, [1] in Zsh) des Arrays ein Verzeichnis ist. Beispiel 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

Um sowohl in zshals auch zu arbeiten bash, müssen Sie zunächst herausfinden, welche Shell derzeit ausgeführt wird. Dadurch erfahren Sie, ob der erste Index eines Arrays 0oder ist 1. Außerdem wird dadurch bestimmt, ob Sie mit dem Glob verwenden müssen, (N)um zsh anzuweisen, NICHT zu beenden, wenn es keine Übereinstimmungen für den Glob gibt (zsh wird standardmäßig beendet, wenn es Glob-Übereinstimmungsfehler gibt).

(an diesem Punkt sollte es langsam offensichtlich werden, dass das Schreiben eines Skripts, das zuverlässig funktioniert,beidebash und zsh wird wahrscheinlich mehr Ärger machen als es wert ist)

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
...

Bei Bedarf können Sie auch über das Array iterieren, um zu prüfen, ob ein Element einer bestimmten Version entspricht. zB

for d in "${rubygemdirs[@]}" ; do
  [[ $d =~ /2\.6\.0/ ]] && echo found gem dir for ruby 2.6.0
done

Hinweis: Die /s im regulären Ausdruck sind wörtliche Schrägstriche. Sie markieren nicht den Anfang und das Ende des regulären Ausdrucks wie in sed.

Antwort3

Platzhalter werden nur in Listenkontexten erweitert, also dort, wo sie zu mehreren Dingen erweitert werden können. Bei einer Zuweisung an eine Skalarvariable können sie nicht erweitert werden, da eine Skalarvariable nur einen Wert enthalten kann.

Mit würden Sie lediglich den String in einer Skalarvariable ruby_gem_home="$HOME/.gem/ruby/*/bin"speichern und nicht die Liste der Dateien, zu der sich glob im Listenkontext erweitern würde./home/user/.gem/ruby/*/bin$ruby_gem_home

Listenkontexte in zsh können Argumente für einfache Befehle oder anonyme Funktionen sein, danach for/select x in, in den Zielen von Umleitungen (wenn multiosaktiviert ist), in Array- oder assoziativen Array-Zuweisungen und einigen anderen Sonderfällen¹.

Sie könnten hier also eine anonyme Funktion verwenden:

() {
  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-/)

Hier wird auch der NGlob-Qualifizierer hinzugefügt, damit der Glob bei fehlender Übereinstimmung zu einer leeren Liste erweitert wird, anstatt einen Fehler zu melden, und -/um die Datei auf den Typ zu beschränkenVerzeichnis(wird nach der Symlink-Auflösung mit ermittelt -).

Wenn es mehr als ein Ruby-Gem-Verzeichnis gibt und Sie das mit der höchsten Versionsnummer auswählen möchten, haben Sie folgende Möglichkeiten:

() {
  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)

Wobei nsich einschaltet numericglobsortund On oin umgekehrter Reihenfolge nach ndemselben sortiert, sodass $1das mit der höchsten Nummer enthalten ist.


¹ Wie ininfo zsh 'Conditional Expressions', es gibt auch einen Sonderfall, wenn die extendedglobOption aktiviert ist. In diesem Fall können Sie Globs innerhalb verwenden [[ ... ]], normalerweise mit den -n/ -z-Operatoren. Sie können also verwenden, [[ -n ~/.gem/ruby/*/bin(#qN-/) ]]um zu prüfen, ob hier mindestens ein solches Verzeichnis vorhanden ist.

verwandte Informationen