Wie übergebe ich einen regulären Ausdruck, wenn ich in bash einen Verzeichnispfad finde?

14

Ich habe ein kleines Bash-Skript geschrieben, um festzustellen, ob ein Verzeichnis mit dem Namen anacondaoder minicondain meinem Benutzer $HOME. Aber es findet das miniconda2Verzeichnis in meinem Haus nicht.

Wie könnte ich das beheben?

if [ -d "$HOME"/"(ana|mini)conda[0-9]?" ]; then
    echo "miniconda directory is found in your $HOME"
else
    echo "anaconda/miniconda is not found in your $HOME"
fi

PS: Wenn [ -d "$HOME"/miniconda2 ]; thenja, dann findet es das miniconda2-Verzeichnis, also denke ich, dass der Fehler im Teil liegt"(ana|mini)conda[0-9]?"

Ich möchte, dass das Skript allgemein ist. Für mich ist es miniconda2, aber für andere Benutzer ist es möglicherweise anaconda2, miniconda3 und so weiter.

Jenny
quelle
Ein anderer Benutzer könnte anaconda_2 oder -2 oder -may2019 verwenden. Wäre xxxconda * nicht besser?
WinEunuuchs2Unix
2
Die Bash-Dateinamenerweiterung verwendet Glob-Ausdrücke, keine regulären Ausdrücke.
Peter Cordes

Antworten:

13

Dies ist eine überraschend knifflige Sache.

Grundsätzlich -dwird nur ein einziges Argument getestet - auch wenn Sie Dateinamen mit einem regulären Ausdruck abgleichen könnten.

Eine Möglichkeit wäre, das Problem umzudrehen und Verzeichnisse auf eine Regex-Übereinstimmung zu testen, anstatt die Regex-Übereinstimmung für Verzeichnisse zu testen. Mit anderen Worten, durchlaufen Sie alle Verzeichnisse $HOMEmit einem einfachen Shell-Glob und testen Sie jedes mit Ihrer Regex, brechen Sie eine Übereinstimmung ab und testen Sie schließlich, ob das BASH_REMATCHArray nicht leer ist:

#!/bin/bash

for d in "$HOME"/*/; do
  if [[ $d =~ (ana|mini)conda[0-9]? ]]; then
    break;
  fi
done

if ((${#BASH_REMATCH[@]} > 0)); then
    echo "anaconda/miniconda directory is found in your $HOME"
  else
    echo "anaconda/miniconda is not found in your $HOME"
fi

Eine alternative Möglichkeit wäre, anstelle des regulären Ausdrucks einen erweiterten Shell-Glob zu verwenden und alle Glob-Übereinstimmungen in einem Array zu erfassen. Testen Sie dann, ob das Array nicht leer ist:

#!/bin/bash

shopt -s extglob nullglob

dirs=( "$HOME"/@(ana|mini)conda?([0-9])/ )

if (( ${#dirs[@]} > 0 )); then
  echo "anaconda/miniconda directory is found in your $HOME"
else
  echo "anaconda/miniconda is not found in your $HOME"
fi

Durch das Trailing wird /sichergestellt, dass nur Verzeichnisse abgeglichen werden. die nullglobverhindern , dass die Shell die unerreichte Zeichenfolge im Fall von Null Spielen von der Rückkehr.


Um entweder rekursiv zu machen, setzen Sie die globstarShell-Option ( shopt -s globstar) und dann jeweils:

  • (Regex-Version): for d in "$HOME"/**/; do

  • (erweiterte glob version): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )

Stahlfahrer
quelle
1
Ich würde die Array-Route gehen. Sie können ?([0-9])anstelle von @(|[0-9])- ?(...)Null oder Eins verwenden, genau wie beim Regex- ?Quantifizierer.
Glenn Jackman
2
Sie brauchen nicht einmal extglob, wenn Sie eine Klammererweiterung verwenden (dies generiert alle möglichen übereinstimmenden Namen):~/{ana,mini}conda{0..9}*/
xenoid
Gibt es überhaupt eine dieser Lösungen zu bearbeiten, so dass es auch dann funktioniert , wenn minioder anacondainstalliert ist $HOME/sub-directories? Zum Beispiel$HOME/sub-dir1/sub-dir2/miniconda2
Jenny
1
@ Jenny siehe meine globstar
Änderung
1
@terdon yeah Ich wollte nicht wirklich in die Hasengrube gehen, um herauszufinden, was das "richtige" ist - ich habe nur den
regulären Ausdruck
9

In der Tat ist dies, wie bereits erwähnt, schwierig. Mein Ansatz ist der folgende:

  • Gebrauch findund sein Regex- Funktionen, um die fraglichen Verzeichnisse zu finden.
  • Lassen find ein xfür jedes gefundene Verzeichnis drucken
  • Speichern Sie die x es in einer Zeichenfolge
  • Wenn die Zeichenfolge nicht leer ist, wurde eines der Verzeichnisse gefunden.

Somit:

xString=$(find $HOME -maxdepth 1 \
                     -type d \
                     -regextype egrep \
                     -regex "$HOME/(ana|mini)conda[0-9]?" \
                     -printf 'x');
if [ -n "$xString" ]; then
    echo "found one of the directories";
else
    echo "no match.";
fi

Erläuterung:

  • find $HOME -maxdepth 1 findet alles unten $HOME , beschränkt aber die Suche auf eine Ebene (dh, es wird nicht in Unterverzeichnisse rekursiv).
  • -type d schränkt die Suche auf nur ein dSchränkt Branchen ein
  • -regextype egrepgibt an, mit findwelcher Art von regulären Ausdrücken wir es zu tun haben. Dies ist notwendig, weil Dinge wie [0-9]?und (…|…)etwas Besonderes sind undfind diese standardmäßig nicht erkennen.
  • -regex "$HOME/(ana|mini)conda[0-9]?"ist der eigentliche reguläre Ausdruck wir Ausschau halten möchten
  • -printf 'x'druckt nur eine xfür alles, was die vorherigen Bedingungen erfüllt.
PerlDuck
quelle
Wenn es ein Streichholz gibt. -bash: -regex: command not found found one of the directories
Jenny
Hallo PerlDuck: Danke. Eine schöne Antwort auch. Ich erhalte jedoch eine Fehlermeldung für printf. Wenn ich beispielsweise das Skript ausführe, wird es in Ordnung ausgeführt, aber der Befehl printf wird nicht gefunden, wenn es keine Übereinstimmung gibt, aber ich denke, es liegt daran, dass möglicherweise nichts zu drucken ist. -bash: -printf: command not found no match.
Jenny
3
@Jenny Möglicherweise haben Sie beim Kopieren einen Tippfehler gemacht, da dieser für mich in Ordnung ist. -printfist kein Befehl, sondern ein Argument für find. Das macht der Backslash am Ende der vorherigen Zeile.
wjandrea
1
Ich würde vorschlagen -quit, den gefundenen Pfad nach dem Drucken zu drucken, es sei denn, Sie möchten weiterhin Unklarheiten feststellen.
Peter Cordes
Und warum nicht den tatsächlichen Pfad ausdrucken? Sie haben es bereits, so scheint es eine Schande, es zu verwerfen und xstattdessen zu verwenden:foundDir=$(find $HOME -maxdepth 1 -type d -regextype egrep -regex "$HOME/(ana|mini)conda[0-9]?" -print -quit); echo "found $foundDir"
Terdon
2

Sie können eine Liste der zu testenden Verzeichnisnamen durchlaufen und darauf reagieren, wenn einer von ihnen vorhanden ist:

a=0
for i in {ana,mini}conda{,2}; do
  if [ -d "$i" ]; then
    unset a
    break
  fi
done
echo "anaconda/miniconda directory is ${a+not }found in your $HOME"

Diese Lösung lässt offensichtlich nicht die volle Regex-Kraft zu, aber die Shell-Globbing- und Klammer-Expansion ist zumindest in dem von Ihnen gezeigten Fall gleich. Die Schleife wird beendet, sobald ein Verzeichnis vorhanden ist, und die zuvor festgelegte Variable wird gelöscht a. In der folgenden echoZeile wird die Parametererweiterung ${a+not } auf "nichts" erweitert, wenn agesetzt (= kein Verzeichnis gefunden) und "nicht".

Dessert
quelle
1

Mögliche Problemumgehung besteht darin, Miniconda und Anaconda wie unten gezeigt getrennt zu suchen

if [ -d "$HOME"/miniconda* ] || [ -d "$HOME"/anaconda* ]; then
    echo "miniconda directory is found in your $HOME"
else
    echo "anaconda/miniconda is not found in your $HOME"
fi

Aber wenn jemand Vorschläge hat, würde ich gerne wissen, warum wir bei der Suche nach Verzeichnissen keine Regex übergeben können.

Jenny
quelle
2
Ich habe das hochgestuft - aber dann wurde mir klar, dass es kaputt gehen wird, wenn der Benutzer mehr als ein passendes Verzeichnis hat (z. B. miniconda UND miniconda2)
steeldriver
@steeldriver: "es wird kaputt gehen, wenn der Benutzer mehr als ein passendes Verzeichnis hat" Ja, das ist in der Tat wahr. Haben Sie Vorschläge, wie Sie das Problem beheben können?
Jenny
@Jenny Verwenden Sie ein Array, wie in der Antwort von steeldriver. shopt -s nullglob; dirs=( "$HOME"/miniconda* "$HOME"/anaconda* ); if (( ${#dirs[@]} > 0 )); then ...
wjandrea
Wenn Sie ersetzen ] || [mit -osollte es zumindest nicht brechen , wenn beiden Verzeichnisse gefunden werden , da beide Verzeichnis Klackse für im gleichen Test betreut werden.
Phoenix
@steeldriver und Jenny: Möglicherweise möchten Sie, dass die Mehrdeutigkeit aufgehoben wird, anstatt nur eine auszuwählen . Lassen Sie den Benutzer sein Verzeichnis angeben, anstatt möglicherweise das falsche auszuwählen. (Bearbeiten Sie z. B. das Skript, um den Verzeichnisnamen festzulegen, anstatt den automatischen Erkennungscode auszuführen.)
Peter Cordes,