Cara membagi string di shell dan mendapatkan bidang terakhir

293

Misalkan saya memiliki string 1:2:3:4:5dan saya ingin mendapatkan bidang terakhirnya ( 5dalam hal ini). Bagaimana saya melakukannya menggunakan Bash? Saya sudah mencoba cut, tetapi saya tidak tahu bagaimana menentukan bidang terakhir -f.

cd1
sumber

Jawaban:

430

Anda dapat menggunakan operator string :

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

Ini memangkas segala sesuatu dari depan sampai ':', rakus.

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }
Stephen
sumber
7
Meskipun ini berfungsi untuk masalah yang diberikan, jawaban William di bawah ini ( stackoverflow.com/a/3163857/520162 ) juga kembali 5jika stringnya 1:2:3:4:5:(saat menggunakan operator string menghasilkan hasil kosong). Ini sangat berguna ketika parsing path yang bisa berisi (atau tidak) /karakter finishing .
eckes
9
Lalu bagaimana Anda melakukan kebalikan dari ini? menggema '1: 2: 3: 4:'?
Dobz
12
Dan bagaimana cara menjaga bagian sebelum pemisah terakhir? Ternyata dengan menggunakan ${foo%:*}. #- dari awal; %- dari ujung. #, %- pertandingan terpendek; ##, %%- pertandingan terlama.
Mihai Danila
1
Jika saya ingin mendapatkan elemen terakhir dari jalur, bagaimana saya harus menggunakannya? echo ${pwd##*/}tidak bekerja.
Putnik
2
@ Patnik yang dilihat perintah pwdsebagai variabel. Coba dir=$(pwd); echo ${dir##*/}. Bekerja untukku!
Stan Strum
344

Cara lain adalah membalik sebelum dan sesudah cut:

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

Ini membuatnya sangat mudah untuk mendapatkan yang terakhir kecuali satu bidang, atau rentang bidang apa pun yang dinomori dari akhir.

a3nm
sumber
17
Jawaban ini bagus karena menggunakan 'cut', yang penulis (mungkin) sudah tidak asing lagi. Plus, saya suka jawaban ini karena saya menggunakan 'cut' dan memiliki pertanyaan yang tepat ini, karenanya menemukan utas ini melalui pencarian.
Dannid
6
Beberapa potong-dan-tempel pakan untuk orang yang menggunakan spasi sebagai pembatas:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll
2
rev | cut -d -f1 | rev sangat pintar! Terima kasih! Membantu saya banyak (use case saya adalah rev | -d '' -f 2- | rev
EdgeCaseBerg
1
Saya selalu lupa rev, hanya apa yang saya butuhkan! cut -b20- | rev | cut -b10- | rev
shearn89
Saya berakhir dengan solusi ini, usaha saya memotong jalur file dengan "awk -F" / "'{print $ NF}'" agak rusak bagi saya, karena nama file termasuk spasi putih juga terpotong
THX
76

Sulit untuk mendapatkan bidang terakhir menggunakan potongan, tetapi berikut adalah beberapa solusi dalam awk dan perl

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'
William Pursell
sumber
5
keuntungan besar dari solusi ini daripada jawaban yang diterima: itu juga cocok dengan jalur yang mengandung atau tidak mengandung /karakter finishing : /a/b/c/ddan /a/b/c/d/menghasilkan hasil yang sama ( d) saat memproses pwd | awk -F/ '{print $NF}'. Jawaban yang diterima menghasilkan hasil kosong dalam kasus/a/b/c/d/
eckes
@ cek Jika ada solusi AWK, pada GNU bash, versi 4.3.48 (1) - rilis itu tidak benar, karena itu penting setiap kali Anda mengikuti garis miring atau tidak. Sederhananya AWK akan digunakan /sebagai pembatas, dan jika path Anda /my/path/dir/itu akan menggunakan nilai setelah pembatas terakhir, yang hanya berupa string kosong. Jadi yang terbaik adalah menghindari trailing slash jika Anda perlu melakukan hal seperti yang saya lakukan.
stamster
Bagaimana saya mendapatkan substring SAMPAI bidang terakhir?
blackjacx
1
@blackjacx Ada beberapa kebiasaan, tetapi sesuatu seperti awk '{$NF=""; print $0}' FS=: OFS=:sering bekerja dengan cukup baik.
William Pursell
29

Dengan asumsi penggunaan yang cukup sederhana (misalnya, tidak ada pembatas pembatas), Anda dapat menggunakan grep:

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

Breakdown - temukan semua karakter bukan pembatas ([^:]) di akhir baris ($). -o hanya mencetak bagian yang cocok.

Nicholas MT Elliott
sumber
-E berarti menggunakan sintaks yang diperluas; [^ ...] berarti apa pun selain karakter yang terdaftar; + satu atau lebih hits seperti itu (akan mengambil panjang maksimum yang mungkin untuk pola; item ini adalah ekstensi gnu) - untuk contoh char yang memisahkan adalah titik dua.
Alexander Stohr
18

Satu arah:

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

Lain, menggunakan array:

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

Lain lagi dengan array:

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

Menggunakan Bash (versi> = 3.2) ekspresi reguler:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}
Dijeda sampai pemberitahuan lebih lanjut.
sumber
11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Cukup terjemahkan pembatas ke baris baru dan pilih entri terakhir tail -1.

pengguna3133260
sumber
Ini akan gagal jika item terakhir berisi a \n, tetapi untuk sebagian besar kasus adalah solusi yang paling mudah dibaca.
Yajo
7

Menggunakan 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
Rafael
sumber
4

Jika bidang terakhir Anda adalah karakter tunggal, Anda bisa melakukan ini:

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

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

Periksa manipulasi string dalam bash .

Ab Irato
sumber
Ini tidak bekerja: memberikan yang terakhir karakter dari a, bukan yang terakhir bidang .
gniourf_gniourf
1
Benar, itu idenya, jika Anda tahu panjang bidang terakhir itu bagus. Jika tidak, Anda harus menggunakan sesuatu yang lain ...
Ab Irato
4

Ada banyak jawaban bagus di sini, tetapi saya masih ingin membagikan yang ini dengan menggunakan nama kecil :

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

Namun itu akan gagal jika sudah ada beberapa '/' di string Anda . Jika slash / adalah pembatas Anda maka Anda hanya perlu (dan harus) menggunakan basename.

Ini bukan jawaban terbaik tetapi itu hanya menunjukkan bagaimana Anda bisa kreatif menggunakan perintah bash.

021
sumber
2

Menggunakan Bash.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0
ghostdog74
sumber
Bisa digunakan echo ${!#}sebagai ganti eval echo \$${#}.
Rafa
2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

Pertama, gunakan xargs, pisahkan dengan menggunakan ":", - n1 berarti setiap baris hanya memiliki satu bagian. Kemudian, pring bagian terakhir.

Crytis
sumber
1
for x in `echo $str | tr ";" "\n"`; do echo $x; done
Nahid Akbar
sumber
2
Ini mengalami masalah jika ada spasi di salah satu bidang. Juga, itu tidak secara langsung menjawab pertanyaan tentang mengambil bidang terakhir .
chepner
1

Bagi mereka yang nyaman dengan Python, https://github.com/Russell91/pythonpy adalah pilihan yang bagus untuk menyelesaikan masalah ini.

$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'

Dari pythonpy bantuan: -x treat each row of stdin as x.

Dengan alat itu, mudah untuk menulis kode python yang diterapkan pada input.

Christoph Böddeker
sumber
1

Mungkin agak terlambat dengan jawabannya meskipun solusi sederhana adalah membalik urutan string input. Ini akan memungkinkan Anda untuk selalu mendapatkan item terakhir tidak peduli panjangnya.

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

Penting untuk dicatat, jika menggunakan metode ini dan angkanya lebih besar dari 1 digit (Atau lebih besar dari satu karakter dalam keadaan apa pun), Anda perlu menjalankan perintah 'rev' lain di atas keluaran yang disalurkan.

[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

Semoga saya bisa membantu, tepuk tangan

Chris
sumber
1

Solusi menggunakan read builtin:

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

Atau, untuk membuatnya lebih umum:

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

Jika Anda suka python dan memiliki opsi untuk menginstal paket, Anda dapat menggunakan utilitas python ini .

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5
bom
sumber
python dapat melakukan ini secara langsung: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB
@MortenB Anda salah. Seluruh tujuan pythonppaket adalah untuk membuat Anda melakukan hal yang sama python -cdengan mengetik karakter lebih sedikit. Silakan lihat README di repositori.
bom
0

Pencocokan Regex sedadalah serakah (selalu berlaku untuk kejadian terakhir), yang dapat Anda gunakan untuk keuntungan Anda di sini:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
cair
sumber