Grep kullanarak bir karakterin konumu nasıl bulunur?


11

Ben grep komutunu kullanarak dizede bir karakterin konumunu tanımlamak gerekir.

Örneğin, dize RAMSITALSKHMAN|1223333.

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

|Verilen dizede konumunu nasıl bulabilirim ?


grep ile mi olmalı?
Braiam

Yanıtlar:


29

-bBasit metin konumu ile aynı olan bayt uzaklığını elde etmek için kullanabilirsiniz (ancak UTF-8 veya benzeri için değil).

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

Yukarıda, -agrep'e girişi metin olarak kullanmasını söylemek için anahtarı kullanıyorum; ikili dosyalar üzerinde çalışırken ve -oyalnızca eşleşen karakter (ler) in çıkış anahtarı kullanılır.

Yalnızca konumu istiyorsanız, grep komutunu yalnızca konumu çıkarmak için kullanabilirsiniz:

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

Tuhaf bir çıktı alırsanız, grep'in renklerin etkin olup olmadığını kontrol edin. --colors=neverGrep'e ileterek veya grep komutunun önüne bir ön ek uygulayarak \(diğer adları devre dışı bırakacak) renkleri devre dışı bırakabilirsiniz, örneğin:

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

Birden çok eşleşme döndüren bir dize head -n1için, ilk eşleşmeyi almak üzere geçiş yapın.

Her ikisini de yukarıda kullandığımı ve grep yalnızca bir takma ad (komut dosyası veya başka bir yolla), yalnızca takma adlar kullanarak "diğer adı" ise çalışmadığını unutmayın.


3
Şimdi arayın 2;)
Izkata

Teşekkürler @Izkata, haklısın. ^
Yazımı

1
Hangi grep sürümünü kullandınız? Ben olsun 0:|0 satırın başlangıcına bayt pozisyonu olduğu için output-- olarak |bulunmuştur.
Alex,

Debian streç @Alex GNU grep: grep (GNU grep) 2.27. Belki OS X mi kullanıyorsunuz?
runejuhl

11

Deneyin:

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

çıktı:

15:|

Bu size dizin tabanlı-1 olan konumu verecektir.



1
@ user82782: Hangi komutu çalıştırdınız? İşe yaramadığını nereden biliyorsun?
cuonglm

printf '%s\n' '|' | grep -o . | grep -n '|'baskılar 1, değil 0beklendiği gibi.
l0b0

1
@ l0b0: OP, endeks tabanı 0 veya 1'i istediğini söylemiyor
cuonglm

Sadece bir yazılım geliştiricisinin ne beklediğini kastediyorum.
l0b0

8

kabuğunu kullanıyorsanız, veya gibi harici süreçleri ortaya çıkarmaya gerek kalmadan tamamen yerleşik işlemleri kullanabilirsiniz :

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

Bu, herhangi bir dizeyle aşağıdaki tüm oluşumları kaldırmak ve bunu geçici bir değişkene kaydetmek için bir parametre genişletmesi kullanır |. Bu sadece endeksi elde etmek için geçici değişkenin uzunluğunu ölçmektir |.

Dikkat ifedin,| orijinal dizede hiç yoktur. Eğer değilse, geçici değişken orjinal ile aynı olacaktır.

Bu ayrıca |, bash dizelerini dizine eklerken genellikle yararlı olan sıfır temelli dizini de sağlar . Ancak, tek tabanlı dizine ihtiyacınız varsa, bunu yapabilirsiniz:

$ echo $((${#tmp}+1))
15
$ 

1
muhtemelen en iyi cevap, bu sözdizimi güzel ve anlamını anladığınızda çok hızlı ve kullanımı kolaydır, çekirdeğe çok yaşa
vdegenne

4

Eşleşmenin indexgerçekleştiği karakterlerde konumu döndürmek için awk işlevini kullanabilirsiniz :

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

Perl indexişlevini kullanmanın bir sakıncası yoksa , bu, bir karakterin sıfır, bir veya daha fazla tekrarını bildirmeyi işler:

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

Sadece okunabilirlik için, boru hattı iki hatta ayrılmıştır.

Hedef karakter bulunduğu sürece index, sıfır (0) temelinde pozitif bir değer döndürür. Bu nedenle, "abc | xyz | 123456 | zzz |" dizesi ayrıştırıldığında 0, 4, 8, 15 ve 19 konumlarını döndürür.


Bu kullanım için awk, grep'ten daha yararlı / kolaydır.
Archemar

Bu yalnızca gibi dize ile çalışmaz, ilk pozisyonunu yazdırmakRAMSITALSKHMAN|1|223333
cuonglm

3

Bunu "ifade eşleşmesi" veya "ifade dizini" kullanarak da yapabiliriz

expr $ string $ substring ile eşleşir; burada $ substring bir RE'dir.

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

Ve yukarıda size pozisyonu verecektir, çünkü eşleşen alt dizenin uzunluğunu döndürür.

Ancak arama dizini için daha spesifik olmak gerekirse:

mystring="RAMSITALSKHMAN|122333"
echo `expr index "$mystring" '|'`

Başka bir yere yorum yapmak için yeterli itibarım yok. Şahsen @Gnouc tarafından verilen cevabı beğendim. Ancak neden 'expr' kullanarak basit şeyler yapabildiğimizde awk kullanmak ve karmaşık hale getirmek
bluefoggy

@kingsdeb bu sadece bir öneri.
Avinash Raj

@kingsdeb: Çünkü (1) awkçözümler bir dosyanın her satırında bu bilgileri rapor etmek için önemsiz bir şekilde değiştirilebilir (tek yapmanız ENDgereken, gerçekten gerekli olmayan, JRFerguson'un cevabından kaldırmaktır ve Avinash Raj'ın zaten yaptığı) ; bununla birlikte, bunu exprçözümle yapmak için, açık bir döngü eklemeniz gerekir (ve Gnouc'un cevabı bunu yapabilmek için kolayca uyarlanamaz, görebiliyorum) ve (2) awkçözümler tüm raporları bildirmek için uyarlanabilir. her satırdaki exprçözümden biraz daha kolay eşleşir (aslında, Avinash Raj'ın bunu zaten yaptığı gibi).
G-Man 'Sepet Monica'yı

Neden echo `...`burada kullanasýn ki ?
Stéphane Chazelas

Bu sadece çıktı burada göstermek için
bluefoggy

2

Başka bir awk komutu ,

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

Alan ayırıcısını boş dize olarak ayarlayarak awk, kayıttaki tek tek karakteri ayrı alanlar olarak döndürür.


2

bazı alternatifler:

Gnouc'un cevabına benzer, ancak kabukla:

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

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

birden çok satıra sahip sedve dcmuhtemelen birden fazla satır içerebilir:

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

15

ile $IFS...

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

Bu aynı zamanda kaç tane var olduğunu da söyleyecektir ...

echo $(($#-1))
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.