Ich möchte das letzte Zeichen eines Strings löschen, ich habe dieses kleine Skript ausprobiert:
#! /bin/sh
t="lkj"
t=${t:-2}
echo $t
aber es wird "lkj" ausgegeben, was mache ich falsch?
Ich möchte das letzte Zeichen eines Strings löschen, ich habe dieses kleine Skript ausprobiert:
#! /bin/sh
t="lkj"
t=${t:-2}
echo $t
aber es wird "lkj" ausgegeben, was mache ich falsch?
Antworten:
In einer POSIX-Shell ${t:-2}
bedeutet die Syntax etwas anderes - sie wird auf den Wert von t
if t
gesetzt und nicht null und ansonsten auf den Wert erweitert 2
. Wenn Sie ein einzelnes Zeichen durch Parametererweiterung zuschneiden möchten, möchten Sie wahrscheinlich die folgende Syntax verwenden${t%?}
Beachten Sie, dass in ksh93
, bash
oder zsh
, ${t:(-2)}
oder ${t: -2}
(beachten Sie das Leerzeichen) sind als Teil Expansion legal sind aber wahrscheinlich nicht das, was Sie wollen, da sie den Teil beginnend an einer Position 2 Zeichen vom Ende (dh es entfällt die Rück ersten Zeichen i
des Zeichenfolge ijk
).
Weitere Informationen finden Sie im Abschnitt zur Shell-Parametererweiterung im Bash-Referenzhandbuch:
${parameter%word}
entfernt die kürzeste Suffix- Musterübereinstimmung word
- siehe Abschnitt Parametererweiterung vonman bash
Ab bash
4.2 können Sie:
${var::-1}
Beispiel:
$ a=123
$ echo "${a::-1}"
12
Beachten Sie, dass Sie bei älteren Versionen bash
(z. B. bash 3.2.5
unter OS X) Leerzeichen zwischen und nach Doppelpunkten lassen sollten:
${var: : -1}
bash
Version 4.2-alpha. Schade, dass die Version, auf die ich Zugriff habe, früher ist. : - /
${var:offset:lenght}
nur in hinzugefügt bash 4.2
. Vielleicht fügt OSX einen eigenen Patch für bash
.
Zum Entfernen der letzten n
Zeichen aus einer Zeile, in der sed
OR nicht verwendet wird awk
:
> echo lkj | rev | cut -c (n+1)- | rev
So können Sie beispielsweise das letzte Zeichen folgendermaßen löschen one character
:
> echo lkj | rev | cut -c 2- | rev
> lk
von rev
manpage:
BESCHREIBUNG
Das Dienstprogramm rev kopiert die angegebenen Dateien in die Standardausgabe, wobei die Reihenfolge der Zeichen in jeder Zeile umgekehrt wird. Wenn keine Dateien angegeben sind, wird die Standardeingabe gelesen.
AKTUALISIEREN:
Wenn Sie die Länge der Zeichenfolge nicht kennen, versuchen Sie Folgendes:
$ x="lkj"
$ echo "${x%?}"
lk
Mit sed sollte es so schnell wie möglich sein
sed 's/.$//'
Dein einziges Echo ist dann echo ljk | sed 's/.$//'
.
Mit dieser Option kann die einzeilige Zeichenfolge eine beliebige Größe haben.
Einige Optionen je nach Shell:
t=${t%?}
t=`expr " $t" : ' \(.*\).'`
t=${t[1,-2]}
t=${t:0:-1}
t=${t:0:${#t}-1}
t=${t/%?}
t=${t/~(E).$/}
@ {t=$1} ~~ $t *?
Beachten Sie, dass, obwohl alle das letzte Zeichen entfernen sollen, einige Implementierungen (solche, die keine Multi-Byte-Zeichen unterstützen) stattdessen das letzte Byte entfernen (was wahrscheinlich das letzte Zeichen beschädigen würde, wenn es ein Multi-Byte wäre) ).
Die expr
Variante geht davon aus, $t
dass nicht mehr als ein Newline-Zeichen endet. Es wird auch ein Exit-Status ungleich Null zurückgegeben, wenn die resultierende Zeichenfolge 0
( 000
oder sogar -0
mit einigen Implementierungen) endet . Es kann auch zu unerwarteten Ergebnissen kommen, wenn die Zeichenfolge ungültige Zeichen enthält.
t=${t%?}
ist nicht Bourne, aber es ist unwahrscheinlich, dass Sie heutzutage auf eine Bourne-Shell stoßen. ${t%?}
funktioniert aber in allen anderen.
fish
ist in Arbeit. 2.3.0, das das string
eingebaute Programm einführte, wurde zum Zeitpunkt der Fragen und Antworten nicht veröffentlicht. Mit der Version, auf der ich es teste, müssen Sie string replace -r '(?s).\z' '' -- $t
(und ich würde erwarten, dass sie das ändern möchten, sollten sie die Flags ändern, die sie an PCRE übergeben) oder mehr verworrene. Es geht auch schlecht um Newline-Charaktere, und ich weiß, dass sie das auch ändern wollen.
Die tragbarste und kürzeste Antwort lautet mit ziemlicher Sicherheit:
${t%?}
Dies funktioniert in bash, sh, ash, dash, busybox / ash, zsh, ksh usw.
Es funktioniert mithilfe der Shell-Parametererweiterung der alten Schule. Insbesondere %
gibt das an, dass das kleinste übereinstimmende Suffix eines Parameters entfernt werden soll t
, das mit dem Glob-Muster übereinstimmt ?
(dh: ein beliebiges Zeichen).
Siehe "Kleinstes Suffixmuster entfernen" hier für eine (viel) detailliertere Erklärung und mehr Hintergrundinformationen. Siehe auch die Dokumentation zu Ihrer Shell (zB:) man bash
unter "Parametererweiterung".
Als Randnotiz, wenn Sie stattdessen das erste Zeichen entfernen möchten, würden Sie verwenden ${t#?}
, da #
Übereinstimmungen von der Vorderseite der Zeichenfolge (Präfix) anstelle der Rückseite (Suffix).
Erwähnenswert ist auch, dass beide %
und #
haben %%
und ##
Versionen, die die längste Version des angegebenen Musters anstelle der kürzesten entsprechen. Beide ${t%%?}
und ${t##?}
würden in diesem Fall jedoch dasselbe tun wie ihr einzelner Operator (fügen Sie also nicht das unnütze zusätzliche Zeichen hinzu). Dies liegt daran, dass das angegebene ?
Muster nur mit einem einzelnen Zeichen übereinstimmt. Mischen Sie *
mit einigen Nicht-Platzhaltern ein, und die Dinge werden mit %%
und interessanter ##
.
Das Verstehen von Parametererweiterungen oder zumindest das Wissen über ihre Existenz und das Nachschlagen von Parametern ist unglaublich nützlich, um Shell-Skripte mit vielen verschiedenen Geschmacksrichtungen zu schreiben und zu entschlüsseln. Parameter Erweiterungen sehen oft wie arkane Shell Voodoo für viele Menschen , weil ... na ja ... sie sind arkane Shell Voodoo (obwohl ziemlich gut dokumentiert , wenn Sie für „Parameter Expansion“ suchen wissen). Auf jeden Fall gut im Werkzeuggürtel zu haben, wenn man in einer Muschel steckt.
t=lkj
echo ${t:0:${#t}-1}
Sie erhalten einen Teilstring von 0 bis zur Stringlänge -1. Beachten Sie jedoch, dass diese Subtraktion bash-spezifisch ist und nicht für andere Shells funktioniert.
Kann zum Beispiel dash
nicht einmal analysieren
echo ${t:0:$(expr ${#t} - 1)}
Zum Beispiel unter Ubuntu /bin/sh
istdash
Sie können auch verwenden head
, um alle Zeichen außer dem letzten auszudrucken.
$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin
Leider enthalten einige Versionen von head
nicht die führende -
Option. Dies ist head
bei OS X der Fall.
Es ist einfach genug, reguläre Ausdrücke zu verwenden:
n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"
Um einige mögliche Verwendungen von purer Bash zu vervollständigen:
#!/bin/bash
# Testing substring removal
STR="Exemple string with trailing whitespace "
echo "'$STR'"
echo "Removed trailing whitespace: '${STR:0:${#STR}-1}'"
echo "Removed trailing whitespace: '${STR/%\ /}'"
Die erste Syntax nimmt einen Teilstring aus einem String, die Syntax ist
Für die zweite beachten Sie das Vorzeichen, was "vom Zeilenende" bedeutet und die Syntax ist
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}
Und hier sind zwei kürzere Formen der oben genannten
echo "Removed trailing whitespace: '${STR::-1}'"
echo "Removed trailing whitespace: '${STR%\ }'"
Beachten Sie hier erneut das %
Zeichen mit der Bedeutung "Entfernen" (dh durch "Ersetzen") des kürzesten übereinstimmenden Musters (hier durch Leerzeichen "\" am Ende des PARAMETERS dargestellt) - hier mit dem Namen " STR"