คำสั่งเพื่อรับบรรทัดที่ n ของ STDOUT


210

มีคำสั่ง bash ใดบ้างที่จะให้คุณได้บรรทัดที่ n ของ STDOUT?

กล่าวคือสิ่งที่จะใช้เวลานี้

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

และทำสิ่งที่ชอบ

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

ฉันรู้ว่าสิ่งนี้จะเป็นวิธีปฏิบัติที่ไม่ดีเมื่อเขียนสคริปต์เพื่อนำมาใช้ซ้ำ แต่เมื่อทำงานกับเชลล์แบบวันต่อวันมันจะมีประโยชน์สำหรับฉันที่จะกรอง STDOUT ของฉันในลักษณะนี้

ฉันก็รู้ว่านี่จะเป็นคำสั่งแบบกึ่ง ๆ เขียน (buffer STDOUT ส่งคืนบรรทัดที่ระบุ) แต่ฉันต้องการทราบว่ามีคำสั่งเชลล์มาตรฐานบางอย่างที่จะทำสิ่งนี้ซึ่งจะพร้อมใช้งานโดยที่ฉันไม่ปล่อยสคริปต์ลง


5
ฉันเพิ่งโหวตสิ่งนี้เพื่อการใช้คำmagic-command
เจ็ด

คำตอบ:


332

ใช้sedเพื่อความหลากหลาย:

ls -l | sed -n 2p

การใช้ทางเลือกนี้ซึ่งดูมีประสิทธิภาพมากขึ้นเนื่องจากหยุดอ่านอินพุตเมื่อพิมพ์บรรทัดที่ต้องการอาจสร้าง SIGPIPE ในกระบวนการป้อนกระดาษซึ่งอาจทำให้เกิดข้อความแสดงข้อผิดพลาดที่ไม่ต้องการ:

ls -l | sed -n -e '2{p;q}'

ฉันเห็นว่าบ่อยครั้งพอที่ฉันมักจะใช้ครั้งแรก (ซึ่งง่ายต่อการพิมพ์อย่างไรก็ตาม) lsไม่ใช่คำสั่งที่บ่นเมื่อได้รับ SIGPIPE

สำหรับช่วงของเส้น:

ls -l | sed -n 2,4p

สำหรับหลายบรรทัด:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'

p หมายถึงอะไร เช่น 2p 3p ในแง่ที่ฉันเข้าใจว่ามันมีไว้สำหรับเส้น 2/3 แต่ทฤษฎี / ความเข้าใจเบื้องหลังคืออะไร
Leo Ufimtsev

4
@LeoUfimtsev: pเป็นsedคำสั่งที่พิมพ์บรรทัดที่ระบุโดยช่วงของบรรทัดที่นำหน้ามัน ดังนั้น2,4pหมายถึงพิมพ์บรรทัดที่ 2, 3, 4
Jonathan Leffler

3
และnหมายถึงไม่พิมพ์บรรทัดอื่นทั้งหมด
Timo

85
ls -l | head -2 | tail -1

13
+2 แต่ฉันขอแนะนำhead -n 2 | tail -n 1- หัวและก้อยสมัยใหม่มักจะออกคำเตือนเป็นอย่างอื่น
Michael Krelin แฮกเกอร์

2
การใช้สองกระบวนการในการกรองข้อมูลนั้นค่อนข้างเกินความเป็นจริง (แต่ด้วยพลังของเครื่องจักรในทุกวันนี้พวกเขาน่าจะรับมือได้) การใช้ทั่วไปในการจัดการช่วงหนึ่งนั้นไม่สำคัญเลย (สำหรับช่วง N..M ที่คุณใช้head -n M | tail -n M-N+1) และไม่สามารถทำการวางแนวทั่วไปเพื่อจัดการหลาย ๆ ช่วงได้
Jonathan Leffler

3
หัวประปาเข้าไปในหาง? น่ากลัว หลายวิธีที่ดีกว่าที่จะทำ
camh

16
สิ่งที่ดีเกี่ยวกับบรรทัดคำสั่งห้าม *: ล้านบาทและหนึ่งในวิธีที่จะทำทุกอย่างและทุกคนมีความชื่นชอบและเกลียดทุกวิธีการอื่น ๆ ... :-)
Beggs

1
วิธีพิมพ์ช่วงของเส้นด้วยวิธีอื่น: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(สามารถเพิ่มเงื่อนไขเพิ่มเติมได้ง่ายขึ้นหลายช่วง ฯลฯ ... )
user10607

41

ทางเลือกที่ดีต่อหัว / หาง:

ls -al | awk 'NR==2'

หรือ

ls -al | sed -n '2p'

1
mine awk ดีกว่า แต่ sed ดี
Michael Krelin แฮกเกอร์

ไม่จำเป็นต้องใช้คำว่า "if" - ดูคำตอบของแฮ็กเกอร์ (ยกเว้นส่วนที่เกี่ยวกับการค้นหาทุกไฟล์ในระบบ) ;-)
หยุดชั่วคราวจนกว่าจะมีประกาศ

สิ่งที่'2p'ยืนหยัดเพื่อ?
ทำลาย

1
@ shredding คำตอบสั้น ๆ พิมพ์บรรทัดที่สอง; ยาว -> เมื่อใช้จะมีเพียงบรรทัดที่ตรงกับรูปแบบเท่านั้นที่จะได้รับคำสั่งหลังจากที่อยู่ สั้น ๆ เมื่อใช้กับแฟล็ก / p บรรทัดที่ตรงกันจะถูกพิมพ์สองครั้ง: ไฟล์ sed '/ PATTERN / p' และแน่นอนรูปแบบคือการแสดงออกปกติใด ๆเพิ่มเติม
Medhat

ถ้า2ตัวแปรคืออะไร ทุกตัวอย่างใช้เครื่องหมายคำพูดเดี่ยว แต่ด้วย var คุณต้องมีเครื่องหมายคำพูด เราสามารถ "โปรแกรม" awk ให้ทำงานด้วยเครื่องหมายคำพูดเดียวเมื่อใช้ vars ได้หรือไม่? line =1; ls | awk 'NR==$line'ตัวอย่าง ฉันรู้ว่าbashใช้เครื่องหมายคำพูดคู่กับ vars
Timo


9

เพื่อความสมบูรณ์ ;-)

รหัสที่สั้นกว่า

find / | awk NR==3

ชีวิตที่สั้นลง

find / | awk 'NR==3 {print $0; exit}'

คุณไม่ได้ล้อเล่นเกี่ยวกับความสมบูรณ์!
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

ฉันไม่ใช่ ;-) ที่จริงแล้วฉันไม่รู้ว่าทำไมทุกคนถึงใช้lsคำถามเดิมเกี่ยวกับของใครบางคนSTDOUTดังนั้นฉันจึงคิดว่ามันจะดีกว่าถ้าจะให้ใหญ่กว่านี้
Michael Krelin แฮกเกอร์

อย่างน้อยด้วย GNU awk การกระทำเริ่มต้นคือ{ print $0 }`awk 'NR == 3' เป็นวิธีที่สั้นกว่าในการเขียนแบบเดียวกัน
ephemient

คุณน่าจะเพิ่ม "; ออก" ในการกระทำนั้น ไม่มีจุดประมวลผลส่วนที่เหลือของบรรทัดเมื่อคุณไม่ต้องการทำอะไรกับพวกเขา
camh

@camh: ดูคำตอบของ Jonathan Leffer เกี่ยวกับเรื่องนี้: "อาจสร้าง SIGPIPE ในกระบวนการให้อาหารซึ่งอาจทำให้เกิดข้อความแสดงข้อผิดพลาดที่ไม่ต้องการ"
ephemient


6

คุณสามารถใช้ awk:

ls -l | awk 'NR==2'

ปรับปรุง

รหัสดังกล่าวจะไม่ได้รับสิ่งที่เราต้องการเพราะปิดโดยหนึ่งข้อผิดพลาด: คำสั่ง ls -lบรรทัดแรกคำสั่งเป็นรวมเส้น สำหรับสิ่งนั้นรหัสที่แก้ไขแล้วต่อไปนี้จะใช้ได้:

ls -l | awk 'NR==3'

4

Perl ใช้ง่ายกับคุณหรือไม่

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

แทนหมายเลขใด ๆ ที่คุณต้องการอย่างชัดเจน 7


นี่คือสิ่งที่ฉันชอบเพราะดูเหมือนจะเป็นวิธีที่ง่ายที่สุดในการสรุปสิ่งที่ฉันเป็นส่วนตัวหลังจากนั้นคือทุกๆ nth, perl -n -e 'if ($.% 7 == 0) {พิมพ์; ออก (0); } '
mat kelcey

@matkelcey คุณสามารถ$ awk 'NR % 7' filenameพิมพ์ทุกบรรทัดที่ 7 ในไฟล์
user10607

3

แนะนำโปสเตอร์อื่น

ls -l | head -2 | tail -1

แต่ถ้าคุณต่อหัวเข้าหาหางดูเหมือนว่าทุกอย่างจนถึงบรรทัด N จะถูกประมวลผลสองครั้ง

ท่อหางเข้าไปในหัว

ls -l | tail -n +2 | head -n1

จะมีประสิทธิภาพมากขึ้น?


0

ใช่วิธีที่มีประสิทธิภาพมากที่สุด (ดังที่โจนาธานเลฟเลอร์ชี้ให้เห็น) คือการใช้งานพิมพ์และเลิกพิมพ์:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

การใช้ pipefail และ PIPESTATUS นั้นน่าสนใจมากและนี่เป็นการเพิ่มจำนวนมากในหน้านี้
Mike S

0

อืมมม

sed ไม่ทำงานในกรณีของฉัน ฉันเสนอ:

สำหรับบรรทัด "คี่" 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

สำหรับบรรทัด "even" 2,4,6,8 ls | awk '0 == (NR)% 2'


-1

เพื่อความสมบูรณ์มากขึ้น ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

ทิ้งสายจนกว่าคุณจะได้ที่สองจากนั้นพิมพ์บรรทัดแรกหลังจากนั้น ดังนั้นมันจะพิมพ์บรรทัดที่ 3

ถ้ามันเป็นแค่บรรทัดที่สอง ..

ls -l | (read; head -n1)

ใส่ 'อ่านมากเท่าที่จำเป็น


-1

ฉันเพิ่มสิ่งนี้ใน. bash_profile ของฉัน

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

และมันใช้งานได้

gdf <file name>

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