Cara menemukan bidang terakhir menggunakan 'potong'
310
Tanpa menggunakan sedatau awk, hanyacut , bagaimana cara mendapatkan bidang terakhir ketika jumlah bidang tidak diketahui atau berubah dengan setiap baris?
@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
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.
@ 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, revadalah 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:
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)
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.
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.
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:
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:
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;dowhile[["$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.
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;
cut
perintah :)? mengapa tidak ada perintah Linux lainnya?sed
atauawk
:perl -pe 's/^.+\s+([^\s]+)$/$1/'
.cut
mendukung sesuatu yang tidak. Tapi saya pikir itu berguna, karena memaksa pembaca untuk mempertimbangkan kode yang lebih mudah diikuti. Aku ingin cepat, cara sederhana untuk digunakancut
tanpa perlu menggunakan beberapa sintaks untukawk
,grep
,sed
, dllrev
hal 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.find | cut -d. -f<last>
juga kecenderungan alamiJawaban:
Anda dapat mencoba sesuatu seperti ini:
Penjelasan
rev
membalikkan "maps.google.com" menjadimoc.elgoog.spam
cut
menggunakan titik (yaitu '.') sebagai pembatas, dan memilih bidang pertama, yaitumoc
com
sumber
cut
tetapi tanpased
atauawk
. Jadi apa yang dipikirkan OP?rev
sesudahnya.rev
ideal ganda hebat!Gunakan ekspansi parameter. Ini jauh lebih efisien daripada perintah eksternal apa pun,
cut
(ataugrep
) disertakan.Lihat BashFAQ # 100 untuk pengantar manipulasi string asli di bash.
sumber
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.)bash
skrip (dengan asumsi Anda sudah menggunakanbash
skrip). Tidak perlu menyebut apa pun eksternal.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 sepertirev
itu, selalu tersedia pada shell yang sesuai.Tidak mungkin menggunakan adil
cut
. Berikut cara menggunakangrep
:Ganti koma untuk pembatas lainnya.
sumber
grep -o '^.*,'
rev
menambahkan masalah karakter unicode multibyte dalam kasus saya.sed 's/^.*,//'
yang menggantikan semua karakter hingga dan termasuk koma terakhir dengan string kosong.Tanpa awk? ... Tapi begitu sederhana dengan awk:
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)
sumber
cut
untuk mencapai hasil akhir OP seperti menggunakan sendok untuk "memotong" steak (pun intended :)).awk
adalah pisau steak.echo
karena dapat memperlambat skrip untuk menggunakan file yang panjangawk -F. '{print $NF}' <<< 'maps.google.com'
.Ada beberapa cara. Anda dapat menggunakan ini juga.
Jelas, input spasi kosong untuk perintah tr harus diganti dengan pembatas yang Anda butuhkan.
sumber
Ini adalah satu-satunya solusi yang mungkin untuk tidak menggunakan apa pun selain memotong:
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.
sumber
cut -f2-
dalam satu lingkaran sampai output tidak lagi berubah.Jika string input Anda tidak mengandung garis miring, maka Anda dapat menggunakan
basename
dan subkulit:Ini tidak menggunakan
sed
atauawk
tetapi juga tidak menggunakancut
, 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:sumber
berikut ini mengimplementasikan saran seorang teman
sumber
echo
agar ini dapat bekerja dengan andal dan kuat. Lihat stackoverflow.com/questions/10067266/…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
sumber
Menambahkan pendekatan ke pertanyaan lama ini hanya untuk bersenang-senang:
Selain bash, hanya potongan yang digunakan. Yah, dan gema, kurasa.
sumber
while read -r line; do echo ${line/*;}; done <input.file
menghasilkan hasil yang sama.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;
sumber
ans="a, b, c"
menghasilkanb
, yang tidak memenuhi persyaratan "jumlah bidang tidak diketahui atau berubah dengan setiap baris" .