Löschen Sie das letzte Zeichen einer Zeichenfolge mithilfe der Zeichenfolgenmanipulation im Shell-Skript

187

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?

user3581976
quelle

Antworten:

115

In einer POSIX-Shell ${t:-2}bedeutet die Syntax etwas anderes - sie wird auf den Wert von tif tgesetzt 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, bashoder 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 ides Zeichenfolge ijk).

Weitere Informationen finden Sie im Abschnitt zur Shell-Parametererweiterung im Bash-Referenzhandbuch:

Stahlfahrer
quelle
4
Möchten Sie erklären, was die Magie hinter '%' ist? ?
Afraisse
8
@afraisse ${parameter%word}entfernt die kürzeste Suffix- Musterübereinstimmung word- siehe Abschnitt Parametererweiterung vonman bash
steeldriver
3
Dies funktioniert gut für Bash 4.1.2: $ {t%?} Für Leute, die mit CentOS / RHEL 6.x
Joey T am
185

Ab bash4.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.5unter OS X) Leerzeichen zwischen und nach Doppelpunkten lassen sollten:

${var: : -1}
cuonglm
quelle
13
Dies funktioniert ab bashVersion 4.2-alpha. Schade, dass die Version, auf die ich Zugriff habe, früher ist. : - /
hjk
2
@iamaziz: Von Bash Changelog wurde die negative Länge in ${var:offset:lenght}nur in hinzugefügt bash 4.2. Vielleicht fügt OSX einen eigenen Patch für bash.
Cuonglm
1
@ Cuonglm weder Arbeit: /
Iamaziz
1
Funktioniert nicht auf Mac.
Shinzou
1
MACsters, sehen Sie auf Russ 'Antwort herab
P i
67

Zum Entfernen der letzten nZeichen aus einer Zeile, in der sedOR 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 revmanpage:

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
Networker
quelle
62

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.

1111161171159459134
quelle
10
Beachten Sie, dass im Allgemeinen nicht das letzte Zeichen der Zeichenfolge gelöscht wird , sondern das letzte Zeichen jeder Zeile der Zeichenfolge .
Stéphane Chazelas
44

Einige Optionen je nach Shell:

  • POSIX: t=${t%?}
  • Bourne: t=`expr " $t" : ' \(.*\).'`
  • zsh / yash: t=${t[1,-2]}
  • bash / zsh: t=${t:0:-1}
  • ksh93 / bash / zsh / mksh: t=${t:0:${#t}-1}
  • ksh93 / bash / zsh / mksh: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
  • es: @ {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 exprVariante geht davon aus, $tdass nicht mehr als ein Newline-Zeichen endet. Es wird auch ein Exit-Status ungleich Null zurückgegeben, wenn die resultierende Zeichenfolge 0( 000oder sogar -0mit einigen Implementierungen) endet . Es kann auch zu unerwarteten Ergebnissen kommen, wenn die Zeichenfolge ungültige Zeichen enthält.

Stéphane Chazelas
quelle
Schön und gründlich! Aber ... ich nehme an, dass alle diese Shells POSIX unterstützen, also sollte jeder nur dieses verwenden, um das portabelste zu sein. Auch die kleinste Zeichenanzahl!
Russ
@Russ, t=${t%?}ist nicht Bourne, aber es ist unwahrscheinlich, dass Sie heutzutage auf eine Bourne-Shell stoßen. ${t%?}funktioniert aber in allen anderen.
Stéphane Chazelas
Keine Fischschalenoption angegeben! Wahrscheinlich populärer in diesen Tagen als ksh93 ...
rien333
@ rien333. Ich würde warten, bis sich das Interface etwas stabilisiert hat. fishist in Arbeit. 2.3.0, das das stringeingebaute 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.
Stéphane Chazelas
Für die POSIX-Antwort aufgewertet. bestätigte die Arbeit an Bash 3.2.57 (1)
Avindra Goolcharan
26

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 bashunter "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.

Russ
quelle
Kurz und bündig und funktioniert sowohl unter MacOS als auch unter Linux!
14.
18
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 dashnicht einmal analysieren

echo ${t:0:$(expr ${#t} - 1)}

Zum Beispiel unter Ubuntu /bin/shistdash

Engel
quelle
15

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 headnicht die führende -Option. Dies ist headbei OS X der Fall.

Greenbeansugar
quelle
5

Es ist einfach genug, reguläre Ausdrücke zu verwenden:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"
entmutigen
quelle
5

Einige Verfeinerungen. Um mehr als ein Zeichen zu entfernen, können Sie mehrere Fragezeichen hinzufügen. Um beispielsweise die letzten beiden Zeichen aus der Variablen zu entfernen $SRC_IP_MSG, können Sie Folgendes verwenden:

SRC_IP_MSG=${SRC_IP_MSG%??}
yuliskov
quelle
4

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"

CermakM
quelle
1

Da können wir auch PHP in Kommandozeilen- oder Shell-Skripten verwenden. Es ist manchmal nützlich für chirurgisches Parsen.

php -r "echo substr('Hello', 0, -1);" 
// Output hell

Mit Rohrleitungen:

echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell
NVRM
quelle
-1

In ksh:

echo ${ORACLE_SID/%?/}
Alex
quelle