
Ich sehe dies in BASH 4.3.48 (SLES12 SP4) und BASH 4.4.23 (OpenSUSE Leap 15.1), wenn ich versuche, mehrere nachstehende Leerzeichen aus dem Wert einer Variablen zu entfernen:
~> xxx="-O -Wall "
~> echo "X${xxx%% }X" # (1)
X-O -Wall X
~> echo "X${xxx%% *}X"
X-OX
~> echo "X${xxx% }X"
X-O -Wall X
~> echo "X${xxx% *}X" # (2)
X-O -Wall X
~> echo "X${xxx%% \*}X"
X-O -Wall X
Ich bin der Meinung, dass entweder „ (1)
oder“ (2)
ausreichen sollte.
Im Handbuch steht dazu ${parameter%%word}
:
Entfernt das passende Suffixmuster. Das Wort wird erweitert, um ein Muster zu erzeugen, genau wie bei der Pfadnamenerweiterung. Wenn das Muster mit einem nachfolgenden Teil des erweiterten Parameterwerts übereinstimmt, ist das Ergebnis der Erweiterung der erweiterte Parameterwert, wobei das kürzeste passende Muster (der Fall „%“) oder das längste passende Muster (der Fall „%%“) gelöscht wird.
Da es nicht wie dokumentiert funktioniert (oder wie ich die Dokumentation verstehe), vermute ich, dass es sich um einen Fehler in BASH handelt (nicht übereinstimmende Suffixe (" -Wall
") werden im Fall von " %% *
" entfernt). Habe ich Recht?
Antwort1
In echo "X${xxx%% }X"
ist das Muster ein einzelnes Leerzeichen: . Der längste passende Teil dafür ist genau das: ein einzelnes Leerzeichen. Der kürzeste passende Teil ist auch genau das: ein einzelnes Leerzeichen.
Für alles andere benötigen Sie den Globbing-Operator *
. Dieser wird jedoch mit allem übereinstimmen, wenn Sie den entfernen -Wall
. Bash-Globbing unterstützt nicht direkt ein Äquivalent des regulären Ausdrucks a*
. Sie benötigenerweitertes Globbing:
$ shopt -s extglob
$ echo "X${xxx%%+( )}X"
X-O -WallX
Antwort2
Verwenden Sie die Entfernung eines Präfixes innerhalb einer Suffixentfernung:
$ xxx="-O -Wall "
$ echo "X${xxx%"${xxx##*[! ]}"}X"
X-O -WallX
- Entfernen Sie alles bis zum letzten Zeichen, das kein Leerzeichen ist. Lassen Sie nur die Leerzeichen am Ende übrig.
- Verwenden Sie diese Leerzeichen als Muster für die Suffixentfernung
- Die innere Parametererweiterung sollte in Anführungszeichen gesetzt werden, um zu verhindern, dass sie als Muster interpretiert wird (oben nicht erforderlich, kann aber in anderen Fällen nützlich sein):
$ bash -c 'xxx="-O -Wall* "; echo "X${xxx%%"${xxx##*[! *]}"}X"'
X-O -WallX
$ bash -c 'xxx="-O -Wall* "; echo "X${xxx%%${xxx##*[! *]}}X"'
XX
Ein konstruiertes Beispiel, aber wenn die innere Erweiterung nicht in Anführungszeichen gesetzt wird, wird das darin enthaltene Asterisk von der äußeren Erweiterung als Shell-Muster behandelt. In Anführungszeichen wird es zu einem wörtlichen Asterisk.
Das von Ihnen beobachtete Verhalten ist kein Fehler, sondern zeigt lediglich die Funktionsweise einfacher Shell-Muster:
${xxx%% }
- ein einzelnes Leerzeichen ist ein einzelnes Leerzeichen
- Das längste Vorkommen eines einzelnen Leerzeichens ist ein einzelnes Leerzeichen
${xxx%% *}
- längstes Vorkommen eines einzelnen Leerzeichens gefolgt von irgendetwas/nichts
- alles/nichts wird beinhalten
-Wall
${xxx% }
- Das kürzeste Vorkommen eines einzelnen Leerzeichens ist ein einzelnes Leerzeichen
${xxx% *}
- Das kürzeste Vorkommen eines einzelnen Leerzeichens gefolgt von irgendetwas/nichts ist ein einzelnes Leerzeichen
${xxx%% \*}
\*
ist ein mit Backslash maskiertes Sternchen und wird als wörtliches Sternchen interpretiert- In der Variable gibt es kein Leerzeichen, gefolgt von einem Sternchen, es wird kein Suffix entfernt
Antwort3
read
könnte auch funktionieren (vorausgesetzt, IFS
es enthält „Leerzeichen“):
xxx="-O -Wall "
read -r xxx <<EOF
$xxx
EOF
echo "X${xxx}X"
Ausgabe:
X-O -WallX
read
teilt die Eingabe in Felder auf nachIFS
IFS
standardmäßig ist dies Leerzeichen/Tabulator/Zeilenumbruch, daher werden alle führenden und nachfolgenden Leerzeichen entfernt- Funktioniert in der ersten Zeile der Variable (ist möglicherweise nicht für mehrzeilige Variablen geeignet,
bash
könnte verwendenread -d ''
)
Antwort4
Eine einfache Parametererweiterung ist in den Mustern, die sie abgleichen und entfernen kann, recht begrenzt. Um mehrere (wiederholte) Zeichen vom Ende einer Zeichenfolge zu entfernen, besteht die übliche Lösung darin, zunächst alles zu entfernen, wasnichtdas fragliche Zeichen ${xxx##*[! ]}
(alle nachstehenden Leerzeichen). Wenn Sie dann im zweiten Schritt alles, was sich aus dieser Erweiterung ergibt (alle nachstehenden Leerzeichen), vom Ende entfernen, erhalten Sie das gewünschte Ergebnis (Entfernen nachstehender Leerzeichen).
$ xxx="-O -Wall "
$ echo "<${xxx%"${xxx##*[! ]}"}>"
<-O -Wall>
Als Alternative können Sie in Bash erweitertes Globbing verwenden:
$ shopt -s extglob
$ echo "<${xxx%%+( )}>"
<-O -Wall>
Alternativ können Sie als Alternative auf höherer Ebene das gewünschte Ergebnis mit einem regulären Ausdruck abgleichen:
$ regex='(.*[^ ]) +$';
$ [[ $xxx =~ $regex ]] && echo "<${BASH_REMATCH[1]}>" || echo "<$xxx>"
<-O -Wall>
Oder als Skript:
#!/bin/bash
xxx=${1:-"-O -Wall "}
regex='(.*[^ ]) +$'
if [[ $xxx =~ $regex ]] # if there are trailing spaces
then
echo "<${BASH_REMATCH[1]}>" # Print the string without spaces
else
echo "<$xxx>" # if there are no trailing spaces.
fi