วิธีการประมวลผลแต่ละบรรทัดที่ได้รับจากคำสั่ง grep


152

ฉันมีจำนวนบรรทัดที่ดึงมาจากไฟล์หลังจากรันคำสั่งgrepดังนี้:

var=`grep xyz abc.txt`

สมมติว่าฉันได้ 10 บรรทัดซึ่งประกอบด้วย xyz เป็นผลลัพธ์

ตอนนี้ฉันต้องประมวลผลแต่ละบรรทัดที่ฉันได้รับจากคำสั่ง grep ฉันจะดำเนินการสิ่งนี้ได้อย่างไร


6
ไม่มีคำตอบใดที่นี่พูดถึงพลังgrep -oของสิ่งนี้ การ-oตั้งค่าสถานะจะให้เฉพาะข้อความที่ตรงกับหนึ่งตรงต่อบรรทัดของผลลัพธ์ (มันไม่ครบถ้วนดังนั้นecho aaa |grep 'a*'ให้ "aaa" และละเว้นการจับคู่สามส่วนเท่านั้น "", "a" และ "aa")
Adam Katz

คำตอบ:


269

วิธีง่าย ๆ วิธีหนึ่งคือไม่เก็บผลลัพธ์ในตัวแปร แต่วนซ้ำโดยตรงกับมันด้วย while / read loop

สิ่งที่ต้องการ:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

มีการเปลี่ยนแปลงในรูปแบบนี้ขึ้นอยู่กับสิ่งที่คุณหลังจาก

หากคุณต้องการเปลี่ยนตัวแปรภายในลูป (และให้เห็นการเปลี่ยนแปลงนั้นนอก) คุณสามารถใช้การทดแทนกระบวนการตามที่ระบุไว้ในคำตอบของ fedorqui :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)

หากไม่มีบรรทัดด้วย xyz?
XYZ_Linux

จากนั้นไม่มีอะไรเกิดขึ้นลูปจะไม่ทำงาน
Mat

13
ปัญหาของวิธีนี้คือทุกสิ่งที่อยู่ในลูปนั้นเป็นเพราะ subshell ดังนั้นการตั้งค่าตัวแปรที่กำหนดไว้ภายนอกลูปในระหว่างลูปจะไม่ทำให้ค่าของมันมีอยู่หลังจากลูป!
David Doria

3
@David: เป็นทางเลือกในการแก้ไขข้อกังวลของคุณ (fedorqui ได้อยู่แล้วมันเกินไป.)
จ้า

สำหรับคำสั่งที่บรรทัดสุดท้ายของเอาต์พุตไม่ได้ถูกยกเลิกด้วยการขึ้นบรรทัดใหม่คุณจะต้อง: while read p || [[ -n $p ]]; do ...(ยืมจากstackoverflow.com/questions/1521462/ … )
Ohad Schneider

21

คุณสามารถทำwhile readลูปต่อไปนี้ซึ่งจะถูกป้อนด้วยผลลัพธ์ของgrepคำสั่งโดยใช้การทดแทนกระบวนการที่เรียกว่า:

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

ด้วยวิธีนี้คุณไม่จำเป็นต้องเก็บผลลัพธ์ไว้ในตัวแปร แต่จะ "ส่ง" ผลลัพธ์ไปยังลูปโดยตรง


บันทึกการใช้งานIFS=และread -rตามคำแนะนำใน BashFAQ / 001: ฉันจะอ่านไฟล์ (data stream, variable) แบบ line-by-line (และ / หรือ field-by-field) ได้อย่างไร? :

อ็อพชัน -r เพื่ออ่านป้องกันการตีความเครื่องหมายทับขวา (โดยปกติจะใช้เป็นคู่ของเครื่องหมายทับขวาของบรรทัดใหม่เพื่อดำเนินการต่อผ่านหลายบรรทัดหรือเพื่อหลีกเลี่ยงตัวคั่น) หากไม่มีตัวเลือกนี้แบ็กสแลชที่ไม่ใช้ค่า Escape ใด ๆ ในอินพุตจะถูกยกเลิก คุณควรใช้ตัวเลือก -r กับการอ่านเกือบทุกครั้ง

ในสถานการณ์ข้างต้น IFS = ป้องกันการตัดส่วนของช่องว่างนำหน้าและต่อท้าย ลบออกหากคุณต้องการผลกระทบนี้

เกี่ยวกับการทดแทนกระบวนการอธิบายในหน้าทุบตีแฮกเกอร์ :

การทดแทนกระบวนการเป็นรูปแบบของการเปลี่ยนเส้นทางที่อินพุตหรือเอาต์พุตของกระบวนการ (ลำดับของคำสั่งบางอย่าง) ปรากฏเป็นไฟล์ชั่วคราว


ตกลงฉันนัดที่forรุ่น พยายามทำวนซ้ำ"${$(grep xyz abc.txt)[@]}"ในstackoverflow.com/a/14588210/1983854แต่ทำไม่ได้ ดังนั้นฉันเพิ่งออกจากรุ่นแรก
fedorqui 'ดังนั้นหยุดการทำร้าย'

1
คุณไม่สามารถใช้การขยายพารามิเตอร์กับการทดแทนคำสั่ง (ยกเว้นว่าคุณกำลังใช้zshซึ่งการทำรังแบบนั้นอาจใช้งานได้)
chepner

ปัญหาที่เป็นไปได้อย่างหนึ่งของสำนวนนี้คือถ้ามีอะไรในลูปพยายามอ่านจากอินพุตมาตรฐานมันจะได้ส่วนหนึ่งของไฟล์ เพื่อหลีกเลี่ยงความเป็นไปได้นี้ฉันต้องการส่งไฟล์ผ่าน file descriptor 3 มากกว่า stdin เพียงแค่ใช้while IFS= read -r result <&3และdone 3< <(grep ...
Gordon Davisson

8

ฉันขอแนะนำให้ใช้ awk แทน grep + อย่างอื่นที่นี่

awk '$0~/xyz/{ //your code goes here}' abc.txt


1
คุณจะอ้างอิงบรรทัดที่พบทั้งหมดได้//your code goes hereอย่างไร
user857990

2
@ user857990 ทั้งบรรทัดแสดงเป็น $ 0 ใน AWK
Julien Grenier

2

ไม่มีการวนซ้ำใด ๆ ด้วยตัวเลือก grep --line ที่บัฟเฟอร์:

your_command | grep --line-buffered "your search"

ตัวอย่างชีวิตจริงด้วย ouf คำสั่ง debug เราเตอร์ Symfony PHP Framework, เพื่อ grep เส้นทางที่เกี่ยวข้อง "api" ทั้งหมด:

php bin/console d:r | grep --line-buffered "api"

ทางออกเดียวที่ใช้งานได้หาก your_command ใช้งานได้นาน (เช่นtail -f some.logในกรณีของฉัน) ...
Izkata

0

บ่อยครั้งที่ลำดับของการประมวลผลไม่สำคัญ GNU Parallel ถูกสร้างขึ้นสำหรับสถานการณ์นี้:

grep xyz abc.txt | parallel echo do stuff to {}

หากคุณกำลังดำเนินการเช่น:

grep xyz abc.txt | myprogram_reading_from_stdin

และmyprogramช้าแล้วคุณสามารถเรียกใช้:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin

0

ทำซ้ำผลลัพธ์ grep ด้วย while / read loop ชอบ:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done

0

สำหรับผู้ที่กำลังมองหาหนึ่งซับ:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.