Kabuktaki bir dizeyi bölme ve son alanı alma


293

Dize varsayalım 1:2:3:4:5ve son alanını ( 5bu durumda) almak istiyorum . Bash kullanarak bunu nasıl yapabilirim? Denedim cut, ancak son alanı nasıl belirleyeceğimi bilmiyorum -f.

Yanıtlar:


430

Dize işleçlerini kullanabilirsiniz :

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

Bu, her şeyi önden ':' şeklinde, açgözlülükle keser.

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

7
Bu verilen sorun için çalışırken, aşağıdaki William yanıtı ( stackoverflow.com/a/3163857/520162 ) 5dize ise de döner 1:2:3:4:5:(dize işleçlerini kullanırken boş bir sonuç verir). Bu, özellikle bir bitiş /karakteri içeren (veya içermeyen) yolları ayrıştırırken kullanışlıdır .
eckes

9
Öyleyse bunun tam tersini nasıl yapardınız? '1: 2: 3: 4:'?
Dobz

12
Ve son ayırıcıdan önce bu parçayı nasıl tutar? Görünüşe göre kullanarak ${foo%:*}. #- başlangıçtan itibaren; %- Sondan. #, %- en kısa eşleşme; ##, %%- en uzun eşleşme.
Mihai Danila

1
Son öğeyi yoldan almak istersem nasıl kullanmalıyım? echo ${pwd##*/}çalışmıyor.
Putnik

2
@Putnik bu komutu pwdbir değişken olarak görür . Deneyin dir=$(pwd); echo ${dir##*/}. Benim için çalışıyor!
Stan Strum

344

Başka bir yol, önce ve sonra tersine çevirmektir cut:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

Bu, sondan bir alanın veya sondan numaralandırılan herhangi bir alanın alınmasını kolaylaştırır.


17
Bu cevap güzel, çünkü yazarın (muhtemelen) zaten tanıdık olduğu 'kesimi' kullanıyor. Artı, çünkü bu konuda yanıt almak Ben dolayısıyla arama üzerinden bu konuyu bulma, bu sorunun asıl 'cut' kullanarak ve vardı ediyorum.
Dannid

6
Sınırlayıcı olarak boşluk kullanan kişiler için bazı kes ve yapıştır yem:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll

2
devir | kes -d -f1 | rev çok zekidir! Teşekkürler! Bana bir grup yardımcı oldu (benim kullanım durumum rev | -d '' -f 2- | rev
EdgeCaseBerg

1
Her zaman unutuyorum rev, tam da ihtiyacım olan şeydi! cut -b20- | rev | cut -b10- | rev
shearn89

Bu çözümü buldum, "awk -F" / "'{print $ NF}'" ile dosya yollarını kesme girişimim benim için biraz kırdı, çünkü beyaz boşluklar da dahil olmak üzere dosya adları da kesildi
THX

76

Kesim kullanarak son alanı elde etmek zor, ama burada awk ve perl'de bazı çözümler var

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'

5
bu çözümün kabul edilen cevaba göre büyük avantajı vardır: ayrıca bir bitiş /karakteri içeren veya içermeyen yollarla da eşleşir : /a/b/c/dve işlerken /a/b/c/d/aynı sonucu verir ( d) pwd | awk -F/ '{print $NF}'. Kabul edilen cevap boş bir sonuç ile sonuçlanır/a/b/c/d/
eckes

@eckes AWK çözümü durumunda, GNU bash'da, sürüm 4.3.48 (1) -çalışma, doğru eğik çizgi olduğunda veya olmadığında önemli olduğundan doğru değildir. Basitçe söylemek gerekirse AWK /sınırlayıcı olarak kullanır ve yolunuz ise /my/path/dir/son sınırlayıcıdan sonra değeri kullanır, bu da boş bir dizedir. Bu yüzden benim gibi bir şey yapmanız gerekiyorsa, eğik çizgiden kaçınmak en iyisidir.
stamster

Alt dize son alana kadar nasıl ulaşabilirim?
blackjacx

1
@blackjacx Bazı tuhaflıklar var, ancak böyle bir şey awk '{$NF=""; print $0}' FS=: OFS=:genellikle yeterince iyi çalışıyor.
William Pursell

29

Oldukça basit kullanım varsayarsak (örneğin, sınırlayıcıdan kaçmaz), grep'i kullanabilirsiniz:

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

Arıza - satır sonunda sınırlayıcı ([^:]) olmayan tüm karakterleri ($) bulun. -o sadece eşleşen parçayı yazdırır.


-E genişletilmiş sözdizimi kullanmak anlamına gelir; [^ ...] listelenen karakter (ler) den başka bir anlama gelmez; + bir veya daha fazla bu isabet (model için mümkün olan maksimum uzunluğu alacaktır; bu öğe bir gnu uzantısıdır) - örneğin ayırma karakterleri iki nokta üst üste işaretidir.
Alexander Stohr

18

Tek yön:

var1="1:2:3:4:5"
var2=${var1##*:}

Başka bir dizi kullanarak:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}

Yine bir dizi ile:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}

Bash (sürüm> = 3.2) normal ifadelerini kullanma:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}

11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Sınırlayıcıyı yeni bir satıra çevirin ve ile son girişi seçin tail -1.


Son öğe bir içerirse başarısız olur \n, ancak çoğu durumda en okunabilir çözümdür.
Yajo

7

Kullanma sed:

$ echo '1:2:3:4:5' | sed 's/.*://' # => 5

$ echo '' | sed 's/.*://' # => (empty)

$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c

$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c

4

Son alanınız tek bir karakterse bunu yapabilirsiniz:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

Bash'de dize manipülasyonunu kontrol edin .


Bu işe yaramazsa: son veren karakteri arasında adeğil, son alan .
gniourf_gniourf

1
Doğru, fikir bu, eğer son alanın uzunluğunu biliyorsanız iyi. Değilse başka bir şey kullanmak zorunda ...
Ab Irato

4

Burada birçok iyi cevap var, ama yine de bunu basename kullanarak paylaşmak istiyorum :

 basename $(echo "a:b:c:d:e" | tr ':' '/')

Ancak dizenizde zaten '/' varsa başarısız olur . Eğer eğik çizgi / sınırlayıcınızsa, sadece basename kullanmanız gerekir (ve kullanmalısınız).

Bu en iyi yanıt değil, ancak bash komutlarını kullanarak nasıl yaratıcı olabileceğinizi gösteriyor.


2

Bash kullanma.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0

echo ${!#}Bunun yerine kullanılmış olabilirdi eval echo \$${#}.
Rafa

2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

İlk kullanım xargs ":" kullanarak bölünür, - n1 her satırın sadece bir parçası olduğu anlamına gelir.


1
for x in `echo $str | tr ";" "\n"`; do echo $x; done

2
Alanların herhangi birinde boşluk varsa bu sorunla karşılaşır. Ayrıca, son alanın alınması sorusunu doğrudan ele almaz .
chepner


1

Basit bir çözüm giriş dizesinin sırasını tersine çevirmek olsa da cevapla biraz geç olabilir. Bu, uzunluğu ne olursa olsun her zaman son öğeyi elde etmenizi sağlar.

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

Bununla birlikte, bu yöntemi kullanırken ve sayılar 1 basamaktan büyükse (veya her durumda bir karakterden büyükse), borulu çıkış üzerinde başka bir 'rev' komutu çalıştırmanız gerekeceğini unutmayın.

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

Umarım yardımcı olabilirim, şerefe


1

Read yerleşimini kullanan bir çözüm:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

Veya, daha genel hale getirmek için:

echo "${fields[-1]}" # prints the last item

0

Python'u seviyorsanız ve bir paket kurma seçeneğiniz varsa, bu python yardımcı programını kullanabilirsiniz .

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

python bunu doğrudan yapabilir: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB

@MortenB Yanılıyorsunuz. Tüm amacıpythonpPaketinpython -c daha az karakter yazımıyla aynı şeyleri yapmanızı sağlamaktır. Lütfen depodaki README'ye bir göz atın.
bombaları

0

Regex eşleşmesi sedaçgözlüdür (her zaman en son meydana gelir), burada avantajınıza göre kullanabilirsiniz:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
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.