ฉันจะ cat เนื้อหาของไฟล์ที่พบโดยใช้ find เป็นไฟล์เดียวได้อย่างไร?


11

ฉันพยายามที่จะยิงตัวเองในที่ที่เจ็บ (แย่มาก) โดยการฟอร์แมตพาร์ติชันที่เก็บข้อมูลที่มีค่า แน่นอนมันไม่ได้ตั้งใจ แต่มันเกิดขึ้น

อย่างไรก็ตามฉันจัดการเพื่อใช้testdiskและphotorecกู้คืนข้อมูลส่วนใหญ่ ตอนนี้ฉันมีข้อมูลทั้งหมดที่แจกจ่ายไปแล้วกว่า 25,000 ไดเรกทอรี ไฟล์ส่วนใหญ่เป็นไฟล์. txt ส่วนที่เหลือเป็นไฟล์รูปภาพ มีไฟล์. txt มากกว่า 300 ไฟล์ในแต่ละไดเรกทอรี

ฉันสามารถgrepหรือใช้findเพื่อแยกสตริงจากไฟล์. txt และส่งออกไปยังไฟล์ ตัวอย่างเช่นต่อไปนี้เป็นบรรทัดที่ฉันใช้ตรวจสอบว่าข้อมูลของฉันอยู่ในไฟล์ที่กู้คืนแล้ว:

find ./recup*/ -name '*.txt' -print | xargs grep -i "searchPattern"

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

ผ่านไฟล์ทั้งหมดและค้นหาสตริงเฉพาะ หากพบสตริงนั้นในไฟล์ให้จับเนื้อหาทั้งหมดของไฟล์นั้นไปยังไฟล์เอาต์พุต หากพบรูปแบบในไฟล์มากกว่าหนึ่งไฟล์ต่อท้ายเนื้อหาของไฟล์ที่ตามมาไปยังไฟล์เอาต์พุตนั้น โปรดทราบว่าฉันไม่ต้องการส่งออกรูปแบบที่ฉันค้นหา แต่เนื้อหาทั้งหมดของไฟล์ที่พบรูปแบบ

ฉันคิดว่ามันเป็นไปได้ แต่ฉันไม่รู้วิธีที่จะคว้าเนื้อหาทั้งหมดของไฟล์หลังจาก grepping รูปแบบเฉพาะจากมัน


ดังนั้นด้วยคำสั่งที่คุณให้ไว้มันจะให้ผลลัพธ์ที่คุณต้องการ แต่คุณกำลังจะเปลี่ยนเส้นทางไปยังไฟล์ข้อความ?
ryekayo

หลังจากอ่านคำถามของฉันย่อหน้าที่เริ่มต้นด้วย "Go through ... " ฟังดูเหมือน psuedocode บางทีฉันสามารถรับรหัสพร้อมกับบางบรรทัดสำหรับ / ถ้ารหัส Python จะให้มันยิงในขณะที่ฉันรอการตอบสนองที่มีข้อมูลเพิ่มเติม
Ami

แน่นอนมันเป็น psuedocode และฉันแน่ใจว่าคุณสามารถหาวิธีที่จะทำในทุบตีเช่นกัน
ryekayo

@ryekayo ใช่มันให้ผลลัพธ์กับฉัน แต่นั่นเป็นเพียงการค้นหาไฟล์ที่มีชนิดข้อมูลเฉพาะซึ่งบอกฉันว่ามีข้อมูลมากกว่านั้นในไฟล์นั้น ดังนั้นฉันต้องการคว้าทุกอย่างในไฟล์นั้นและเขียนไปยังไฟล์อื่น
Ami

คุณอาจจะห่อคำสั่งนั้นใน if if บางคำสั่งหรือแม้แต่ switch-case ที่สามารถเรียกใช้ฟังก์ชั่นที่สามารถ cat out เนื้อหาตาม case หรือผลลัพธ์ของคำสั่ง if
ryekayo

คำตอบ:


10

หากฉันเข้าใจเป้าหมายของคุณอย่างถูกต้องสิ่งต่อไปนี้จะทำในสิ่งที่คุณต้องการ:

find ./recup*/ -name '*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

วิธีนี้จะค้นหา*.txtไฟล์ทั้งหมดในไฟล์./recup*/ทดสอบแต่ละไฟล์searchPatternหากตรงcatกับไฟล์นั้น เอาต์พุตของcatไฟล์ ed ทั้งหมดจะถูกนำไปoutputfile.txtยัง

ทำซ้ำสำหรับแต่ละรูปแบบและไฟล์เอาต์พุต


หากคุณมีจำนวนมากของไดเรกทอรีจับคู่คุณอาจจบลงด้วย./recup* argument list too long errorวิธีง่ายๆในการทำสิ่งนี้แทน:

find ./ -mindepth 2 -path './recup*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

สิ่งนี้จะตรงกับเส้นทางแบบเต็ม ดังนั้น./recup01234/foo/bar.txtจะถูกจับคู่ -mindepth 2คือเพื่อที่ว่ามันจะไม่ตรงหรือ./recup.txt./recup0.txt


ใช่ฉันคิดว่าจะทำ และมันทำให้ฉันมีพื้นฐานในการทำงาน เนื่องจากฉันกำลังจะค้นหาสตริงจำนวนมากฉันจึงคิดว่ารหัสบิตของรหัส if / if กับ elif หลายตัวจะช่วยฉันทำงานให้เป็นอัตโนมัติ ขอบคุณ
Ami

ดียิ่งกว่าสิ่งที่ฉันคิด lol
ryekayo

ดูเหมือนจะไม่ทำงาน มีข้อผิดพลาดนี้: "ไม่สามารถดำเนินการ / usr / bin / find: รายการอาร์กิวเมนต์ยาวเกินไป"
Ami

@Ami อัปเดตคำตอบเพื่อให้การแก้ไขปัญหาดังกล่าว
Patrick

2
@Ami หากคุณใช้หลาย ๆ สายมันอาจจะง่ายกว่าที่จะบันทึกชื่อไฟล์บวกทั้งหมดไปยังไฟล์อื่น ( grep -l) จากนั้น|sort|uniqและcatจากรายการไฟล์
Sparhawk

3

แทนที่จะเอาท์พุทรูปแบบของคุณเอาท์พุทชื่อไฟล์โดยใช้ "-l" บน grep จากนั้นใช้เป็นอินพุตเพื่อ cat

find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern" | xargs cat

หรือ

cat $( find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern")

ฉันสงสัยว่าคุณสามารถกรอกรายละเอียดที่เหลือ BTW หากคุณอาจมีช่องว่างหรืออักขระแปลก ๆ อื่น ๆ ในชื่อไฟล์ (ไม่น่าจะเป็นในกรณีนี้ แต่สำหรับวัตถุประสงค์ในอนาคต) ให้ใช้ -print0 ใน find และ -Z บน grep รวมกับตัวเลือก -0 บน xargs ที่จะใช้ ไบต์ว่างระหว่างชื่อไฟล์แทนที่จะขึ้นบรรทัดใหม่

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat

2
ฉันยังชอบตัวเลือก "สอง -exec" ของ Patrick ยกเว้นว่ามันจะทำให้เกิด fork ใหม่ (เช่น clone ()) และ exec สำหรับทุกไฟล์ โดยปกติแล้วคุณสามารถใช้\+แทนที่จะ\;หลีกเลี่ยงปัญหานั้น แต่ฉันไม่รู้ว่าวิธีนี้ใช้ได้กับ arex -exec คู่หนึ่งหรือไม่ (ฉันสงสัยว่า "ไม่ดี") การใช้ xargs คู่หนึ่งคุณจะมีโพรเซสใหม่เกิดขึ้นสองสามโพรเซสซึ่งควรจะเร็วกว่าด้วยไฟล์จำนวนมาก
dannysauer

มันก็ดูดีเช่นกัน ขอบคุณ คำถามหนึ่ง noob: cat หลังจาก xargs สุดท้ายควรจะส่งออกไปยังไฟล์ใช่มั้ย
Ami

เมื่อฉันอ่านครั้งแรกฉันไม่คิดว่าคำถามจะถูกระบุว่าเนื้อหาของไฟล์ควรไปที่ใด ทั้งสามของคำสั่งเหล่านี้ใส่ไฟล์ (s) เนื้อหาใน STDOUT ดังนั้นคุณต้องการเพียงแค่ผนวก (ให้มากที่สุด) >afileหรือ|acommandหรือสิ่งที่มีความเหมาะสมกับสถานการณ์ของคุณ :)
dannysauer

คำตอบที่ดีฉันต้อง cat pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
แอปทำงาน

นี้เป็นเพียงเล็กน้อยปิดหัวข้อ แต่ผมชอบใช้แทนsudo xargs xargs sudoเมื่อคุณเรียกมันสร้างบรรทัดคำสั่งสมมติว่าคำสั่งคือxargs sudo sudo cat argsแต่แมวอยู่ใน / bin ดังนั้นแล้ว sudo /bin/cat argsวิ่ง หากคำสั่งของคุณอยู่ในไดเรกทอรีที่ยาวกว่าเช่น / usr / local / bin คำสั่ง sudo ที่ทำงานจริงอาจส่งผลให้บรรทัดคำสั่งยาวเกินไปและข้อผิดพลาดที่ยากต่อการติดตาม ยิ่งไปกว่านั้นsudo xargsเพียงแค่บันทึกว่าคุณรัน xargs ในขณะที่xargs sudoบันทึกคำสั่งด้วยอาร์กิวเมนต์ทั้งหมด - ส่งผลให้บรรทัดบันทึก sudo ยาวบางบรรทัด :)
dannysauer

1

นี่ไม่ใช่รหัสที่ดีที่สุด แต่มันตรงไปตรงมามากและจะทำงานได้ดีหากประสิทธิภาพไม่ใช่ปัญหา ปัญหาคือว่ามันจะ grep ผ่านไฟล์หลาย ๆ ครั้งแม้ว่าจะพบสตริงแล้ว

ประการแรกค้นหาสตริงของคุณและเขียนไฟล์ที่ตรงกันลงในรายการ

find ./recup*/ -name '*.txt' -execdir grep -il "searchPattern" {} >> /tmp/file_list \;

ทำซ้ำขั้นตอนนี้แทนsearchPatternตามความจำเป็น /tmp/file_listนี้ผลิตรายการของไฟล์การจับคู่ที่

ปัญหาคือไฟล์นี้อาจมีไฟล์ซ้ำกัน |sort|uniqดังนั้นเราสามารถแทนที่ซ้ำกันด้วย sortส่วนสถานที่ซ้ำกันที่อยู่ติดกับแต่ละอื่น ๆ เพื่อให้uniqสามารถลบออกได้ จากนั้นคุณสามารถcatรวมไฟล์เหล่านี้โดยใช้xargs(โดยแต่ละชื่อไฟล์คั่นด้วยบรรทัดใหม่\n) ดังนั้น

</tmp/file_list sort | uniq | xargs -d "\n" cat > final_file.txt

ซึ่งแตกต่างจากคำตอบอื่น ๆ นี้มีสองขั้นตอนในนั้นและไฟล์ชั่วคราวดังนั้นฉันอยากจะแนะนำถ้าคุณมีหลายรูปแบบที่จะหา


0

ขึ้นอยู่กับเชลล์และสภาพแวดล้อมของคุณคุณสามารถทำสิ่งนี้ (ในทุบตี)

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1\|searchPattern2\|searchPattern3' "$file"; then
    cat "$file" >> some/other/file
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

หากคุณต้องการแยกผลลัพธ์ตามรูปแบบคุณสามารถแก้ไขสิ่งนั้นเป็นอย่างที่ต้องการ

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1' "$file"; then
    cat "$file" >> some/other/file1
  elif grep -qim1 'searchPattern2' "$file"; then
    cat "$file" >> some/other/file2
  elif grep -qim1 'searchPattern3' "$file"; then
    cat "$file" >> some/other/file3
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

บิตหลังจาก "เสร็จสิ้น" ทำอะไร สิ่งที่ฉันจะชอบคือการแก้ไขว่าถ้าบล็อกเพื่อให้ไฟล์ที่มีรูปแบบการจับคู่ถูกเขียนไปยังที่แตกต่างกัน
Ami

เพียงแค่แสดงไฟล์ '.txt' ที่พบแต่ละไฟล์จะถูกยกเลิกด้วยอักขระ null (เพื่อให้ปลอดภัยสำหรับชื่อไฟล์ที่มีช่องว่างและอักขระอื่น ๆ ) whileห่วงแล้วอ่านรายการที่และไม่grep/ เงื่อนไขcatส่วนหนึ่ง
ขับขี่เหล็ก

เมื่อฉันพยายามเรียกใช้รหัสฉันได้รับข้อผิดพลาดนี้: ./recoverData.sh: ข้อผิดพลาดทางไวยากรณ์: "(" ไม่คาดคิดนั่นคือที่มาจากวงเล็บรอบคำสั่ง find
Ami

คุณใช้เปลือกอะไร ไวยากรณ์การทดแทนกระบวนการเฉพาะทุบตี - ดังนั้นคุณสมบัติของฉัน "ขึ้นอยู่กับเปลือกและสภาพแวดล้อมของคุณ"
steeldriver

1
คุณสามารถรันคำสั่ง (s) โดยตรงในเปลือกทุบตีโต้ตอบหรือใส่ไว้ในไฟล์ที่มีบรรทัดแรกมี shebang #!/bin/bashทำให้ปฏิบัติการด้วยและดำเนินการได้โดยใช้chmod +x recoverData.sh ./recoverData.shไม่ได้ใช้sh recoverData.shมาตั้งแต่ปี/bin/shน่าจะเป็นdashเปลือก
ขับขี่เหล็ก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.