Extrahieren Sie einen Teil eines Strings mit bash / cut / split

119

Ich habe eine Zeichenfolge wie diese:

/var/cpanel/users/joebloggs:DNS9=domain.com

Ich muss den Benutzernamen ( joebloggs) aus dieser Zeichenfolge extrahieren und in einer Variablen speichern.

Das Format der Zeichenfolge ist mit Ausnahme von immer das gleiche joebloggsund domain.comich denke, die Zeichenfolge kann mit cut?

Die erste Teilung würde durch geteilt :und wir würden den ersten Teil in einer Variablen speichern, um ihn an die zweite Teilungsfunktion zu übergeben.

Die zweite Aufteilung würde durch /das letzte Wort ( joebloggs) aufgeteilt und in einer Variablen gespeichert

Ich weiß, wie man das in PHP mit Arrays und Splits macht, aber ich bin ein bisschen verloren in Bash.

Craig Edmonds
quelle

Antworten:

330

So extrahieren Sie joebloggsaus dieser Zeichenfolge in Bash mithilfe der Parametererweiterung ohne zusätzliche Prozesse ...

MYVAR="/var/cpanel/users/joebloggs:DNS9=domain.com" 

NAME=${MYVAR%:*}  # retain the part before the colon
NAME=${NAME##*/}  # retain the part after the last slash
echo $NAME

Hängt nicht davon ab, joebloggsdass Sie sich in einer bestimmten Tiefe des Pfades befinden.


Zusammenfassung

Eine Übersicht einiger Parametererweiterungsmodi als Referenz ...

${MYVAR#pattern}     # delete shortest match of pattern from the beginning
${MYVAR##pattern}    # delete longest match of pattern from the beginning
${MYVAR%pattern}     # delete shortest match of pattern from the end
${MYVAR%%pattern}    # delete longest match of pattern from the end

Bedeutet #also Übereinstimmung von Anfang an (denken Sie an eine Kommentarzeile) und %bedeutet von Ende an. Eine Instanz bedeutet die kürzeste und zwei Instanzen die längste.

Sie können Teilzeichenfolgen basierend auf der Position mithilfe von Zahlen erhalten:

${MYVAR:3}   # Remove the first three chars (leaving 4..end)
${MYVAR::3}  # Return the first three characters
${MYVAR:3:5} # The next five characters after removing the first 3 (chars 4-9)

Sie können bestimmte Zeichenfolgen oder Muster auch durch Folgendes ersetzen:

${MYVAR/search/replace}

Das patternhat das gleiche Format wie der Dateinamenabgleich, daher ist *(beliebige Zeichen) üblich, häufig gefolgt von einem bestimmten Symbol wie /oder.

Beispiele:

Gegeben eine Variable wie

MYVAR="users/joebloggs/domain.com" 

Entfernen Sie den Pfad, der den Dateinamen hinterlässt (alle Zeichen bis zu einem Schrägstrich):

echo ${MYVAR##*/}
domain.com

Entfernen Sie den Dateinamen und verlassen Sie den Pfad (löschen Sie die kürzeste Übereinstimmung nach der letzten /):

echo ${MYVAR%/*}
users/joebloggs

Holen Sie sich nur die Dateierweiterung (entfernen Sie alle vor dem letzten Zeitraum):

echo ${MYVAR##*.}
com

HINWEIS: Um zwei Operationen auszuführen , können Sie sie nicht kombinieren, sondern müssen einer Zwischenvariablen zugewiesen werden. So erhalten Sie den Dateinamen ohne Pfad oder Erweiterung:

NAME=${MYVAR##*/}      # remove part before last slash
echo ${NAME%.*}        # from the new var remove the part after the last period
domain
Beroe
quelle
Ich bin nicht sicher, ob dies ein Argument für oder gegen die kreative Verwendung von grep ist, aber versuchen Sie es mit VAR = / here / is / a / path: mit / a / Doppelpunkt / inside: DNS9 = domain.com
rici
2
Süss! Und dies geschieht innerhalb der ausführenden Shell und ist damit viel schneller als bei Verwendung anderer Befehle.
Stolsvik
3
@Fadi Sie müssen den Platzhalter wechseln, um vor den Doppelpunkt zu kommen, und #stattdessen verwenden %. Wenn Sie nur den Teil nach dem allerletzten Doppelpunkt möchten, verwenden Sie ${MYVAR##*:}, um den Teil nach dem ersten Doppelpunkt zu erhalten, verwenden Sie${MYVAR#*:}
beroe
4
Freund, du weißt nicht, wie oft ich auf diese Antwort zurückgekommen bin. Danke dir!
Joel B
1
Gute Antwort! Frage: Wenn mein Muster eine Variable wäre, würde ich es so ${RET##*$CHOP}oder so ${RET##*CHOP}(oder auf andere Weise) eingeben? EDIT: Scheint der erstere zu sein,${RET##*$CHOP}
Ctrl S
42

Definieren Sie eine Funktion wie folgt:

getUserName() {
    echo $1 | cut -d : -f 1 | xargs basename
}

Und übergeben Sie die Zeichenfolge als Parameter:

userName=$(getUserName "/var/cpanel/users/joebloggs:DNS9=domain.com")
echo $userName
Stefano Sanfilippo
quelle
1
Diese Antwort hat mir geholfen, das zu erreichen, wofür ich hierher gekommen bin. Es gibt keine akzeptierten Antworten und diese bekommt meine Stimme für die Einfachheit.
Harperville
1
Die einzige Korrektur, die ich im obigen Befehl vornehmen musste, war das Entfernen des ':' wie folgt echo $1 | cut -d -f 1 | xargs. +1 für einfache und ordentliche ans.
Bhushan
20

Was ist mit sed? Das funktioniert in einem einzigen Befehl:

sed 's#.*/\([^:]*\).*#\1#' <<<$string
  • Die #werden für Regex-Teiler verwendet, anstatt dass /die Zeichenfolge darin enthalten ist /.
  • .*/ greift nach der Saite bis zum letzten Backslash.
  • \( .. \)markiert eine Erfassungsgruppe. Das ist \([^:]*\).
    • Das [^:]sagt ein beliebiges Zeichen _ausgenommen einen Doppelpunkt und das *bedeutet null oder mehr.
  • .* bedeutet den Rest der Zeile.
  • \1bedeutet ersetzen, was in der ersten (und einzigen) Erfassungsgruppe gefunden wurde. Das ist der Name.

Hier ist die Aufschlüsselung, die die Zeichenfolge mit dem regulären Ausdruck übereinstimmt:

        /var/cpanel/users/           joebloggs  :DNS9=domain.com joebloggs
sed 's#.*/                          \([^:]*\)   .*              #\1       #'
David W.
quelle
Super schöne Präparation!
Kyb
11

Mit einem einzigen sed

echo "/var/cpanel/users/joebloggs:DNS9=domain.com" | sed 's/.*\/\(.*\):.*/\1/'
Yann Moisan
quelle
10

Verwenden eines einzelnen Awk:

... | awk -F '[/:]' '{print $5}'

Das heißt, wenn Sie entweder als Feldtrennzeichen /oder verwenden :, befindet sich der Benutzername immer in Feld 5.

So speichern Sie es in einer Variablen:

username=$(... | awk -F '[/:]' '{print $5}')

Für eine flexiblere Implementierung sedmuss der Benutzername nicht Feld 5 sein:

... | sed -e s/:.*// -e s?.*/??

Löschen Sie also alles von :und darüber hinaus und löschen Sie dann alles bis zum letzten Mal /. sedist wahrscheinlich auch schneller als awk, also ist diese Alternative definitiv besser.

Janos
quelle