Bagaimana menemukan posisi karakter menggunakan grep?

11

Saya perlu mengidentifikasi posisi karakter dalam string menggunakan perintah grep.

Contoh, stringnya adalah RAMSITALSKHMAN|1223333.

grep -n '[^a-zA-Z0-9\$\~\%\#\^]'

Bagaimana cara menemukan posisi |dalam string yang diberikan?

pengguna82782
sumber
itu harus dengan grep?
Braiam

Jawaban:

29

Anda dapat menggunakan -buntuk mendapatkan byte offset, yang sama dengan posisi untuk teks sederhana (tetapi tidak untuk UTF-8 atau serupa).

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|'
14:|

Di atas, saya menggunakan -asaklar untuk memberi tahu grep untuk menggunakan input sebagai teks; diperlukan saat beroperasi pada file biner, dan -osakelar untuk hanya menampilkan karakter yang cocok.

Jika Anda hanya menginginkan posisi, Anda dapat menggunakan grep untuk mengekstrak hanya posisi:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' | grep -oE '[0-9]+'
14

Jika Anda mendapatkan output aneh, periksa untuk melihat apakah grep memiliki warna yang diaktifkan. Anda dapat menonaktifkan warna dengan meneruskan --colors=neverke grep, atau dengan mengawali perintah grep dengan \(yang akan menonaktifkan alias apa pun), misalnya:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' --color=never | \grep -oE '^[0-9]+'
14

Untuk string yang mengembalikan banyak kecocokan, sambungkan head -n1untuk mendapatkan kecocokan pertama.

Perhatikan bahwa saya menggunakan keduanya di atas, dan perhatikan bahwa yang terakhir tidak akan bekerja jika grep "alias" melalui executable (skrip atau sebaliknya), hanya ketika menggunakan alias.

runejuhl
sumber
3
Sekarang cari 2;)
Izkata
Terima kasih @Izkata, kamu benar. Saya telah memperbarui posting saya sedikit dan menambahkan topi yang hilang ^:)
runejuhl
1
Versi grep mana yang Anda gunakan? Saya mendapatkan 0:|output-- karena 0 adalah posisi byte dari awal baris di mana |ditemukan.
Alex
@ Alex GNU grep dari Debian peregangan: grep (GNU grep) 2.27. Apakah Anda mungkin menggunakan OS X?
runejuhl
11

Mencoba:

printf '%s\n' 'RAMSITALSKHMAN|1223333.' | grep -o . | grep -n '|'

keluaran:

15:|

Ini akan memberi Anda posisi dengan indeks berbasis-1.

cuonglm
sumber
Ini tidak berfungsi :(
user82782
1
@ user82782: Perintah apa yang Anda jalankan? Bagaimana Anda tahu itu tidak berhasil?
cuonglm
printf '%s\n' '|' | grep -o . | grep -n '|'cetakan 1, tidak 0seperti yang diharapkan.
l0b0
1
@ l0b0: OP tidak memberi tahu dia menginginkan basis indeks 0 atau 1.
cuonglm
Maksud saya apa yang diharapkan oleh pengembang perangkat lunak.
l0b0
8

Jika Anda menggunakan shell, Anda dapat menggunakan operasi yang murni bawaan tanpa perlu memunculkan proses eksternal seperti atau :

$ str="RAMSITALSKHMAN|1223333"
$ tmp="${str%%|*}"
$ if [ "$tmp" != "$str" ]; then
> echo ${#tmp}
> fi
14
$ 

Ini menggunakan ekspansi parameter untuk menghapus semua kemunculan |diikuti oleh string apa pun dan menyimpannya dalam variabel sementara. Maka itu hanya masalah mengukur panjang variabel sementara untuk mendapatkan indeks |.

Perhatikan ifmemeriksa apakah |ada sama sekali dalam string asli. Jika tidak maka variabel sementara akan sama dengan yang asli.

Perhatikan juga ini memberikan indeks berbasis nol |yang umumnya berguna saat mengindeks string bash. Namun jika Anda memerlukan indeks berbasis satu, maka Anda dapat melakukan ini:

$ echo $((${#tmp}+1))
15
$ 
Trauma Digital
sumber
1
mungkin jawaban terbaik, sintaks ini indah dan sangat cepat dan mudah digunakan ketika Anda memahami maknanya,
berumur
4

Anda dapat menggunakan indexfungsi awk untuk mengembalikan posisi dalam karakter di mana pertandingan terjadi:

echo "RAMSITALSKHMAN|1223333"|awk 'END{print index($0,"|")}'
15

Jika Anda tidak keberatan menggunakan fungsi Perl index, ini menangani pelaporan nol, satu atau lebih kemunculan karakter:

echo "|abc|xyz|123456|zzz|" | \
perl -nle '$pos=-1;while (($off=index($_,"|",$pos))>=0) {print $off;$pos=$off+1}'

Untuk keterbacaan, hanya, pipa telah dibagi menjadi dua garis.

Selama karakter target ditemukan, indexmengembalikan nilai positif berdasarkan nol (0). Karenanya, string "abc | xyz | 123456 | zzz |" ketika parsed mengembalikan posisi 0, 4, 8, 15 dan 19.

JRFerguson
sumber
untuk penggunaan ini, awk lebih bermanfaat / mudah daripada grep.
Archemar
Ini hanya mencetak posisi pertama, tidak akan berfungsi dengan string sepertiRAMSITALSKHMAN|1|223333
cuonglm
3

Kami juga dapat melakukannya menggunakan "expr match" atau "expr index"

expr cocokkan $ string $ substring di mana $ substring adalah RE.

echo `expr match "RAMSITALSKHMAN|1223333" '[A-Z]*.|'`

Dan di atas akan memberi Anda posisi karena mengembalikan panjang substring yang cocok.

Tetapi untuk lebih spesifik untuk indeks pencarian:

mystring="RAMSITALSKHMAN|122333"
echo `expr index "$mystring" '|'`
bluefoggy
sumber
Saya tidak memiliki reputasi yang cukup untuk berkomentar di tempat lain. Saya pribadi suka jawaban yang diberikan oleh @Gnouc. Namun mengapa menggunakan awk dan membuatnya rumit ketika kita dapat melakukan hal-hal sederhana menggunakan 'expr'
bluefoggy
@ kingdeb itu hanya saran.
Avinash Raj
@kingsdeb: Karena (1) awksolusi sepele dapat dimodifikasi untuk melaporkan informasi ini pada setiap baris file (yang harus Anda lakukan adalah menghapus END, yang tidak pernah benar-benar diperlukan, dari jawaban JRFerguson, dan Avinash Raj sudah melakukannya) ; sedangkan, untuk melakukan itu dengan exprsolusinya, Anda perlu menambahkan loop eksplisit (dan jawaban Gnouc tidak mudah diadaptasi untuk melakukan itu sama sekali, yang bisa saya lihat), dan (2) awksolusi dapat disesuaikan untuk melaporkan semua cocok di setiap baris agak lebih mudah daripada exprsolusi (pada kenyataannya, Avinash Raj sudah melakukannya juga).
G-Man Mengatakan 'Reinstate Monica'
Mengapa Anda gunakan di echo `...`sini?
Stéphane Chazelas
Ini hanya untuk menunjukkan output di sini
bluefoggy
2

Perintah awk lain ,

$ echo 'RAMSITALSKHMAN|1223333'| awk 'BEGIN{ FS = "" }{for(i=1;i<=NF;i++){if($i=="|"){print i;}}}'
15

Dengan mengatur pemisah bidang sebagai string nol, awk mengubah karakter individu dalam catatan sebagai bidang yang terpisah.

Avinash Raj
sumber
2

beberapa alternatif termasuk:

mirip dengan jawaban Gnouc, tetapi dengan shell:

echo 'RAMSITALSKHMAN|1223333' |
tr -c \| \\n | 
sh

sh: line 15: syntax error near unexpected token `|
sh: line 15: `|'

dengan seddan dcmungkin menjangkau beberapa baris:

echo 'RAMSITALSKHMAN|1223333' |
sed 's/[^|]/1+/g;s/|/p/;1i0 1+' |dc

15

dengan $IFS...

IFS=\|; set -f; set -- ${0+RAMSITALSKHMAN|1223333}; echo $((${#1}+1))

Itu juga akan memberi tahu Anda ada berapa banyak seperti ...

echo $(($#-1))
mikeserv
sumber