จะหาตำแหน่งของตัวละครโดยใช้ grep ได้อย่างไร?


11

ฉันต้องการระบุตำแหน่งของอักขระในสตริงโดยใช้คำสั่ง grep

RAMSITALSKHMAN|1223333ตัวอย่างเช่นสตริง

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

ฉันจะค้นหาตำแหน่งของ|ในสตริงที่กำหนดได้อย่างไร


ต้องอยู่กับ grep เหรอ?
Braiam

คำตอบ:


29

คุณสามารถใช้-bเพื่อรับไบต์ออฟเซ็ตซึ่งเป็นตำแหน่งเดียวกันกับข้อความแบบง่าย (แต่ไม่ใช่สำหรับ UTF-8 หรือคล้ายกัน)

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

ในข้างต้นฉันใช้-aสวิตช์บอก grep เพื่อใช้อินพุตเป็นข้อความ จำเป็นเมื่อใช้งานกับไฟล์ไบนารีและ-oสวิตช์เพื่อส่งออกเฉพาะอักขระที่ตรงกัน

หากคุณต้องการตำแหน่งเท่านั้นคุณสามารถใช้ grep เพื่อแยกตำแหน่งเท่านั้น:

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

หากคุณได้รับผลลัพธ์ที่แปลกตรวจสอบเพื่อดูว่า grep มีการเปิดใช้งานสี คุณสามารถปิดการใช้งานสีได้โดยส่งผ่าน--colors=neverไปยัง grep หรือโดยนำหน้าคำสั่ง grep ด้วย\(ซึ่งจะปิดใช้งานนามแฝงใด ๆ ) เช่น:

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

สำหรับสตริงที่ส่งคืนการจับคู่หลาย ๆ ไพพ์head -n1ให้ผ่านเพื่อให้ได้แมตช์แรก

โปรดทราบว่าฉันใช้ทั้งในข้างต้นและทราบว่าหลังจะไม่ทำงานหาก grep คือ "aliased" ผ่านการปฏิบัติการ (สคริปต์หรืออย่างอื่น) เฉพาะเมื่อใช้นามแฝง


3
ตอนนี้ค้นหา2;)
Izkata

ขอบคุณ @Izkata คุณพูดถูก ฉันได้อัปเดตโพสต์ของฉันเล็กน้อยแล้วเพิ่มหมวกที่หายไป^:)
runejuhl

1
คุณใช้ grep รุ่นใด ฉันได้0:|ผลลัพธ์เป็น - เพราะ 0 คือตำแหน่งไบต์ของจุดเริ่มต้นของบรรทัดที่|พบ
อเล็กซ์

grep @ Alex GNU จาก Debian grep (GNU grep) 2.27ยืด: คุณอาจใช้ OS X อยู่หรือเปล่า?
runejuhl

11

ลอง:

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

เอาท์พุท:

15:|

สิ่งนี้จะทำให้คุณอยู่ในตำแหน่งที่มีดัชนีอิง -1


มันไม่ทำงาน :(
82782

1
@ user82782: คุณเรียกใช้คำสั่งอะไร คุณรู้ได้อย่างไรว่ามันไม่ทำงาน
cuonglm

printf '%s\n' '|' | grep -o . | grep -n '|'พิมพ์1ไม่0เป็นไปตามที่คาดไว้
l0b0

1
@ l0b0: OP ไม่ได้บอกว่าเขาต้องการฐานดัชนี 0 หรือ 1
cuonglm

ฉันแค่หมายถึงสิ่งที่นักพัฒนาซอฟต์แวร์คาดหวัง
l0b0

8

หากคุณใช้ shell คุณสามารถใช้การดำเนินการในตัวโดยไม่จำเป็นต้องวางไข่กระบวนการภายนอกเช่นหรือ :

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

ใช้การขยายพารามิเตอร์เพื่อลบการเกิดขึ้นทั้งหมด|ตามด้วยสตริงใด ๆ และบันทึกไว้ในตัวแปรชั่วคราว |แล้วมันเป็นเพียงเรื่องของการวัดความยาวของตัวแปรชั่วคราวที่จะได้รับดัชนีของ

สังเกตว่าifกำลังตรวจสอบว่า|มีอยู่ในสตริงเดิมหรือไม่ หากไม่เป็นเช่นนั้นตัวแปรชั่วคราวจะเหมือนกับของเดิม

หมายเหตุนอกจากนี้ยังมีดัชนี zero-based |ซึ่งโดยทั่วไปมีประโยชน์เมื่อทำดัชนีสตริง bash อย่างไรก็ตามหากคุณต้องการดัชนีแบบอิงพื้นฐานคุณสามารถทำสิ่งนี้ได้:

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

1
อาจเป็นคำตอบที่ดีที่สุดไวยากรณ์นี้สวยงามและรวดเร็วและใช้งานง่ายเมื่อคุณเข้าใจความหมายของมันอยู่ที่แกนกลาง
นาน

4

คุณสามารถใช้indexฟังก์ชั่นawk เพื่อส่งคืนตำแหน่งในตัวละครที่เกิดการแข่งขันขึ้น:

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

หากคุณไม่สนใจการใช้indexฟังก์ชั่นของ Perl สิ่งนี้จะจัดการกับการรายงานศูนย์หนึ่งหรือหลายตัวละคร:

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

เพื่อความสะดวกในการอ่านเท่านั้นท่อส่งได้ถูกแบ่งข้ามสองบรรทัด

ตราบเท่าที่พบอักขระเป้าหมายให้indexส่งคืนค่าบวกตามศูนย์ (0) ดังนั้นสตริง "abc | xyz | 123456 | zzz |" เมื่อแยกวิเคราะห์ส่งคืนตำแหน่ง 0, 4, 8, 15 และ 19


สำหรับการใช้งานนี้ awk มีประโยชน์มากกว่า / ง่ายกว่า grep
Archemar

นี่พิมพ์เฉพาะตำแหน่งแรกจะไม่ทำงานกับสตริงเช่นRAMSITALSKHMAN|1|223333
cuonglm

3

นอกจากนี้เรายังสามารถทำได้โดยใช้ "การจับคู่ expr" หรือ "ดัชนี expr"

expr จับคู่ $ string $ substring โดยที่ $ substring เป็น RE

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

และด้านบนจะให้ตำแหน่งเนื่องจากจะส่งคืนความยาวของสตริงย่อยที่ตรงกัน

แต่จะเฉพาะเจาะจงมากขึ้นสำหรับการค้นหาดัชนี:

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

ฉันไม่มีชื่อเสียงพอที่จะแสดงความคิดเห็นที่อื่น ฉันชอบคำตอบที่ได้รับจาก @Gnouc เป็นการส่วนตัว อย่างไรก็ตามทำไมต้องใช้ awk และทำให้มันซับซ้อนเมื่อเราสามารถทำสิ่งต่าง ๆ ได้ง่ายโดยใช้ 'expr'
bluefoggy

@ kingsdeb เป็นเพียงข้อเสนอแนะ
Avinash Raj

@ kingsdeb: เนื่องจาก (1) awkโซลูชั่นสามารถแก้ไขได้เล็กน้อยสำหรับรายงานข้อมูลนี้ในทุกบรรทัดของไฟล์ (สิ่งที่คุณต้องทำคือลบสิ่งENDที่ไม่จำเป็นจริงๆออกจากคำตอบของ JRFerguson และ Avinash Raj ก็ทำได้แล้ว) ; ในขณะที่จะทำอย่างนั้นกับexprวิธีการแก้ปัญหาที่คุณจะต้องเพิ่มห่วงอย่างชัดเจน (และคำตอบ Gnouc ไม่ได้ปรับตัวได้ง่ายที่จะทำทั้งหมดที่ฉันสามารถดู) และ (2) awkการแก้ปัญหาที่สามารถปรับให้รายงานทั้งหมด การจับคู่ในแต่ละบรรทัดค่อนข้างง่ายกว่าการexprแก้ปัญหา (อันที่จริง Avinash Raj ก็ทำเช่นนั้นด้วย)
G-Man กล่าวว่า 'Reinstate Monica'

ทำไมคุณถึงใช้echo `...`ที่นี่?
Stéphane Chazelas

นี่คือการแสดงผลลัพธ์ที่นี่
bluefoggy

2

คำสั่ง awk อีก ,

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

โดยการตั้งค่าตัวคั่นฟิลด์เป็นสตริง null awk เปลี่ยนอักขระแต่ละตัวในบันทึกเป็นฟิลด์แยก


2

ทางเลือกบางอย่างรวมถึง:

คล้ายกับคำตอบของ Gnouc แต่ใช้กระสุน:

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

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

ด้วยsedและdcอาจทอดหลายบรรทัด:

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

15

ด้วย$IFS...

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

นั่นจะบอกคุณว่ามีจำนวนเท่าใด...

echo $(($#-1))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.