Entfernen Sie ein festes Präfix / Suffix aus einer Zeichenfolge in Bash

485

In meinem bashSkript habe ich eine Zeichenfolge und deren Präfix / Suffix. Ich muss das Präfix / Suffix aus der ursprünglichen Zeichenfolge entfernen.

Angenommen, ich habe die folgenden Werte:

string="hello-world"
prefix="hell"
suffix="ld"

Wie komme ich zum folgenden Ergebnis?

result="o-wor"
Dušan Rychnovský
quelle
5
Werfen
14
Seien Sie sehr vorsichtig, wenn Sie auf das sogenannte Advanced Bash Scripting Guide verlinken. Es enthält eine Mischung aus guten Ratschlägen und schrecklichen.
Tripleee

Antworten:

719
$ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo "${foo}"
o-wor
Adrian Frühwirth
quelle
40
Es gibt auch ## und %%, die so viel wie möglich entfernen, wenn das $ -Präfix oder das $ -Suffix Platzhalter enthalten.
Punkte
28
Gibt es eine Möglichkeit, beide in einer Zeile zu kombinieren? Ich habe es versucht, ${${string#prefix}%suffix}aber es funktioniert nicht.
static_rtti
28
@static_rtti Nein, leider können Sie die Parametersubstitution nicht so verschachteln. Ich weiß, es ist eine Schande.
Adrian Frühwirth
87
@ AdrianFrühwirth: Die ganze Sprache ist eine Schande, aber es ist so nützlich :)
static_rtti
8
Nvm, "Bash Substitution" in Google hat gefunden, was ich wollte.
Tyler
89

Mit sed:

$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

Innerhalb des Befehls sed ^stimmt das Zeichen mit dem Text überein , der mit beginnt $prefix, und der nachfolgende $Text stimmt mit dem Text überein, der mit endet $suffix.

Adrian Frühwirth macht einige gute Punkte in den Kommentaren unten, aber sed für diesen Zweck kann sehr nützlich sein. Die Tatsache, dass der Inhalt von $ Präfix und $ Suffix von sed interpretiert wird, kann entweder gut oder schlecht sein - solange Sie darauf achten, sollte es Ihnen gut gehen. Das Schöne ist, Sie können so etwas tun:

$ prefix='^.*ll'
$ suffix='ld$'
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

Das ist vielleicht das, was Sie wollen, und es ist sowohl schicker als auch leistungsfähiger als die Substitution von Bash-Variablen. Wenn Sie sich daran erinnern, dass mit großer Kraft große Verantwortung verbunden ist (wie Spiderman sagt), sollte es Ihnen gut gehen.

Eine kurze Einführung in sed finden Sie unter http://evc-cit.info/cit052/sed_tutorial.html

Ein Hinweis zur Shell und zur Verwendung von Strings:

Für das gegebene Beispiel würde auch das Folgende funktionieren:

$ echo $string | sed -e s/^$prefix// -e s/$suffix$//

... aber nur weil:

  1. echo ist es egal, wie viele Zeichenfolgen in seiner Argumentliste enthalten sind, und
  2. Das Präfix $ und das Suffix $ enthalten keine Leerzeichen

Es wird im Allgemeinen empfohlen, eine Zeichenfolge in der Befehlszeile in Anführungszeichen zu setzen, da sie dem Befehl auch dann als einzelnes Argument angezeigt wird, wenn sie Leerzeichen enthält. Wir zitieren $ Präfix und $ Suffix aus dem gleichen Grund: Jeder Bearbeitungsbefehl an sed wird als eine Zeichenfolge übergeben. Wir verwenden doppelte Anführungszeichen, weil sie eine variable Interpolation ermöglichen. Hätten wir einfache Anführungszeichen verwendet, hätte der Befehl sed ein Literal erhalten, $prefixund $suffixdas ist sicherlich nicht das, was wir wollten.

Beachten Sie auch meine Verwendung von einfachen Anführungszeichen beim Festlegen der Variablen prefixund suffix. Wir möchten sicher nicht, dass irgendetwas in den Zeichenfolgen interpretiert wird, also zitieren wir sie einfach, damit keine Interpolation stattfindet. Auch in diesem Beispiel ist dies möglicherweise nicht erforderlich, aber es ist eine sehr gute Angewohnheit, sich darauf einzulassen.

Chris Kolodin
quelle
8
Leider ist dies aus mehreren Gründen ein schlechter Rat: 1) Nicht zitiert, $stringunterliegt der Aufteilung und dem Globbing von Wörtern. 2) $prefixund $suffixkann Ausdrücke enthalten, seddie interpretiert werden, z. B. reguläre Ausdrücke oder das als Trennzeichen verwendete Zeichen, das den gesamten Befehl unterbricht. 3) Zweimaliges sedAnrufen ist nicht erforderlich (Sie können -e 's///' -e '///'stattdessen) und die Leitung könnte auch vermieden werden. Betrachten Sie zum Beispiel string='./ *'und / oder prefix='./'und sehen Sie, dass es aufgrund von 1)und schrecklich bricht 2).
Adrian Frühwirth
Fun note: sed kann fast alles als Begrenzer nehmen. In meinem Fall konnte ich, da ich Präfixverzeichnisse aus Pfaden heraus analysierte, nicht verwenden /, also habe ich sed "s#^$prefix##stattdessen verwendet. (Fragilität: Dateinamen können nicht enthalten #. Da ich die Dateien kontrolliere, sind wir dort sicher.)
Olie
@Olie Dateinamen können beliebige Zeichen außer dem Schrägstrich und dem Nullzeichen enthalten. Wenn Sie also nicht die Kontrolle haben, können Sie nicht davon ausgehen, dass ein Dateiname bestimmte Zeichen nicht enthält.
Adrian Frühwirth
Ja, ich weiß nicht, was ich dort gedacht habe. iOS vielleicht? Keine Ahnung. Dateinamen können sicherlich "#" enthalten. Keine Ahnung warum ich das gesagt habe. :)
Olie
@Olie: Wie ich Ihren ursprünglichen Kommentar verstanden habe, haben Sie gesagt, dass die Einschränkung Ihrer Wahl #als Sed-Trennzeichen bedeutet, dass Sie keine Dateien verarbeiten können, die dieses Zeichen enthalten.
P Daddy
17

Kennen Sie die Länge Ihres Präfixes und Suffixes? In Ihrem Fall:

result=$(echo $string | cut -c5- | rev | cut -c3- | rev)

Oder allgemeiner:

result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev)

Aber die Lösung von Adrian Frühwirth ist viel cooler! Das wusste ich nicht!

tommy.carstensen
quelle
14

Ich benutze grep zum Entfernen von Präfixen aus Pfaden (die von nicht gut behandelt werden sed):

echo "$input" | grep -oP "^$prefix\K.*"

\K Entfernt alle Zeichen davor aus der Übereinstimmung.

Vladimir Petrakovich
quelle
grep -Pist eine nicht standardmäßige Erweiterung. Mehr Leistung für Sie, wenn es auf Ihrer Plattform unterstützt wird. Dies ist jedoch ein zweifelhafter Rat, wenn Ihr Code einigermaßen portabel sein muss.
Tripleee
@ Tripleee In der Tat. Aber ich denke, ein System mit installiertem GNU Bash hat auch einen Grep, der PCRE unterstützt.
Vladimir Petrakovich
1
Nein, MacOS hat zum Beispiel Bash out of the box, aber nicht GNU grep. Frühere Versionen hatten tatsächlich die -POption von BSD, grepaber sie haben sie entfernt.
Tripleee
9
$ string="hello-world"
$ prefix="hell"
$ suffix="ld"

$ #remove "hell" from "hello-world" if "hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}

$ #remove "ld" from "o-world" if "ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor

Anmerkungen:

# $ Präfix: Durch Hinzufügen von # wird sichergestellt, dass der Teilstring "Hölle" nur entfernt wird, wenn er am Anfang gefunden wird. % $ Suffix: Durch Hinzufügen von% wird sichergestellt, dass der Teilstring "ld" nur entfernt wird, wenn er am Ende gefunden wird.

Ohne diese werden die Teilzeichenfolgen "hell" und "ld" überall entfernt, selbst wenn sie sich in der Mitte befinden.

Vijay Vat
quelle
Danke für die Notizen! qq: In Ihrem Codebeispiel haben Sie auch einen Schrägstrich /direkt nach der Zeichenfolge. Wozu dient das?
Diego Salazar
1
/ trennt die aktuelle Zeichenfolge und die Unterzeichenfolge. Unterzeichenfolge hier ist das Suffix in der geposteten Frage.
Vijay Vat
7

Verwenden des =~Operators :

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ [[ "$string" =~ ^$prefix(.*)$suffix$ ]] && echo "${BASH_REMATCH[1]}"
o-wor
Martin - マ ー チ ン
quelle
6

Kleine und universelle Lösung:

expr "$string" : "$prefix\(.*\)$suffix"
Tosi Do.
quelle
1
Wenn Sie Bash verwenden, sollten Sie es wahrscheinlich überhaupt nicht verwenden expr. Es war in den Tagen der ursprünglichen Bourne-Hülle eine Art praktisches Spülbecken, ist aber jetzt weit hinter seinem Mindesthaltbarkeitsdatum zurück.
Tripleee
5

Mit @Adrian Frühwirth Antwort:

function strip {
    local STRING=${1#$"$2"}
    echo ${STRING%$"$2"}
}

benutze es so

HELLO=":hello:"
HELLO=$(strip "$HELLO" ":")
echo $HELLO # hello
math2001
quelle
0

Ich würde Capture-Gruppen in Regex verwenden:

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ set +H # Disables history substitution, can be omitted in scripts.
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/" <<< $string
o-wor
$ string1=$string$string
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/g" <<< $string1
o-woro-wor

((?:(?!(${suffix})).)*)stellt sicher, dass der Inhalt von ${suffix}aus der Erfassungsgruppe ausgeschlossen wird. In Bezug auf das Beispiel ist es die Zeichenfolge, die äquivalent zu ist [^A-Z]*. Andernfalls erhalten Sie:

$ perl -pe "s/${prefix}(.*)${suffix}/\1/g" <<< $string1
o-worldhello-wor
Bayou
quelle