Cara menemukan bidang terakhir menggunakan 'potong'

310

Tanpa menggunakan sedatau awk, hanya cut , bagaimana cara mendapatkan bidang terakhir ketika jumlah bidang tidak diketahui atau berubah dengan setiap baris?

noobcoder
sumber
8
Apakah Anda jatuh cinta dengan cutperintah :)? mengapa tidak ada perintah Linux lainnya?
Jayesh Bhoi
7
Tanpa sedatau awk: perl -pe 's/^.+\s+([^\s]+)$/$1/'.
jordanm
3
kemungkinan duplikat tentang cara memisahkan string dalam shell dan mendapatkan bidang terakhir
Amir Ali Akbari
4
@MestreLion Sering kali orang membaca pertanyaan untuk menemukan solusi untuk variasi masalah. Yang ini dimulai dengan premis palsu yang cutmendukung sesuatu yang tidak. Tapi saya pikir itu berguna, karena memaksa pembaca untuk mempertimbangkan kode yang lebih mudah diikuti. Aku ingin cepat, cara sederhana untuk digunakan cuttanpa perlu menggunakan beberapa sintaks untuk awk, grep, sed, dll revhal melakukan trik; sangat elegan, dan sesuatu yang tidak pernah saya pertimbangkan (walaupun kikuk untuk situasi lain). Saya juga suka membaca pendekatan lain dari jawaban lain.
Beejor
3
Datang ke sini masalah kehidupan nyata: Saya ingin mencari semua ekstensi file yang berbeda di pohon sumber, untuk memperbarui file .gitattributes. Begitu find | cut -d. -f<last>juga kecenderungan alami
pejantan

Jawaban:

680

Anda dapat mencoba sesuatu seperti ini:

echo 'maps.google.com' | rev | cut -d'.' -f 1 | rev

Penjelasan

  • rev membalikkan "maps.google.com" menjadi moc.elgoog.spam
  • cut menggunakan titik (yaitu '.') sebagai pembatas, dan memilih bidang pertama, yaitu moc
  • terakhir, kami balikkan lagi untuk mendapatkan com
zedfoxus
sumber
6
Itu tidak hanya menggunakan cuttetapi tanpa sedatau awk. Jadi apa yang dipikirkan OP?
Jayesh Bhoi
7
@tom OP telah mengajukan lebih banyak pertanyaan dari ini dalam beberapa jam terakhir. Berdasarkan interaksi kami dengan OP, kami tahu bahwa awk / sed / etc. tidak diizinkan dalam pekerjaan rumahnya, tetapi referensi ke rev belum dibuat. Jadi itu layak dicoba
zedfoxus
4
@ zfus saya mengerti. Mungkin ingin menempel yang lain revsesudahnya.
tom
17
revideal ganda hebat!
Ford Guo
6
Luar biasa, sederhana, sempurna, terima kasih untuk penjelasannya juga - tidak cukup banyak orang yang menjelaskan setiap langkah dalam rantai panjang perintah pipa
Pete
128

Gunakan ekspansi parameter. Ini jauh lebih efisien daripada perintah eksternal apa pun, cut(atau grep) disertakan.

data=foo,bar,baz,qux
last=${data##*,}

Lihat BashFAQ # 100 untuk pengantar manipulasi string asli di bash.

Charles Duffy
sumber
3
@ ErwinWessels: Karena bash sangat lambat. Gunakan bash untuk menjalankan jaringan pipa, bukan untuk memproses data secara massal. Maksud saya, ini bagus jika Anda memiliki satu baris teks yang sudah ada dalam variabel shell, atau jika Anda ingin melakukannya while IFS= read -ra array_var; do :;done <(cmd)untuk memproses beberapa baris. Tetapi untuk file besar, rev | cut | rev mungkin lebih cepat! (Dan tentu saja awk akan lebih cepat dari itu.)
Peter Cordes
2
@PeterCordes, awk akan lebih cepat untuk file besar, tentu saja, tetapi dibutuhkan sedikit input untuk mengatasi biaya startup faktor-konstan. (Ada juga cangkang - seperti ksh93 - dengan kinerja lebih dekat ke awk, di mana sintaks yang diberikan dalam jawaban ini tetap valid; bash sangat lamban, tetapi bahkan tidak dekat dengan satu-satunya opsi yang tersedia).
Charles Duffy
1
Terima kasih @PeterCordes; seperti biasa saya kira setiap alat memiliki kasus penggunaannya.
Erwin Wessels
1
Sejauh ini, ini adalah cara tercepat dan paling ringkas untuk memangkas satu variabel di dalam bashskrip (dengan asumsi Anda sudah menggunakan bashskrip). Tidak perlu menyebut apa pun eksternal.
Ken Sharp
1
@Balmipour, ... Namun, rev adalah khusus untuk OS Anda menggunakan apapun yang menyediakan itu - itu tidak standar di semua sistem UNIX. Lihat daftar bab untuk bagian POSIX pada perintah dan utilitas - tidak ada di sana. Dan ${var##prefix_pattern}sebenarnya bukan bash-specific; itu ada dalam standar sh POSIX , lihat bagian akhir 2.6.2 (ditautkan), jadi tidak seperti revitu, selalu tersedia pada shell yang sesuai.
Charles Duffy
89

Tidak mungkin menggunakan adil cut. Berikut cara menggunakan grep:

grep -o '[^,]*$'

Ganti koma untuk pembatas lainnya.

tom
sumber
3
Untuk melakukan yang sebaliknya, dan menemukan segala sesuatu kecuali bidang terakhir lakukan:grep -o '^.*,'
Ariel
2
Ini sangat berguna, karena revmenambahkan masalah karakter unicode multibyte dalam kasus saya.
Brice
3
Saya mencoba melakukan ini pada MinGW tetapi versi grep saya tidak mendukung -o, jadi saya menggunakan sed 's/^.*,//'yang menggantikan semua karakter hingga dan termasuk koma terakhir dengan string kosong.
TamaMcGlinn
46

Tanpa awk? ... Tapi begitu sederhana dengan awk:

echo 'maps.google.com' | awk -F. '{print $NF}'

AWK adalah alat yang jauh lebih kuat untuk ada di saku Anda. -F jika untuk pemisah bidang NF adalah jumlah bidang (juga merupakan singkatan dari indeks terakhir)

Amir Mehler
sumber
2
Ini universal dan berfungsi persis seperti yang diharapkan setiap saat. Dalam skenario ini, menggunakan cutuntuk mencapai hasil akhir OP seperti menggunakan sendok untuk "memotong" steak (pun intended :)). awkadalah pisau steak.
Hickory420
3
Hindari penggunaan yang tidak perlu echokarena dapat memperlambat skrip untuk menggunakan file yang panjang awk -F. '{print $NF}' <<< 'maps.google.com'.
Anil_M
14

Ada beberapa cara. Anda dapat menggunakan ini juga.

echo "Your string here"| tr ' ' '\n' | tail -n1
> here

Jelas, input spasi kosong untuk perintah tr harus diganti dengan pembatas yang Anda butuhkan.

rjni
sumber
Terima kasih! sesuatu yang bekerja di busybox sh 1.0.0 :)
kevinf
1
Ini terasa seperti jawaban paling sederhana bagi saya, lebih sedikit pipa dan makna yang lebih jelas
joeButler
1
Itu tidak akan berfungsi untuk seluruh file, yang mungkin dimaksudkan OP.
Amir
7

Ini adalah satu-satunya solusi yang mungkin untuk tidak menggunakan apa pun selain memotong:

gema "string" | cut -d '.' -f2- [repeat_following_part_forever_or_until_out_of_memory:] | cut -d '.' -f2-

Dengan menggunakan solusi ini, jumlah bidang memang bisa tidak diketahui dan bervariasi dari waktu ke waktu. Namun karena panjang baris tidak boleh melebihi LINE_MAX karakter atau bidang, termasuk karakter baris baru, maka jumlah bidang sewenang-wenang tidak akan pernah bisa menjadi bagian dari kondisi nyata solusi ini.

Ya, solusi yang sangat konyol tetapi satu-satunya yang memenuhi kriteria saya pikir.

Seorang teman
sumber
2
Bagus. Ambillah yang terakhir '.' off "string" dan ini berfungsi.
Matt
2
Saya suka ketika semua orang mengatakan sesuatu tidak mungkin dan kemudian seseorang berdentang dengan jawaban yang berhasil. Bahkan jika itu memang sangat konyol.
Beejor
Satu bisa mengulangi cut -f2-dalam satu lingkaran sampai output tidak lagi berubah.
loa_in_
4

Jika string input Anda tidak mengandung garis miring, maka Anda dapat menggunakan basenamedan subkulit:

$ basename "$(echo 'maps.google.com' | tr '.' '/')"

Ini tidak menggunakan sedatau awktetapi juga tidak menggunakan cut, jadi saya tidak yakin apakah itu memenuhi syarat sebagai jawaban atas pertanyaan seperti kata-katanya.

Ini tidak berfungsi dengan baik jika memproses string input yang dapat berisi garis miring ke depan. Solusi untuk situasi itu adalah mengganti garis miring dengan karakter lain yang Anda tahu bukan bagian dari string input yang valid. Misalnya, karakter pipe ( |) juga tidak diperbolehkan dalam nama file, jadi ini akan berfungsi:

$ basename "$(echo 'maps.google.com/some/url/things' | tr '/' '|' | tr '.' '/')" | tr '|' '/'
jstine
sumber
2

berikut ini mengimplementasikan saran seorang teman

#!/bin/bash
rcut(){

  nu="$( echo $1 | cut -d"$DELIM" -f 2-  )"
  if [ "$nu" != "$1" ]
  then
    rcut "$nu"
  else
    echo "$nu"
  fi
}

$ export DELIM=.
$ rcut a.b.c.d
d
pengguna2166700
sumber
2
Anda perlu tanda kutip ganda di sekitar argumen echoagar ini dapat bekerja dengan andal dan kuat. Lihat stackoverflow.com/questions/10067266/…
tripleee
0

Jika Anda memiliki file bernama filelist.txt yang merupakan jalur daftar seperti berikut ini: c: /dir1/dir2/file1.h c: /dir1/dir2/dir3/file2.h

maka Anda dapat melakukan ini: rev filelist.txt | cut -d "/" -f1 | putaran

aperson1961
sumber
0

Menambahkan pendekatan ke pertanyaan lama ini hanya untuk bersenang-senang:

$ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info

$ cat tmp.sh # showing off the script to do the job
#!/bin/bash
delim=';'
while read -r line; do  
    while [[ "$line" =~ "$delim" ]]; do
        line=$(cut -d"$delim" -f 2- <<<"$line")
    done
    echo "$line"
done < input.file

$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info

Selain bash, hanya potongan yang digunakan. Yah, dan gema, kurasa.

Kaffe Myers
sumber
Meh, mengapa tidak menghapus potongan sepenuhnya dan hanya menggunakan bash ... x] while read -r line; do echo ${line/*;}; done <input.filemenghasilkan hasil yang sama.
Kaffe Myers
-1

Saya menyadari jika kita hanya memastikan pembatas trailing ada, itu berfungsi. Jadi dalam kasus saya, saya memiliki pembatas koma dan spasi putih. Saya menambahkan spasi di bagian akhir;

$ ans="a, b"
$ ans+=" "; echo ${ans} | tr ',' ' ' | tr -s ' ' | cut -d' ' -f2
b
AnneTheAgile
sumber
Dan ans="a, b, c"menghasilkan b, yang tidak memenuhi persyaratan "jumlah bidang tidak diketahui atau berubah dengan setiap baris" .
jww