รับบรรทัดเฉพาะจากไฟล์ข้อความโดยใช้เชลล์สคริปต์


101

ฉันกำลังพยายามหาบรรทัดเฉพาะจากไฟล์ข้อความ

จนถึงตอนนี้ออนไลน์ฉันได้เห็นสิ่งต่างๆเช่น sed เท่านั้น (ฉันสามารถใช้ sh -not bash หรือ sed หรืออะไรก็ได้แบบนั้น) ฉันต้องทำสิ่งนี้โดยใช้เชลล์สคริปต์พื้นฐานเท่านั้น

cat file | while read line
    do
       #do something
    done

ฉันรู้วิธีการวนซ้ำตามบรรทัดดังที่แสดงด้านบน แต่ถ้าฉันต้องการรับเนื้อหาของบรรทัดใดบรรทัดหนึ่ง


คุณรู้หมายเลขบรรทัดหรือไม่
Mehul Rathod

1
จากนั้นคุณจะได้รับการนับ
Ignacio Vazquez-Abrams

ใช่หมายเลขบรรทัดคือ 5 @MehulRathod
GangstaGraham

3
ทำไมถึงcatโอเค แต่sedไม่ใช่? นั่นไม่สมเหตุสมผล
William Pursell

5
เพราะไม่มีใครสามารถปฏิเสธcatได้ อ๊ะ ... น่ารักcat!

คำตอบ:


205

sed:

sed '5!d' file

awk:

awk 'NR==5' file

เกี่ยวกับคำสั่ง sh ฉันไม่สามารถใช้ sed, awk ฉันควรทำให้สิ่งนี้ชัดเจนยิ่งขึ้นในคำถาม
GangstaGraham

@GangstaGraham คุณบอกว่าคุณรู้วิธีการวนซ้ำผ่านบรรทัดแล้วการเพิ่มเคาน์เตอร์ล่ะ? ถ้าตัวนับถึงหมายเลขบรรทัดเป้าหมายของคุณรับเส้นและทำลายลูป มันช่วยอะไร?
เคนท์

4
@KanagaveluSugumar อ่านหน้าข้อมูลของ sed 5!dหมายถึงลบทุกบรรทัดยกเว้น 5. shell var เป็นไปได้คุณต้องมีเครื่องหมายคำพูดคู่
เคนท์

13
ฉันขอแนะนำให้เพิ่มตัวแปรอื่น: sed -n 5pสิ่งนี้ดูเหมือนจะเป็นเหตุผลที่ต้องจำสำหรับมือใหม่เพราะ-nหมายความว่า "ไม่มีเอาต์พุตตามค่าเริ่มต้น" และpย่อมาจาก "พิมพ์" และไม่มีการพูดถึงการลบที่อาจทำให้สับสน (เมื่อมีคนพูดถึงไฟล์การลบบรรทัดมีแนวโน้มที่จะ หมายถึงสิ่งที่แตกต่างออกไป)
Josip Rodin

1
@JosipRodin คุณพูดถูก-n '5p'ทำงานสำหรับปัญหานี้ด้วย ความแตกต่างคือ5!dคุณสามารถเพิ่ม-iเพื่อเขียนการเปลี่ยนแปลงกลับไปที่ไฟล์ได้ อย่างไรก็ตาม-n 5pคุณต้องsed -n '5p' f > f2&& mv f2 fถามอีกครั้งสำหรับคำถามนี้ฉันเห็นด้วยกับความคิดเห็นของคุณ
Kent

22

สมมติ lineเป็นตัวแปรที่เก็บหมายเลขบรรทัดที่คุณต้องการหากคุณสามารถใช้ได้headและtailมันค่อนข้างง่าย:

head -n $line file | tail -1

ถ้าไม่ควรใช้งานได้:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

นี้-eqเปรียบเทียบเป็นจำนวนเต็มดังนั้นจึงต้องการหมายเลขบรรทัดไม่เนื้อหาบรรทัด ( $line) นี้จะต้องมีการแก้ไขโดยการกำหนดเช่นwant=5ก่อนที่จะห่วงแล้วใช้เปรียบเทียบ-eq $want[ย้ายจากการแก้ไขที่ถูกปฏิเสธ]
Josip Rodin

1
@JosipRodin ฉันให้คำแนะนำการแก้ไขโดยอิสระตามความคิดเห็นของคุณตามที่ฉันเห็นด้วยกับมัน หวังว่าครั้งนี้จะไม่ถูกปฏิเสธ
Victor Zamanian


11

วิธีประสิทธิภาพที่ดีที่สุด

sed '5q;d' file

เพราะsedหยุดอ่านบรรทัดใด ๆ หลังจากบรรทัดที่ 5

อัปเดตการทดลองจากMr. Roger Dueck

ฉันติดตั้ง wcanadian-insane (6.6MB) และเปรียบเทียบ sed -n 1p / usr / share / dict / words และ sed '1q; d' / usr / share / dict / words โดยใช้คำสั่ง time; ครั้งแรกใช้เวลา 0.043 วินาทีวินาทีเพียง 0.002 วินาทีดังนั้นการใช้ 'q' จึงเป็นการปรับปรุงประสิทธิภาพอย่างแน่นอน!


1
สิ่งนี้เขียนกันทั่วไป:sed -n 5q
William Pursell

3
ฉันชอบวิธีแก้ปัญหานี้เพราะsedหยุดอ่านบรรทัดใด ๆ หลังจากบรรทัดที่ 5
Anthony Geoghegan

2
ฉันติดตั้ง wcanadian-insane (6.6MB) และเปรียบเทียบsed -n 1p /usr/share/dict/wordsและsed '1q;d' /usr/share/dict/wordsใช้timeคำสั่ง ครั้งแรกใช้เวลา 0.043 วินาทีวินาทีเพียง 0.002 วินาทีดังนั้นการใช้ 'q' จึงเป็นการปรับปรุงประสิทธิภาพอย่างแน่นอน!
Roger Dueck

5

ตัวอย่างเช่นคุณต้องการรับบรรทัด 10 ถึง 20 ของไฟล์คุณสามารถใช้สองวิธีต่อไปนี้:

head -n 20 york.txt | tail -11

หรือ

sed -n '10,20p' york.txt 

p ในคำสั่งด้านบนหมายถึงการพิมพ์

นี่คือสิ่งที่คุณจะเห็น: ป้อนคำอธิบายภาพที่นี่


2

วิธีมาตรฐานในการทำสิ่งนี้คือการใช้เครื่องมือภายนอก การไม่อนุญาตให้ใช้เครื่องมือภายนอกในขณะที่เขียนเชลล์สคริปต์นั้นเป็นเรื่องไร้สาระ อย่างไรก็ตามหากคุณไม่ต้องการใช้เครื่องมือภายนอกจริงๆคุณสามารถพิมพ์บรรทัดที่ 5 ด้วย:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

โปรดสังเกตว่าสิ่งนี้จะพิมพ์บรรทัดตรรกะ 5 นั่นคือหากinput-fileมีความต่อเนื่องของบรรทัดจะนับเป็นบรรทัดเดียว คุณสามารถเปลี่ยนลักษณะการทำงานนี้ได้โดยเพิ่ม-rคำสั่ง read (ซึ่งน่าจะเป็นพฤติกรรมที่ต้องการ)


1
$((++i))ดูเหมือนจะเป็นการทุบตี; หาก OP ถูก จำกัด ในการใช้เครื่องมือภายนอกฉันจะไม่คิดว่าพวกเขาจะสามารถเข้าถึงได้มากกว่าธรรมดา/bin/sh
Josip Rodin

@JosipRodin ไม่ใช่มันเป็นคุณสมบัติPOSIX (แต่การสนับสนุนสำหรับการ++เพิ่มจะถูกทำเครื่องหมายเป็นตัวเลือกโดยเฉพาะ)
tripleee

@tripleee มันใช้ไม่ได้กับ dash สมัยใหม่เป็น / bin / sh ดังนั้นฉันจะไม่พึ่งพามัน
Josip Rodin

แต่วิธีแก้ปัญหาง่ายๆเช่น$((i+=1))ทำงานใน Dash ได้เช่นกัน
tripleee

$(($i+1))เป็นวิธีแก้ปัญหาง่ายๆที่ฉันคิด
Josip Rodin

1

ควบคู่ไปกับคำตอบของ William Pursellนี่คือโครงสร้างง่ายๆซึ่งควรใช้งานได้แม้ใน v7 Bourne shell ดั้งเดิม (และยังเป็นสถานที่ที่ Bash ไม่สามารถใช้ได้)

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

โปรดสังเกตด้วยว่าการปรับให้เหมาะสมเพื่อbreakออกจากลูปเมื่อเราได้รับบรรทัดที่เราต้องการ


0

ฉันไม่ชอบคำตอบใด ๆ เป็นพิเศษ

นี่คือวิธีที่ฉันทำ

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

ง่ายด้วย perl! หากคุณต้องการรับบรรทัด 1, 3 และ 5 จากไฟล์ให้พูด / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'แต่ smartmatch อยู่ระหว่างการทดลองและไม่ได้ใช้งาน
โซริน

ไม่ใช่วิธีการแก้ปัญหาอื่น ๆ เพียงวิธีเดียวที่รัดกุมหรือให้ความยืดหยุ่นมากขนาดนี้ (ทำไมทุกอย่างที่ช่วยประหยัดเวลาและทำให้สิ่งต่างๆง่ายขึ้น "คนฉลาด" "ท้อใจ" เราทุกคนควรจ้องหน้าจอทั้งวันหรือไม่)
dagelf

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
อย่างน้อยคุณช่วยอธิบายนิดนึงได้ไหมว่าทำไมงานนี้ถึงทำให้คนที่ถามคำถามชัดเจนขึ้น
ted

ดังนั้น grep แรกจะเลือกบรรทัดทั้งหมดโดยเพิ่มหมายเลขบรรทัดที่จุดเริ่มต้น จากนั้น grep ที่สองจะเลือกบรรทัดเฉพาะโดยจับคู่หมายเลขบรรทัดเมื่อเริ่มต้น และในที่สุดหมายเลขบรรทัดจะถูกตัดออกจากจุดเริ่มต้นของบรรทัดในเสียงสะท้อน
Oder

สิ่งนี้ทั้งซับซ้อนและไม่มีประสิทธิภาพเมื่อเทียบกับ sed -n 5pซึ่งแน่นอนว่ายังสามารถปรับให้เหมาะสมกับสิ่งที่ต้องการได้sed -n '5!d;p;q'
tripleee
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.