Bash: วิธีรับบางบรรทัดจากไฟล์และบันทึกเอาต์พุตไปยังไฟล์อื่น [ปิด]

Jan 17 2021

ฉันมีไฟล์บันทึกแบบนี้

$ cat build.log
..........
[ 60% 60917/101076] AAPT2 compile ....
[ 60% 60918/101076] AAPT2 compile ....
[ 60% 60919/101076] AAPT2 compile ....
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

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

$ cat parsed.log
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

เช่นเดียวกับรับความคืบหน้าสุดท้าย[ 60% 60920/101076]จนถึงจุดสิ้นสุดของไฟล์โดยใช้ grep, sed หรืออะไรก็ได้ ขอขอบคุณ

คำตอบ

dawg Jan 17 2021 at 22:29

นี่คือ perl:

$ perl -0777 -lne 'print $1 if /(^\[[^[]*\z)/m' file

หรือท่อ perl:

$ perl -E 'say reverse <>' file | perl -lpE 'if (/^\[/){ say; last}' | perl -E 'say reverse <>'

สำหรับสิ่งที่awkคุณสามารถทำได้:

$ awk 'BEGIN{RS="\\["}END{print "[" $0}' file

แน่นอนคุณอาจรู้ว่าหากความล้มเหลวเป็น 3 บรรทัดเสมอวิธีที่ง่ายที่สุดคือใช้tail:

$ tail -n 3 file

พิมพ์ทั้งหมด:

[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
WilliamPursell Jan 17 2021 at 17:57
$ cat build.log ........ [ 60% 60917/101076] AAPT2 compile .... [ 60% 60918/101076] AAPT2 compile .... [ 60% 60919/101076] AAPT2 compile .... [ 60% 60920/101076] AAPT2 compile .... ninja: build stopped: subcommand failed. 21:41:22 ninja failed with: exit status 1 $ awk '$2 != n[2]{print p} {p = $0; split(p,n,FS)} END{print p}' build.log
........
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

awkเป็นเครื่องมือที่เหมาะสมสำหรับสิ่งนี้ ที่นี่เราตรวจสอบว่าฟิลด์ที่ 2 ตรงกับฟิลด์ที่ 2 ของบรรทัดก่อนหน้าหรือไม่และทำการพิมพ์หรือไม่ จากนั้นเก็บบรรทัดก่อนหน้าและทำซ้ำ พิมพ์บรรทัดสุดท้ายของอินพุตเสมอ

potong Jan 17 2021 at 18:07

สิ่งนี้อาจได้ผลสำหรับคุณ (GNU sed):

sed '/^\[/h;//!H;$!d;x' file

หากเส้นเริ่มต้นให้[จัดเก็บไว้ในช่องว่าง (เขียนทับสิ่งที่ก่อนหน้านี้)

ในทำนองเดียวกันต่อท้ายบรรทัดปัจจุบันเข้ากับช่องว่าง

ลบทุกบรรทัดยกเว้นบรรทัดสุดท้าย

ในตอนท้ายของไฟล์ให้สลับไปที่ช่องว่างและพิมพ์เนื้อหา

IdrissNeumann Jan 17 2021 at 18:15

บางครั้งคุณอาจไม่โชคดีถ้าคุณต้องการใช้ตัวกรองประเภทนี้โดยไม่ต้องเปลี่ยนลำดับของบรรทัด และถ้าเส้นที่ไม่ได้เขียนที่จุดเริ่มต้นของไฟล์ของคุณหรือที่สิ้นสุด: tac, sortและuniqจะไม่เป็นเครื่องมือที่เหมาะสม

นี่คือวิธีแก้ปัญหาโดยใช้awk:

$ awk 'function push(a,e) { a[length(a)+1] = e } BEGIN {split("", lines); to_replace="toreplace"; exists=0} {if ($0 ~ "^\\[ [0-9]+%"){ll=$0; if (exists <= 0) {exists++; push(lines,to_replace)}} else {push(lines, $0)}} END {for (e in lines){if (lines[e] == to_replace) {print ll} else {print lines[e]}}}' test.log 
..........
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

เวอร์ชันที่อ่านได้และอธิบายได้:

# a function to append an element to an array dynamically
function push(a,e) { 
  a[length(a)+1] = e 
} 

BEGIN {
  split("", lines); # initializing an array
  to_replace="toreplace"; # you can change the replace key if you want
  exists=0
} 

{
  if ($0 ~ "^\\[ [0-9]+%"){ # matching all percentages/progression lines, regardless of their values ll=$0; 
    if (exists <= 0) {
      exists++; 
      push(lines, to_replace)
    }
  } else {
    push(lines, $0)
  }
}

END {
  for (e in lines) {
    if (lines[e] == to_replace) {
      print ll
    } else {
      print lines[e]
    }
  }
}

ความแตกต่างกับโซลูชันของWilliam Purselคือเมื่อเปอร์เซ็นต์ของคุณเพิ่มขึ้น มาดูความแตกต่างของพฤติกรรมในกรณีนี้:

$ cat test.log 
..........
[ 60% 60917/101076] AAPT2 compile ....
[ 60% 60918/101076] AAPT2 compile ....
[ 60% 60919/101076] AAPT2 compile ....
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####
$ awk 'function push(a,e) { a[length(a)+1] = e } BEGIN {split("", lines); to_replace="toreplace"; exists=0} {if ($0 ~ "^\\[ [0-9]+%"){ll=$0; if (exists <= 0) {exists++; push(lines,to_replace)}} else {push(lines, $0)}} END {for (e in lines){if (lines[e] == to_replace) {print ll} else {print lines[e]}}}' test.log 
..........
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####
$ awk '$2 != n[2]{print p} {p = $0; split(p,n,FS)} END{print p}' test.log 
..........
[ 60% 60919/101076] AAPT2 compile ....
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

ดังนั้นคุณอาจจะเลือกว่าคุณต้องการเก็บเส้นความก้าวหน้าสุดท้ายไว้แม้ว่าเปอร์เซ็นต์จะไม่เท่ากันหรือถ้าคุณต้องการกรองเฉพาะบรรทัดที่มีเปอร์เซ็นต์เท่ากันหลาย ๆ ครั้ง

Enlico Jan 17 2021 at 18:47

หากมีการรับประกันว่าบรรทัดแรกที่คุณต้องการนำออกเป็นบรรทัดสุดท้ายที่ขึ้นต้นด้วย[คุณสามารถทำได้ซึ่งจะแทนที่ทุกอย่างตั้งแต่ต้นไฟล์ไปจนถึง[บรรทัดสุดท้ายที่นำหน้าด้วยบรรทัดเดียว[:

sed -z 's/.*\n\[/[/' file
EdMorton Jan 17 2021 at 22:50

สมมติว่า...บรรทัดที่จุดเริ่มต้นของการป้อนข้อมูลตัวอย่างของคุณแสดงถึง[...] ...บรรทัดที่นำหน้ามากกว่า:

$ awk '/^\[/{p=$0 ORS; next} {print p $0; p=""}' build.log
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

หากสมมติฐานนั้นไม่ถูกต้องโปรดแก้ไขคำถามของคุณเพื่อแสดงตัวอย่างขั้นต่ำที่สมบูรณ์และตรวจสอบได้ซึ่งเป็นตัวอย่างที่มีค่าตัวแทนเท่านั้นไม่ใช่...s