วิธีการเลือกบางบรรทัด (n, n + 4, n + 8, n + 12 …) จากไฟล์?


11

การป้อนข้อมูล:

1
hgh
h2b
h4h
2
ok
koko
lkopk
3
uh
ju
nfjvn
4

ผลลัพธ์ที่คาดหวัง:

1
2
3
4

ดังนั้นฉันต้องมีค่า 1, 5, 9, 13 ของไฟล์ในไฟล์เอาต์พุต ทำอย่างไร


2
ดูเพิ่มเติมที่: unix.stackexchange.com/questions/325985/…ด้วย GNU sed คุณสามารถทำได้sed -n '1~4p'
Sundeep

1
ที่เกี่ยวข้อง: superuser.com/questions/396536/…
Julien Lopez

คำตอบ:


28

ใช้ AWK:

awk '!((NR - 1) % 4)' input > output

หาวิธีการทำงานนี้เหลือเป็นแบบฝึกหัดสำหรับผู้อ่าน


ขอบคุณสำหรับหลักสูตร awk สั้น ๆ นี้!
darxmurf

20
NR % 4 == 1จะเป็น IMO ที่ชัดเจนมากขึ้น
Stéphane Chazelas

12
เห็นด้วย @ Stéphane; นี่อาจเป็นคำถามในส่วนของฉัน แต่สำหรับคำถามการบ้านที่อาจเกิดขึ้นฉันพยายามทำให้งงงวยคำตอบของฉันเล็กน้อย ...
Stephen Kitt

@StephenKitt ทำให้งงงวยคำตอบของคุณ? จริงๆ? นี่ไม่ใช่สถานที่ที่จะทำเช่นนั้น
ข้อมูล

22

การใช้split (GNU coreutils):

split -nr/1/4 input > output
  • -nสร้างCHUNKSไฟล์เอาต์พุต

และCHUNKSเป็น

  • r/K/N ใช้การกระจายแบบโรบินแบบกลมและส่งเอาต์พุต Kth of N ไปยัง stdout เท่านั้นโดยไม่ต้องแยกบรรทัด / บันทึก

1
ใจเป่า คำตอบเช่นนี้คือเหตุผลที่ฉันรัก SE นี้ ขอบคุณ!
user1717828

21

ด้วย GNU sed:

sed '1~4!d' < input > output

ด้วยมาตรฐานsed:

sed -n 'p;n;n;n' < input > output

ด้วย1และ4ใน$nและ$iตัวแปร:

sed "$n~$i!d" # GNU only
awk -v n="$n" -v i="$i" 'NR >= n && (NR % i) == (n % i)'


4

Python version เพื่อความสนุกสนาน:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())

enumerate(f)ควรจะสามารถทำงานได้ในขณะที่ใช้หน่วยความจำน้อยลง
iruvar

@iruvar นั่นมันเรียบร้อยมาก! ไม่เคยรู้มาก่อนว่า; จะใช้ในอนาคต รู้สึกอิสระที่จะแก้ไขมันเป็นคำตอบนี้; ฉันจะไม่รักษามันด้วยการปรับให้เหมาะสมเพราะคำตอบ Bash อื่น ๆ (โดยเฉพาะอันนี้ ) เป็นวิธีที่จะไปแน่นอน
user1717828

หากคุณกำลังจะใช้readlines(จึงทำให้ไฟล์ทั้งหมดในหน่วยความจำ slurping) คุณสามารถใช้f.readlines()[::4]เพื่อรับทุกบรรทัดที่สี่ print(''.join(f.readlines()[::4]))ดังนั้นคุณสามารถใช้
Nick Matteo

3

POSIX sed: วิธีนี้ใช้ sedix posixly และสามารถเรียกใช้ได้ทุกที่หรืออย่างน้อย seds ที่เคารพ posix

 $ sed -ne '
   /\n/!{
    H;s/.*//;x
   }

   :loop
       $bdone
       N;s/\n/&/4
       tdone
   bloop

   :done
   s/.//;P
 ' input.file

อีกประการหนึ่งคือการสร้างรหัส sed แบบเป็นโปรแกรมเพื่อวัตถุประสงค์ในการปรับขยาย:

$ code=$(yes n | head -n 4 | paste -sd\; | sed s/n/p/)
$ sed -ne "$code" input.file

Perl: เราเติมอาร์เรย์ A จนกว่าจะมีขนาด 4 จากนั้นเราพิมพ์องค์ประกอบแรกและล้างอาร์เรย์

$ perl -pe '
   $A[@A] = @A ? <> : $_ while @A < 4;
   $_ = (splice @A)[0];
' input.file

1

โทรด้วยscriptname filename skip(4 ในกรณีของคุณ) มันทำงานได้โดยการiterลากเส้นจากด้านบนของไฟล์จากนั้นจึงส่งออกล่าสุดเท่านั้น จากนั้นจะเพิ่มขึ้นiterโดยskipsและซ้ำตราบใดที่ค่าของiterยังไม่ได้เกินในlinesfile

#!/bin/bash
file="$1"
lines=`wc -l < "$file"`
skips="$2" || "4"
iter=1
while [ "$iter" -le "$lines" ]; do
 head "$file" -n $iter | tail -n 1
 iter=$(( $iter + $skips ))
done

1

Pure Bash:

mapfile -t lines < input
for (( i=0; i < ${#lines[@]}; i+=4 ))
do printf "%s\n" "${lines[$i]}"
done

mapfileเป็น buildin ที่เพิ่มใน Bash 4 ซึ่งอ่านอินพุตมาตรฐานลงในอาร์เรย์ชื่อที่นี่linesพร้อมหนึ่งบรรทัดต่อรายการ -tตัวเลือกแถบบรรทัดใหม่สุดท้าย

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

mapfile -t -c4 -C 'printf "%.0s%s\n"' < input

สิ่งนี้ใช้printfbuiltin; รหัสรูปแบบ%.0sระงับอาร์กิวเมนต์แรก (ดัชนี) ดังนั้นจะพิมพ์เฉพาะบรรทัด

คุณสามารถใช้คำสั่งเดียวกันเพื่อพิมพ์ทุก ๆ บรรทัดที่สี่โดยเริ่มจากบรรทัดที่ 1, 2 หรือ 3 แต่คุณต้องเพิ่มบรรทัดที่ 3, 2 หรือ 1 inputก่อนที่จะป้อนมันmapfileซึ่งฉันคิดว่าเป็นปัญหามากกว่าที่ควรจะเป็น .

สิ่งนี้ยังใช้งานได้:

mapfile -t lines < input
printf "%s%.0s%.0s%.0s\n" "${lines[@]}"

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

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