ค้นหาหลายสตริงในบรรทัดเดียวโดยใช้ regex ในไฟล์/ไดเร็กทอรีที่ซ้อนกันและผลลัพธ์ที่ตรงกัน

ตัวอย่างเช่น หากมีไฟล์และไดเร็กทอรี:

/tmp/temp_dir/subdir_001/file_001.txt
/tmp/temp_dir/subdir_001/file_002.txt
/tmp/temp_dir/subdir_002/file_003.txt
/tmp/temp_dir/subdir_003/file_004.txt

และเนื้อหาเหล่านั้นมีเนื้อหาหลากหลายพร้อมบรรทัดเฉพาะที่ regex สามารถพบได้ ตัวอย่างเช่น นี่คือเนื้อหาของไฟล์ file_001.txt:

abc cba
little boy writes -54321_12345 and goes to street 987
bca acb
little boy writes -12345_54321 and jumps to street 789
cab bac

สิ่งที่ฉันสนใจคือบรรทัดเหล่านั้นที่ขึ้นต้นด้วย little boy writes ฉันใช้รูปแบบ regex นี้เพื่อค้นหาข้อมูลสำคัญที่ฉันต้องการบันทึกเป็นเอาต์พุต: little boy writes (\-\d+\_\d+).*street (\d+)

ฉันจะค้นหาแบบวนซ้ำและส่งออกเฉพาะสตริงที่ตรงกันได้อย่างไร ดังนั้นในไฟล์เอาต์พุตฉันจะมีสิ่งนี้เท่านั้น:

54321_12345 987
12345_54321 789

person iorsa    schedule 30.07.2020    source แหล่งที่มา
comment
สิ่งที่เกี่ยวกับการใช้ find และ exec   -  person Raman Sailopal    schedule 30.07.2020


คำตอบ (3)


การรวมกันของ find และ sed ควรทำเคล็ดลับ:

find /tmp/temp_dir/ -type f -exec sed -En 's/little boy writes -([0-9]+_[0-9]+).*street ([0-9]+)/\1 \2/p' {} + > output

ชำรุด :

  • find /tmp/temp_dir/ -type f : เราค้นหาทุกไฟล์แบบวนซ้ำจากโฟลเดอร์รูท
  • -exec sed '... ' {} + รันคำสั่งในทุกไฟล์ที่พบ (ในที่นี้ {} แสดงถึงรายการที่ดึงข้อมูลโดยคำสั่ง find และ + หมายความว่าคำสั่งจะถูกดำเนินการอีกครั้งในผลลัพธ์สุดท้าย ตามที่อธิบายไว้ที่นี่)
  • sed -En 's/little boy writes -([0-9]+_[0-9]+).*street ([0-9]+)/\1 \2/p' : เราดำเนินการตามรูปแบบที่คุณอธิบายไว้ในคำถามของคุณ โดยใช้ sed (\d ไม่ใช่คลาสอักขระ sed ที่ถูกต้อง เราใช้ [0-9] แทน)
  • > output เราเปลี่ยนเส้นทางเอาต์พุตของคำสั่งนี้ไปยังไฟล์ชื่อ output
person Aserre    schedule 30.07.2020

คุณสามารถใช้ grep รวมกับ sed:

$ grep '^little boy writes' /tmp/temp_dir/subdir_*/file_*.txt | sed -re 's/^.* -([0-9]+_[0-9]+).*street ([0-9]+)/\1 \2/' > output.txt
person Joe    schedule 30.07.2020

คุณ สามารถ รับบรรทัดที่มีเพียงการเรียกซ้ำ grep โดยมีหรือไม่มีชื่อไฟล์

grep -r  '^little boy writes' *  # lists source filenames
grep -hr '^little boy writes' *  # does not

สิ่งนี้จะรายงานทั้งบรรทัด การจับคู่รูปแบบ Perl (-P) กับ -o อาจตรวจพบบรรทัดที่ถูกต้องและส่งคืนเฉพาะบิตที่คุณต้องการเท่านั้น แต่รูปแบบนี้อาจดูแย่มากสำหรับคนส่วนใหญ่ที่จะเข้าใจและดูแลรักษา ดังนั้นจึงอาจคุ้มค่ากับกระบวนการที่สอง -

grep -hr '^little boy writes' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt |
  sed -E 's/[^0-9_]*([0-9_]+)/\1 /g'

หรือถ้าคุณต้องการหลีกเลี่ยงช่องว่างนั้นในตอนท้ายจริงๆ

grep -hr '^little boy writes' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt |
  's/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/\1 \2/'

แต่ถ้าคุณรู้แน่ชัดว่าไฟล์เหล่านั้นอยู่ที่ไหนเพียงพอสำหรับการเผยแพร่เช่นนั้น สิ่งที่คุณต้องมีก็แค่ไฟล์ sed

sed -En '/^little boy writes/{ s/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/\1 \2/g; p; }' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt

หากคุณไม่ทำเช่นนั้น grep และ/หรือ sed อาจบดขยี้ข้อมูลจำนวนมากที่คุณสามารถหลีกเลี่ยงได้...และบางทีโครงสร้างไดเร็กทอรีของคุณอาจไม่สอดคล้องกันนัก ในกรณีนั้น shopt จะช่วยได้

shopt -s globstar # let's ** stand for variable depth of subdirectories
sed -En '/^little boy writes/{ s/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/\1 \2/g; p; }' **/file_[0-9][0-9][0-9].txt

นั่นควรจะมีประสิทธิภาพมากกว่านี้มาก (และเร็วกว่ามาก) โดยจะปล่อยให้ระบบปฏิบัติการเลือกไฟล์ที่ตรงกันและส่งเฉพาะไฟล์เหล่านั้นให้กับ sed เพื่อการสแกน

นอกจากนี้ยังใช้ sed เพียงอินสแตนซ์เดียว แทนที่จะวางไข่สำหรับแต่ละไฟล์ด้วย find หรือต้องการ xargs

ขอให้โชคดี.

person Paul Hodges    schedule 30.07.2020