Làm thế nào để tìm vị trí của một nhân vật bằng grep?


10

Tôi cần xác định tư thế của một ký tự trong chuỗi bằng lệnh grep.

Ví dụ, chuỗi là RAMSITALSKHMAN|1223333.

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

Làm thế nào để tôi tìm vị trí của |chuỗi đã cho?


nó phải được với grep?
Braiam

Câu trả lời:


28

Bạn có thể sử dụng -bđể lấy phần bù byte, giống như vị trí cho văn bản đơn giản (nhưng không phải cho UTF-8 hoặc tương tự).

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

Ở trên, tôi sử dụng công -atắc để báo cho grep sử dụng đầu vào dưới dạng văn bản; cần thiết khi hoạt động trên các tệp nhị phân và -ochuyển sang chỉ xuất (các) ký tự phù hợp.

Nếu bạn chỉ muốn vị trí, bạn có thể sử dụng grep để chỉ trích xuất vị trí:

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

Nếu bạn nhận được đầu ra lạ, hãy kiểm tra xem grep có bật màu không. Bạn có thể vô hiệu hóa màu sắc bằng cách chuyển --colors=neverđến grep hoặc bằng cách thêm tiền tố vào lệnh grep bằng một \(sẽ vô hiệu hóa bất kỳ bí danh nào), ví dụ:

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

Đối với một chuỗi trả về nhiều kết quả khớp, hãy nối qua head -n1để có được kết quả khớp đầu tiên.

Lưu ý rằng tôi sử dụng cả hai ở trên và lưu ý rằng cái sau sẽ không hoạt động nếu grep bị "bí danh" thông qua một tệp thực thi (tập lệnh hoặc cách khác), chỉ khi sử dụng bí danh.


3
Bây giờ tìm kiếm 2;)
Izkata

Cảm ơn @Izkata, bạn đã đúng. Tôi đã cập nhật bài viết của mình một chút và thêm chiếc mũ bị mất ^:)
runejuhl

1
Phiên bản nào của grep bạn đã sử dụng? Tôi nhận 0:|làm đầu ra-- bởi vì 0 là vị trí byte của đầu dòng |được tìm thấy.
Alex

@Alex GNU grep từ Debian căng : grep (GNU grep) 2.27. Có lẽ bạn đang sử dụng OS X?
runejuhl

11

Thử:

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

đầu ra:

15:|

Điều này sẽ cung cấp cho bạn vị trí với chỉ số dựa trên 1.


Nó không hoạt động :(
user82782

1
@ user82782: Bạn đã chạy lệnh gì? Làm thế nào bạn biết nó không hoạt động?
cuonglm

printf '%s\n' '|' | grep -o . | grep -n '|'in 1, không 0như mong đợi.
l0b0

1
@ l0b0: OP không cho biết anh ta muốn chỉ số cơ sở 0 hoặc 1.
cuonglm

Tôi chỉ có nghĩa là những gì một nhà phát triển phần mềm mong đợi.
l0b0

8

Nếu bạn đang sử dụng shell, bạn có thể sử dụng các hoạt động được tích hợp hoàn toàn mà không cần phải sinh ra các quy trình bên ngoài như hoặc :

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

Điều này sử dụng một mở rộng tham số để loại bỏ tất cả các lần xuất hiện |theo sau bởi bất kỳ chuỗi nào và lưu nó trong một biến tạm thời. Sau đó, chỉ là vấn đề đo chiều dài của biến tạm thời để lấy chỉ số của |.

Lưu ý iflà kiểm tra xem |có tồn tại trong chuỗi gốc không. Nếu không, biến tạm thời sẽ giống như biến ban đầu.

Cũng lưu ý rằng điều này cung cấp chỉ mục dựa trên zero |, thường hữu ích khi lập chỉ mục chuỗi bash. Tuy nhiên, nếu bạn yêu cầu chỉ mục một, thì bạn có thể làm điều này:

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

1
có lẽ là câu trả lời tốt nhất, cú pháp này là đẹp và quá nhanh và dễ dàng để sử dụng khi bạn hiểu được ý nghĩa của nó, dài sống với lõi
vdegenne

4

Bạn có thể sử dụng indexchức năng của awk để trả về vị trí trong các ký tự nơi xảy ra trận đấu:

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

Nếu bạn không phiền khi sử dụng indexchức năng của Perl , điều này sẽ xử lý báo cáo bằng không, một hoặc nhiều lần xuất hiện của một ký tự:

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

Để dễ đọc, chỉ, đường ống đã được chia thành hai dòng.

Miễn là tìm thấy ký tự đích, indextrả về giá trị dương dựa trên 0 (0). Do đó, chuỗi "abc | xyz | 123456 | zzz |" khi phân tích cú pháp trả về các vị trí 0, 4, 8, 15 và 19.


Đối với việc sử dụng này, awk hữu ích / dễ dàng hơn grep.
Archemar

Điều này chỉ in vị trí đầu tiên, sẽ không hoạt động với chuỗi nhưRAMSITALSKHMAN|1|223333
cuonglm

3

Chúng tôi cũng có thể làm điều đó bằng cách sử dụng "expr match" hoặc "expr index"

expr khớp với chuỗi $ $ chuỗi con trong đó chuỗi con $ là RE.

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

Và ở trên sẽ cung cấp cho bạn vị trí vì nó trả về độ dài của chuỗi con phù hợp.

Nhưng để cụ thể hơn cho chỉ mục tìm kiếm:

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

Tôi không có đủ danh tiếng để bình luận ở bất cứ nơi nào khác. Cá nhân tôi thích câu trả lời được đưa ra bởi @Gnouc. Tuy nhiên, tại sao phải sử dụng awk và làm cho nó phức tạp khi chúng ta có thể làm những việc đơn giản bằng cách sử dụng 'expr'
bluefoggy

@kingsdeb nó chỉ là một gợi ý.
Avinash Raj

@kingsdeb: Bởi vì (1) các awkgiải pháp có thể được sửa đổi một cách tầm thường để báo cáo thông tin này trên mỗi dòng của tệp (tất cả những gì bạn phải làm là loại bỏ END, điều không bao giờ thực sự cần thiết, từ câu trả lời của JRFerguson và Avinash Raj đã làm điều đó rồi) ; trong khi đó, để làm điều đó với exprgiải pháp, bạn sẽ cần thêm một vòng lặp rõ ràng (và câu trả lời của Gnouc không dễ thích nghi để làm điều đó, tôi có thể thấy) và (2) các awkgiải pháp có thể được điều chỉnh để báo cáo tất cả phù hợp với từng dòng dễ dàng hơn so với exprgiải pháp (trên thực tế, Avinash Raj cũng đã làm điều đó).
G-Man nói 'Phục hồi Monica'

Tại sao bạn sẽ sử dụng echo `...`ở đây?
Stéphane Chazelas

Điều này chỉ để hiển thị đầu ra ở đây
bluefoggy

2

Một lệnh awk khác ,

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

Bằng cách đặt dấu phân cách Trường thành chuỗi rỗng, awk biến ký tự riêng lẻ trong bản ghi thành các trường riêng biệt.


2

một số lựa chọn thay thế bao gồm:

tương tự như câu trả lời của Gnouc, nhưng với vỏ:

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

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

với seddccó thể kéo dài nhiều dòng:

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

15

với $IFS...

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

Điều đó cũng sẽ cho bạn biết có bao nhiêu giống như ...

echo $(($#-1))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.