Jak znaleźć pozycję postaci za pomocą grep?

11

Muszę zidentyfikować pozycję znaku w ciągu za pomocą polecenia grep.

Przykład: ciąg to RAMSITALSKHMAN|1223333.

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

Jak znaleźć pozycję |w danym ciągu?

użytkownik82782
źródło
to musi być z grep?
Braiam

Odpowiedzi:

29

Możesz użyć, -baby uzyskać przesunięcie bajtu, które jest takie samo jak pozycja dla prostego tekstu (ale nie dla UTF-8 lub podobnego).

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

Powyżej używam -aprzełącznika, aby powiedzieć grepowi, aby używał danych wejściowych jako tekstu; konieczne podczas pracy na plikach binarnych, a -oprzełącznik wyświetla tylko pasujące znaki.

Jeśli chcesz tylko pozycję, możesz użyć grep, aby wyodrębnić tylko pozycję:

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

Jeśli otrzymasz dziwny wynik, sprawdź, czy grep ma włączone kolory. Możesz wyłączyć kolory, przechodząc --colors=neverdo grep lub poprzedzając polecenie grep znakiem \(który wyłączy wszelkie aliasy), np .:

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

W przypadku ciągu, który zwraca wiele dopasowań, przeciągnij, head -n1aby uzyskać pierwsze dopasowanie.

Zauważ, że używam obu z powyższych, i zauważ, że ten ostatni nie będzie działał, jeśli grep jest „aliasowany” przez plik wykonywalny (skrypt lub w inny sposób), tylko przy użyciu aliasów.

runejuhl
źródło
3
Teraz szukaj 2;)
Izkata
Dzięki @Izkata, masz rację. Trochę zaktualizowałem swój post i dodałem brakujący kapelusz ^:)
runejuhl,
1
Z jakiej wersji grep korzystałeś? Dostaję 0:|jako wynik - ponieważ 0 to bajtowa pozycja początku wiersza, w którym się |znajduje.
Alex
@Alex GNU grep z odcinka wpisy: grep (GNU grep) 2.27. Być może używasz OS X?
runejuhl
11

Próbować:

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

wynik:

15:|

To da ci pozycję z indeksem opartym na 1.

Cuonglm
źródło
Nie działa :(
user82782,
1
@ user82782: Jakie polecenie wykonałeś? Skąd wiesz, że to nie zadziałało?
cuonglm
printf '%s\n' '|' | grep -o . | grep -n '|'wydruki 1niezgodne z 0oczekiwaniami.
l0b0
1
@ l0b0: OP nie mówi, że chce bazy 0 lub 1.
indeksu
Mam na myśli to, czego oczekiwałby programista.
l0b0
8

Jeśli używasz powłoki , możesz korzystać z czysto wbudowanych operacji bez potrzeby odradzania procesów zewnętrznych, takich jak lub :

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

Wykorzystuje to rozszerzenie parametrów, aby usunąć wszystkie wystąpienia |następujących po dowolnym ciągu i zapisać je w zmiennej tymczasowej. W takim przypadku wystarczy zmierzyć długość zmiennej tymczasowej, aby uzyskać indeks |.

Zauważ, że ifsprawdza, czy |w ogóle istnieje w oryginalnym ciągu. Jeśli tak nie jest, zmienna tymczasowa będzie taka sama jak pierwotna.

Zauważ też, że zapewnia to indeks zerowy, |który jest ogólnie przydatny podczas indeksowania ciągów bash. Jeśli jednak potrzebujesz indeksu opartego na jednym, możesz to zrobić:

$ echo $((${#tmp}+1))
15
$ 
Cyfrowa trauma
źródło
1
prawdopodobnie najlepsza odpowiedź, ta składnia jest piękna i tak szybka i łatwa w użyciu, gdy zrozumiesz jej znaczenie, niech żyje do samego rdzenia
vdegenne
4

Możesz użyć indexfunkcji awk, aby zwrócić pozycję w znakach, w których występuje dopasowanie:

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

Jeśli nie masz nic przeciwko użyciu funkcji Perla index, obsługuje to zgłaszanie zera, jednego lub więcej wystąpień znaku:

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

Tylko w celu zapewnienia czytelności potok został podzielony na dwie linie.

Dopóki znak docelowy zostanie znaleziony, indexzwraca wartość dodatnią w oparciu o zero (0). Stąd ciąg „abc | xyz | 123456 | zzz |” po przeanalizowaniu zwraca pozycje 0, 4, 8, 15 i 19.

JRFerguson
źródło
w tym celu awk jest bardziej użyteczny / łatwy niż grep.
Archemar
To tylko wydrukuje pierwszą pozycję, nie będzie działać z ciągiem jakRAMSITALSKHMAN|1|223333
cuonglm
3

Możemy to również zrobić za pomocą „dopasowania wyrażenia” lub „indeksu wyrażenia”

wyrażenie dopasowuje $ string $ substring, gdzie $ substring jest RE.

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

A powyżej da ci pozycję, ponieważ zwraca dopasowaną długość podłańcucha.

Ale aby być bardziej szczegółowym w wyszukiwaniu indeksu:

mystring="RAMSITALSKHMAN|122333"
echo `expr index "$mystring" '|'`
bluefoggy
źródło
Nie mam wystarczającej reputacji, aby komentować gdzie indziej. Osobiście podobała mi się odpowiedź udzielona przez @Gnouc. Jednak po co używać awk i
komplikować
@kingsdeb to tylko sugestia.
Avinash Raj
@kingsdeb: Ponieważ (1) awkrozwiązania można w trywialny sposób modyfikować w celu zgłaszania tych informacji w każdym wierszu pliku (wszystko, co musisz zrobić, to usunąć ENDodpowiedź JRFergusona, która nigdy nie była tak naprawdę konieczna, a Avinash Raj już to robi) ; mając na uwadze, że aby to zrobić za pomocą exprrozwiązania, należy dodać wyraźną pętlę (a odpowiedź Gnouca nie jest łatwa do dostosowania, aby to zrobić w ogóle, co widzę), oraz (2) awkrozwiązania można dostosować do zgłaszania wszystkich dopasowuje się w każdej linii nieco łatwiej niż exprrozwiązanie (w rzeczywistości Avinash Raj już to robi).
G-Man mówi „Reinstate Monica”
Dlaczego miałbyś echo `...`tu skorzystać ?
Stéphane Chazelas
Ma to tylko pokazać wynik tutaj
bluefoggy
2

Kolejne polecenie awk ,

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

Ustawiając separator pól jako ciąg zerowy, awk zamienia pojedynczy znak w rekordzie jako osobne pola.

Avinash Raj
źródło
2

niektóre alternatywy obejmują:

podobny do odpowiedzi Gnouca, ale z powłoką:

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

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

z sedi dcprawdopodobnie obejmujący wiele linii:

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

15

z $IFS...

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

To będzie również powiedzieć, jak wiele istnieje jak ...

echo $(($#-1))
mikeserv
źródło