Bir dizenin sadece son 3 karakterini basma komutu


30

cutKomutun nbir dizgenin ilk karakterlerini yazdırabileceğini ama son nkarakterlerin nasıl seçileceğini biliyorum ?

Değişken sayıda karakter içeren bir dizgem varsa, dizenin yalnızca son üç karakterini nasıl yazdırabilirim. Örneğin.

"sınırsız" çıktı gerekli "ted"
"987654" çıkış gerekli "654"
"123456789" çıkış gerekli "789"

Yanıtlar:


52

Neden kimse açık bir cevap vermedi?

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

… Ya da biraz daha az belirgin

grep -o '...$'

Kuşkusuz, ikincisi, üçten az karakter içeren satırların yok olması; Ancak soru açıkça bu dava için davranış tanımlamamıştır.


6
veyagrep -o '.\{3\}$'
Avinash Raj,

3
veyaecho "unlimited" | python -c "print raw_input()[-3:]"
Kiro

8
@Kiro ya da "echo unlimited" | java -jar EnterpriseWordTrimmer.jarkarakter manipülasyonu için daha ağır bir dil getirmenin gerçekten gerekli olduğunu sanmıyorum.
wchargin

11
Unuttuğunuz @WCharginjava -server -Xms300M -Xmx3G -XX:+UseParallelGC -cp /path/to/all/the/jars/ -Dinput.interactive=false -Dinput.pipe=true -Dconfig.file=/path/to/config/last-three-letters.cfg -jar ...
23:14

6
grep -o -P '.{0,3}$'Satır 3 karakterden az olsa bile son 3 karakteri yazdıracaktır. -Pdiş tellerinden kaçmak zorunda kalmaz.
Raghu Dodda

43

Basit tutmak - kuyruk

Sadece karakterleri saymak için düzenli bir ifadeye veya birden fazla işleme ihtiyacımız olmamalıdır. Genellikle bir dosyanın son satırlarını göstermek için kullanılan
komut tail, bunun için doğru araç gibi görünen bir seçeneğe ( ) sahiptir :-c--bytes

$ printf 123456789 | tail -c 3
789

(Bir kabuğun içindeyken, mikeserv'in yanıtında olduğu gibi bir yöntem kullanmak mantıklıdır, çünkü işlemi başlatmak için tasarruf sağlar tail.)

Gerçek Unicode karakterler?

Şimdi son üç karakteri soruyorsunuz ; Bu cevabın size verdiği şey bu değil: son üç baytı çıktı !

Her karakter bir bayt olduğu sürece tail -cçalışır. O kullanılan olabilir Yani karakter kümesi ise ASCII, ISO 8859-1ya da bir çeşit.

Unicode girişiniz varsa, ortak UTF-8formatta olduğu gibi, sonuç yanlıştır:

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

Bu örnekte, kullanılarak UTF-8yunan karakterleri alfa, beta ve gama iki bayttır:

$ printf 123αβγ | wc -c  
9

Bu seçenek -men azından gerçek unicode karakterleri sayabilir:

printf 123αβγ | wc -m
6

Tamam, yani son 6 bayt bize son 3 karakteri verecek:

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

Dolayısıyla, tailgenel karakterlerin kullanılmasını desteklemiyor ve hatta denemiyor bile (aşağıya bakın): Değişken boyut satırlarını işler, ancak değişken boyut karakterleri kullanmaz.

Bunu şu şekilde koyalım: tailsorunun yapısının çözmesi için doğru, ancak veri türünde yanlış.

GNU coreutils

Ayrıca baktığımızda, sana GNU coreutils, temel araçları koleksiyonu gibi çıkıyor sed, ls, tailve cut, henüz tam uluslararasılaştırıldı değildir. Genelde Unicode'u desteklemekle ilgili.
Örneğin, cutburada karakter desteği için kuyruk yerine kullanmak için iyi bir aday olacaktır; Bayt veya karakterlerde çalışmak için seçeneklere sahiptir, -c( --bytes) ve -m( --chars);

Ancak bundan -m/ --charssürüm itibariyle edilir
cut (GNU coreutils) 8.212013,
uygulanmadı!

Kimden 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.


Ayrıca bakınız bu cevabı için Can'ın UTF-8 ile `kesme -c` (` --characters`) kullanılmasın? .


2
Aslında, diğer cevapların çoğu, geçerli yerel ayar UTF-8 kodlamasını belirttiği sürece Unicode'u gayet iyi görünüyor. Sadece sizin ve Glen Jackson'ın cuttemelli çözümü sizin gibi görünmüyor.
Ilmari Karonen

@IlmariKaronen Doğru, ipucu için teşekkürler. Bazı detaylarla birlikte düzenleme yaptım.
Volker Siegel

1
POSIX’in tail, baytla değil, karakterle başa çıkması gerektiğini açıkça belirttiğini unutmayın . Bir keresinde karakterleri de seçmek için yeni bir seçenek eklemek için bir yama yaptım, ancak birleştiğine inanmıyorum: - /
Martin Tournoij

Dosya modunda çalışmaz, gibitail -c3 -n10 /var/log/syslog
Suncatcher

@Suncatcher Denedim ve işe yaradı. Gördüğün problem nedir? Komutunuz tail -c3 -n10 /var/log/syslogson 10 satır için soruyor ve bu benim için çalışıyor. Seçeneği kullanırsınız -c3ve bundan sonra çelişen seçeneği kullanabilirsiniz -n10. Daha sonraki seçenek önceliklidir.
Volker Siegel

36

Metin adlı bir kabuk değişkeni ise STRING, bir yapabilirsiniz bash, zshveya mkshkabuk:

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

Veya

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

Ayrıca bu sözdiziminin nereden geldiği ksh93 ile çalışmak için de faydası vardır.

Nokta olduğunu :ayrılmış zorundadır -aksi takdirde olur, ${var:-default}Bourne kabuğunun operatör.

zshVeya yashkabuklarındaki eşdeğer sözdizimi :

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

2
Daha fazla bilgi bulabilmem için ne tür bir sözdizimi / işlem denir?
Tulains Córdova

6
Buna Substring Expansion adı verilir . Bir çeşit Parametre Genişlemesi . Genel form $ {parametresi: offset: length} , ancak uzunluk alanı isteğe bağlıdır (ve gördüğünüz gibi yukarıdaki cevabında yer almamıştır). DopeGhoti de yazılı olabilir ${STRING:(-3):3}(belirten uzunluk alanı) ${STRING: -3}(arasında bir boşluk :ve -) ya da ${STRING: -3:3}.
G-Man 'Monica'yı Yeniden Girin' diyor

Bu durumda, uzunluğunun belirtilmesi, 3"sondan üçüncü karakterden üç karakter, dahil olmak üzere" ifadesinin pratik terimlerle özdeş bir işlem olduğu sorusuna göre oldukça fazladır. dahil ".
DopeGhoti

13

Kullanarak awk:

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

11

Dize bir değişken ise, şunları yapabilirsiniz:

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

Bu, son üç karakteri $varbenzer değerden çıkarır:

${var%???}

... ve sonra $varher şeyin başından soyunuyorlar, ama neye benziyordu:

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

Bu yöntemin kendine özgü olumsuz yönleri ve olumsuz yönleri vardır. Parlak tarafı tamamen POSIX-taşınabilir ve herhangi bir modern kabukta çalışması gerekir. Ayrıca, $varen az üç karakter içermiyorsa, ewline'dan sonra hiçbir şey\n basılmaz. Sonra tekrar, bu durumda yazdırılmasını istiyorsanız , aşağıdaki gibi ek bir adıma ihtiyacınız var:

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

Bu şekilde $last3, yalnızca $var3 veya daha az bayt içeriyorsa hiç boştur . Ve $varsadece boşsa ya da - ile ikame $last3edilir $last3, unsetve unsetbiz sadece ayarlamamızdan olmadığını biliriz .


Bu oldukça düzenli +1. Kenara: printfbiçim dizelerinizi alıntılamamanızın bir nedeni var mı?
jasonwryan

Neden sadece kullanmıyorsunuz ${VARNAME:(-3)}(varsayılan bash)?
DopeGhoti

1
Aydınlattığın için teşekkürler; (bana) biraz garip görünüyor olsa bile, mantıklı ...
jasonwryan

1
@DopeGhoti - çünkü bu neredeyse hiç yapmadığım bir varsayım. Bu bashPOSIX uyumluluğunu iddia eden diğer tüm kabuklarda olduğu gibi çalışır .
mikeserv

3
@odyssey - sorundur csholduğu değil arasında modern POSIX uyumlu maalesef, burada söz kabukları. POSIX-shell spec modeli kshhem daha sonra hem cshde geleneksel Bourne tarzı mermilerin bir kombinasyonundan sonra modellenmiştir . kshHem cshmükemmel iş kontrol işlevselliği hem de eski Bourne stillerinin giriş / çıkış yönlendirmesini içeriyordu . Aynı zamanda bazı şeyleri de ekledi - örneğin yukarıda anlattığım karakter dizisi işleme kavramları. Bu muhtemelen cshbildiğim kadarıyla geleneksel hiçbir işe yaramayacak , söylediğim için üzgünüm.
mikeserv

7

Bunu yapabilirsin, ama bu biraz ... aşırı:

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

3

Utf-8 dizeleri için kurşun geçirmez çözüm:

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")

Veya kullan:

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

hatalı biçimlendirilmiş veri işlemeyi önlemek için.

Örnek:

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

Bunun gibi bir şey çıktılar:

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

Yerel ayarlara bağlı değildir (yani ile çalışır LC_ALL=C). Bash, sed, grep, awk, revBöyle bir şey gerektirir:LC_ALL=en_US.UTF-8

Ortak çözüm:

  • Bayt al
  • Kodlamayı algıla
  • Baytları karakterlere dönüştür
  • Karakterleri ayıkla
  • Karakteri bayt olarak kodla

Uchardet ile kodlamayı tespit edebilirsiniz . Ayrıca ilgili projelere bakın .

Perl'deki Encode , Python 2.7'deki codec bileşenlerinin kodunu çözebilir / kodlayabilirsiniz.

Örnek :

Utf-16le dizesinden son üç karakteri çıkar ve bu karakterleri utf-8'e dönüştür

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"
)

Ayrıca bakınız: perlunitut , Python 2 Unicode NASIL


echokurşun geçirmez kaynağınız nedir?
mikeserv

@mikeserv, decode/encodebenim kurşun geçirmez kaynağım. Cevabımı temizledim.
Evgeny Vereshchagin

Bu ayrıca, doğru çalıştığını garanti etmek için yerel ayarlara da bağlıdır, çünkü bir bayt kümesi farklı karakter gruplarındaki farklı karakterleri yansıtabilir. "İşe yarıyor" LC_ALL=Cçünkü bu çok "aptal" bir ayardır, ancak bir UTF-8 dizesini SHIFT-5'e veya bir SHIFT-5 dizesini
KOI8'e

@Carpetsmoker, teşekkürler. Yorumunuzu açıklayabilir misiniz? Sanırım bu iyi perl -CAO -e 'print substr($ARGV[0], -3)'çalışıyor. A@ARGV elemanlarının UTF-8'de kodlanmış dizeler olması beklenir, OSTDOUT UTF-8'de olur.
Evgeny Vereshchagin

size atama hakkında söylediğiniz gibi görünüyorutf8_str
Evgeny Vereshchagin

1

"Expr" veya "rev" kullanmaya ne dersiniz?

@ G-Man tarafından verilene benzer bir cevap : expr "$yourstring" : '.*\(...\)$' Grep çözümüyle aynı dezavantajı vardır.

İyi bilinen bir numara "kes" i "rev" ile birleştirmektir: echo "$yourstring" | rev | cut -n 1-3 | rev


revSolüsyon çok benziyor Glenn Jackman en
Jeff Schaller

Haklısın @Jeff_Schaller: Ben Glen'in birini özledim :-(
gildux

0

Dizenin büyüklüğünü:

size=${#STRING}

Sonra son n karakterinin alt dizesini alın:

echo ${STRING:size-n:size}

Örneğin:

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

verecek:

789

0

kuyruk -n 1 revisions.log | awk '{print substr ($ 0, 0, uzunluk ($ 0) - (uzunluk ($ 0) -13))}'

İlk on üç karakteri basmak istiyorsanız


-1

Dizge içinde boşluklar varsa printf çalışmayacaktır.

Boşluk içeren dize için kodun altında

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

nux


Eğer printfişe yaramazsa, o zaman çok yanlış bir şey yapıyorsun .
Kusalananda

1
@Kusalananda: Saurabh'ın gösterdiği emre dayanarak denediler printf $str( printf "$str"veya yerine printf '%s' "$str"). Ve, evet, printf $strbir çok yanlış. ( echo -n $strdaha iyi değil.)
G-Man
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.