it-swarm.com.de

Ein Befehl zum Drucken der letzten 3 Zeichen einer Zeichenfolge

Ich weiß, dass der Befehl cut die ersten n Zeichen einer Zeichenfolge drucken kann, aber wie wählt man die letzten n Zeichen aus?

Wenn ich eine Zeichenfolge mit einer variablen Anzahl von Zeichen habe, wie kann ich nur die letzten drei Zeichen der Zeichenfolge drucken? z.B.

"Unbegrenzte" Ausgabe benötigt wird "ted" 
 "987654" Ausgabe benötigt ist "654" 
 "123456789" Ausgabe benötigt wird "789"
31
odyssey

Warum hat niemand die offensichtliche Antwort gegeben?

sed 's/.*\(...\)/\1/'

… Oder das etwas weniger offensichtliche

grep -o '...$'

Zugegeben, der zweite hat den Nachteil, dass Linien mit weniger als drei Zeichen verschwinden; Die Frage hat das Verhalten für diesen Fall jedoch nicht explizit definiert.

Einfach halten - Schwanz

Wir sollten keinen regulären Ausdruck oder mehr als einen Prozess benötigen, nur um Zeichen zu zählen.
Der Befehl tail, der häufig zum Anzeigen der letzten Zeilen einer Datei verwendet wird, verfügt über die Option -c (--bytes). ), was genau das richtige Werkzeug dafür zu sein scheint:

$ printf 123456789 | tail -c 3
789

(Wenn Sie sich in einer Shell befinden, ist es sinnvoll, eine Methode wie in der Antwort von mikeserv zu verwenden, da dies das Starten des Prozesses für tail spart.)

Echte Unicode-Zeichen?

Nun fragen Sie nach den letzten drei Zeichen ; Das gibt Ihnen diese Antwort nicht: Sie gibt die letzten drei Bytes aus!

Solange jedes Zeichen ein Byte ist, funktioniert tail -c Nur. Es kann also verwendet werden, wenn der Zeichensatz ASCII, ISO 8859-1 Ist. oder eine Variante.

Wenn Sie eine Unicode-Eingabe haben, wie im allgemeinen Format UTF-8, ist das Ergebnis falsch:

$ printf 123αβγ | tail -c 3
�γ

In diesem Beispiel sind die griechischen Zeichen Alpha, Beta und Gamma mit UTF-8 Zwei Byte lang:

$ printf 123αβγ | wc -c  
9

Die Option -m Kann mindestens die echten Unicode-Zeichen zählen:

printf 123αβγ | wc -m
6

Ok, die letzten 6 Bytes geben uns die letzten 3 Zeichen:

$ printf 123αβγ | tail -c 6
αβγ

Daher unterstützt tail die Behandlung allgemeiner Zeichen nicht und versucht es nicht einmal (siehe unten): Es verarbeitet Zeilen variabler Größe, aber keine Zeichen variabler Größe.

Sagen wir es so: tail ist genau richtig für die Struktur des zu lösenden Problems, aber falsch für die Art der Daten.

GNU Coreutils

Wenn man weiter schaut, stellt sich heraus, dass du GNU coreutils, die Sammlung grundlegender Werkzeuge wie sed, ls, tail und cut ist noch nicht vollständig internationalisiert. Dabei geht es hauptsächlich um die Unterstützung von Unicode.
Zum Beispiel wäre cut ein guter Kandidat, um hier anstelle des Schwanzes für die Charakterunterstützung zu verwenden; Es gibt Optionen zum Bearbeiten von Bytes oder Zeichen, -c (--bytes) Und -m (--chars);

Nur das -m/--chars Ist ab Version
cut (GNU coreutils) 8.21, 2013,
nicht implementiert!

Von info cut:

`-c CHARACTER-LIST'
`--characters=CHARACTER-LIST'
     Select for printing only the characters in positions listed in CHARACTER-LIST.  
     The same as `-b' for now, but internationalization will change that.


Siehe auch diese Antwort bis Kann `cut -c` (` --characters`) nicht mit UTF-8 verwenden? .

45
Volker Siegel

Wenn sich Ihr Text in einer Shell-Variablen namens STRING befindet, können Sie dies in einer bash, zsh oder mksh Shell tun:

printf '%s\n' "${STRING:(-3)}"

Oder

printf '%s\n' "${STRING: -3}"

dies hat auch den Vorteil, mit ksh93 zu arbeiten, woher diese Syntax stammt.

Der Punkt ist, dass der : Vom - Getrennt werden muss, sonst wird er zum ${var:-default} - Operator der Bourne Shell.

Die äquivalente Syntax in den Shells zsh oder yash lautet:

printf '%s\n' "${STRING[-3,-1]}"
37
DopeGhoti

Verwenden von awk:

awk '{ print substr( $0, length($0) - 2, length($0) ) }' file
ted
654
789
13
jasonwryan

Wenn sich die Zeichenfolge in einer Variablen befindet, können Sie Folgendes tun:

printf %s\\n "${var#"${var%???}"}"

Das entfernt die letzten drei Zeichen vom Wert von $var Wie:

${var%???}

... und dann vom Kopf von $var alles abstreifen aber wie wurde gerade abgestreift:

${var#"${var%???}"}

Diese Methode hat ihre Vor- und Nachteile. Auf der positiven Seite ist es vollständig POSIX-portabel und sollte in jeder modernen Shell funktionieren. Auch wenn $var Nicht mindestens drei Zeichen enthält nichts, aber die nachfolgende \n Ewline wird gedruckt. Andererseits, wenn Sie wollen es in diesem Fall gedruckt haben, brauchen Sie einen zusätzlichen Schritt wie:

last3=${var#"${var%???}"}
printf %s\\n "${last3:-$var}"

Auf diese Weise ist $last3 Nur dann leer, wenn $var 3 oder weniger Bytes enthält. Und $var Wird $last3 Nur ersetzt, wenn $last3 Leer ist oder unset - und wir wissen, dass es nicht unset ist, weil wir nur Stell es ein.

11
mikeserv

Sie können dies tun, aber das ist ein wenig ... übertrieben:

for s in unlimited 987654 123456789; do
    rev <<< $s | cut -c 1-3 | rev
done 
ted
654
789
7
glenn jackman

Die kugelsichere Lösung für utf-8-Saiten:

utf8_str=$'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82' # привет

last_three_chars=$(Perl -CAO -e 'print substr($ARGV[0], -3)' "$utf8_str")

Oder verwenden Sie:

last_three_chars=$(Perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' "$utf8_str")

um die fehlerhafte Datenverarbeitung zu verhindern.

Beispiel:

Perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' $'\xd0\xd2\xc9\xd7\xc5\xd4' # koi8-r привет

Gibt so etwas aus:

utf8 "\xD0" does not map to Unicode at /usr/lib/x86_64-linux-gnu/Perl/5.20/Encode.pm line 175.

Hängt nicht von den Gebietsschemaeinstellungen ab (d. H. Funktioniert mit LC_ALL=C). Bash, sed, grep, awk, rev erfordern ungefähr Folgendes: LC_ALL=en_US.UTF-8

Gemeinsame Lösung:

  • Bytes empfangen
  • Codierung erkennen
  • Dekodieren Sie Bytes in Zeichen
  • Zeichen extrahieren
  • Zeichen in Bytes codieren

Sie können die Codierung mit chardet erkennen. Siehe auch verwandte Projekte .

Sie können mit Encode in Perl, Codecs in Python 2.7) dekodieren/kodieren

Beispiel :

Extrahieren Sie die letzten drei Zeichen aus der Zeichenfolge utf-16le und konvertieren Sie diese Zeichen in utf-8

utf16_le_str=$'\xff\xfe\x3f\x04\x40\x04\x38\x04\x32\x04\x35\x04\x42\x04' # привет

chardet <<<"$utf16_le_str"  # outputs <stdin>: UTF-16LE with confidence 1.0

last_three_utf8_chars=$(Perl -MEncode -e '
    my $chars = decode("utf-16le", $ARGV[0]);
    my $last_three_chars = substr($chars, -3);
    my $bytes = encode("utf-8", $last_three_chars);
    print $bytes;
  ' "$utf16_le_str"
)

Siehe auch: perlunitut , Python 2 Unicode HOWTO

3

Was ist mit "expr" oder "rev"?

Eine Antwort ähnlich der von @ G-Man : expr "$yourstring" : '.*\(...\)$' Sie hat den gleichen Nachteil wie die grep-Lösung.

Ein bekannter Trick besteht darin, "cut" mit "rev" zu kombinieren: echo "$yourstring" | rev | cut -n 1-3 | rev

1
gildux

tail -n 1 revisions.log | awk '{Drucksubstrat ($ 0, 0, Länge ($ 0) - (Länge ($ 0) -13))}'

Wenn Sie die ersten dreizehn Zeichen von Anfang an drucken möchten

0

Holen Sie sich die Größe der Zeichenfolge mit:

size=${#STRING}

Dann erhalten Sie Teilzeichenfolge des letzten n Zeichens:

echo ${STRING:size-n:size}

Zum Beispiel:

STRING=123456789
n=3
size=${#STRING}
echo ${STRING:size-n:size}

würde geben:

789
0
Esref

printf funktioniert nicht, wenn der String Leerzeichen enthält.

Unten Code für Zeichenfolge mit Leerzeichen

str="Welcome to Linux"
echo -n $str | tail -c 3

nux

0
Saurabh