Wie erhalte ich das letzte Zeichen einer Zeichenfolge in einer Shell?

125

Ich habe die folgenden Zeilen geschrieben, um das letzte Zeichen einer Zeichenfolge zu erhalten:

str=$1
i=$((${#str}-1))
echo ${str:$i:1}

Es funktioniert für abcd/:

$ bash last_ch.sh abcd/
/

Es funktioniert nicht fürabcd* :

$ bash last_ch.sh abcd*
array.sh assign.sh date.sh dict.sh full_path.sh last_ch.sh

Es listet die Dateien im aktuellen Ordner auf .

thinker3
quelle

Antworten:

95

Dies ist einer der Gründe, warum Sie Ihre Variablen zitieren müssen:

echo "${str:$i:1}"

Andernfalls erweitert bash die Variable und führt in diesem Fall vor dem Ausdrucken ein Globbing durch. Es ist auch besser, den Parameter im Skript anzugeben (falls Sie einen passenden Dateinamen haben):

sh lash_ch.sh 'abcde*'

Siehe auch die Reihenfolge der Erweiterungen im Bash-Referenzhandbuch . Variablen werden vor der Dateinamenerweiterung erweitert.

Um das letzte Zeichen zu erhalten, sollten Sie nur -1als Index verwenden, da die negativen Indizes ab dem Ende der Zeichenfolge zählen:

echo "${str: -1}"

Das Leerzeichen nach dem Doppelpunkt ( :) ist ERFORDERLICH.

Dieser Ansatz wird ohne den Platz nicht funktionieren.

perreal
quelle
62
Übrigens zählen negative Indizes von rechts, "${1: -1}"das reicht also.
Choroba
7
Sie sollten als formelle Lösung antworten, nicht hier in Kommentaren.
BMW
Zu Ihrer Information: Sie können dies auch in einem "Einzeiler" als tun echo "${str:$((${#str}-1)):1}". Auf diese Weise können Sie auch die zurückgegebenen nachfolgenden Zeichen variieren. Diese Arbeit für mich in Bash v4.1.0 (1)
SaxDaddy
16
@ Chorobas Antwort: Beachten Sie den verdammten Raum! Das "${1:-1}
stolpert
192

Per @perreal ist es wichtig, Variablen zu zitieren, aber weil ich diesen Beitrag ungefähr fünf Mal gelesen habe, bevor ich in den Kommentaren einen einfacheren Ansatz für die vorliegende Frage gefunden habe ...

str='abcd/'
echo "${str: -1}"

Ausgabe: /

str='abcd*'
echo "${str: -1}"

Ausgabe: *

Vielen Dank an alle, die oben daran teilgenommen haben. Ich habe im gesamten Thread entsprechend +1 hinzugefügt!

Quickshiftin
quelle
25
NB: Beachten Sie den Platz im Inneren ${std: -1}, ohne den Platz funktioniert dies nicht. Ich bin darauf gestoßen und habe festgestellt, dass das ${std:~0}auch funktioniert (mit oder ohne Leerzeichen). Ich bin mir nicht sicher, ob dies ein dokumentiertes Verhalten ist, aber ich habe mich nicht darum gekümmert, es nachzuschlagen.
Bart
4
es ist dokumentiert. man bash sagt: Arithmetische Ausdrücke, die mit a - beginnen, müssen durch Leerzeichen vom vorhergehenden getrennt werden: um von der Erweiterung "Standardwerte verwenden" unterschieden zu werden
mighq
3
Das ist großartig, danke fürs Teilen. Die BASH-Manpage ist fast überwältigend zu lesen, ich war schon mehrere Male dort. Ich denke, sie könnten einen College-Kurs über Shell-Scripting machen lol.
Quickshiftin
3
Diese Lösung funktioniert in einem .shSkript nicht, wenn versucht wird, das abgerufene Zeichen einer Variablen zuzuweisen. declare LAST=$(echo "${str: -1}") Dies führt zum Beispiel dazu, dass der böse rote Balken einen Syntaxfehler
anzeigt, der
3
wenn Editor Syntaxprüfung nicht wie dieser Raum try${str:0-1}
ttt
36

Ich weiß, dass dies ein sehr alter Thread ist, aber niemand hat erwähnt, was für mich die sauberste Antwort ist:

echo -n $str | tail -c 1

Beachten Sie, -ndass das Echo am Ende keine neue Zeile enthält.

user5394399
quelle
15
Nicht einmal annähernd sauberer als $ {str: -1}
Spitzmaus
8
Es ist sauberer, da es nicht auf einem Bash-Ismus beruht, sodass es mit jeder Posix-Shell funktioniert.
John Eikenberry
Was ist der "Bashist"? "$ {str: -1}" Und ... Was ist am saubersten? "$ {str: -1}" Bash ist das Besht! :-)
Roger
Für alle, die versuchen, die letzten Zeichen in einer großen Datei zu drucken, hat dies den Job für mich erledigt: cat user / folder / file.json | tail -c 1 Sie können die 1 durch die Anzahl der Zeichen ersetzen, die Sie drucken möchten.
Diego Serrano
5

eine andere lösung mit awk script:

letzte 1 Zeichen:

echo $str | awk '{print substr($0,length,1)}'

letzte 5 Zeichen:

echo $str | awk '{print substr($0,length-5,5)}'
mr.baby123
quelle
1
Nicht das, wonach das OP gefragt hat, aber dies ist die beste Lösung, die ich für eine Datei gefunden habe. Die meisten anderen Ansätze funktionieren nicht, wenn Sie eine Datei zuordnen, wie in: cat file | awk '{print substr($0,length,1)}'Danke!
Xmar
4

Einzelne Zeile:

${str:${#str}-1:1}

Jetzt:

echo "${str:${#str}-1:1}"
Eduardo Cuomo
quelle
1
Warum verwenden Sie zwei Klammerpaare? Außerdem können Sie seit 4.2 negative Offsets verwenden, wie in der Antwort von quickshiftin gezeigt: echo "${str: -1}"ist genug (beachten Sie den Abstand zwischen :und -).
gniourf_gniourf
Zwei Paare von Klammern werden für arithmetische Operationen verwendet
Eduardo Cuomo
Nein. Bitte lesen Sie den entsprechenden Teil des Handbuchs, in dem Sie lesen werden: Länge und Versatz sind arithmetische Ausdrücke (siehe Shell-Arithmetik ); Daher sind Sie bereits in der Shell-Arithmetik und benötigen überhaupt keine Klammern. Außerdem wäre es logischer, wenn das, was Sie gesagt haben, wahr wäre, eine arithmetische Erweiterung mit zu haben $((...)).
gniourf_gniourf
@gniourf_gniourf du hast recht! Ich verstehe jetzt Ihre vorherige Frage. Antwort aktualisiert
Eduardo Cuomo
4

Jede Antwort impliziert bisher, dass das Wort "Shell" in der Frage Bash entspricht.

So könnte man das in einer Standard-Bourne-Shell machen:

printf $str | tail -c 1
phep
quelle
1
echo -nWie von user5394399 vorgeschlagen, werden Probleme vermieden, wenn $strSonderformatzeichen enthalten sind.
Conrad Meyer
Der -nSchalter ist ein Bashismus. Es wird in der Standard-Bourne-Shell nicht als Bindestrich usw. funktionieren. Das war der Punkt meiner Antwort.
Phep
1
Nicht ganz ein Bashismus, aber POSIX erkennt, dass die Implementierung definiert ist ("Wenn der erste Operand -n ist oder wenn einer der Operanden ein <backslash> -Zeichen enthält, sind die Ergebnisse implementierungsdefiniert"). Ihre Antwort könnte stattdessen mit printf "%s" "$str" | ...:-) behoben werden .
Conrad Meyer
4

Versuchen:

"${str:$((${#str}-1)):1}"

Zum Beispiel:

someone@mypc:~$ str="A random string*"; echo "$str"
A random string*
someone@mypc:~$ echo "${str:$((${#str}-1)):1}"
*
someone@mypc:~$ echo "${str:$((${#str}-2)):1}"
g
mark_infinite
quelle
-2
echo $str | cut -c $((${#str}))

ist ein guter Ansatz

pradeep
quelle