จัดเรียง แต่ให้บรรทัดส่วนหัวอยู่ด้านบน


55

ฉันได้รับผลลัพธ์จากโปรแกรมที่สร้างหนึ่งบรรทัดที่เป็นส่วนหัวของคอลัมน์จากนั้นเป็นกลุ่มของข้อมูล ฉันต้องการตัดคอลัมน์ต่าง ๆ ของผลลัพธ์นี้และดูเรียงตามคอลัมน์ต่าง ๆ ไม่มีส่วนหัวที่ตัดและการเรียงลำดับจะประสบความสำเร็จได้อย่างง่ายดายผ่านทาง-kตัวเลือกที่จะsortไปพร้อมกับcutหรือawkเพื่อดูชุดย่อยของคอลัมน์นี้ อย่างไรก็ตามวิธีการเรียงลำดับนี้จะรวมส่วนหัวของคอลัมน์เข้ากับส่วนที่เหลือของบรรทัดผลลัพธ์ มีวิธีง่ายๆในการรักษาส่วนหัวไว้ที่ด้านบนหรือไม่?


1
ฉันมาข้ามต่อไปนี้การเชื่อมโยง อย่างไรก็ตามฉันไม่สามารถใช้เทคนิคนี้ใน{ head -1; sort; }การทำงานได้ มันจะลบข้อความจำนวนมากเสมอหลังจากบรรทัดแรก ไม่มีใครรู้ว่าทำไมสิ่งนี้เกิดขึ้น?
เบื่อ

1
ฉันสงสัยว่าเป็นเพราะheadกำลังอ่านมากกว่าหนึ่งบรรทัดในบัฟเฟอร์และทิ้งมันเกือบทั้งหมด sedความคิดของฉันมีปัญหาเดียวกัน
แอนดี้

@jonderry - เทคนิคนั้นใช้ได้กับlseekอินพุตที่สามารถใช้ได้เท่านั้นดังนั้นจึงไม่สามารถใช้งานได้เมื่ออ่านจากไพพ์ มันจะทำงานถ้าคุณเปลี่ยนเส้นทางไปยังไฟล์>outfileจากนั้นเรียกใช้{ head -n 1; sort; } <outfile
don_crissti

คำตอบ:


58

ขโมยความคิดของ Andy และทำให้เป็นฟังก์ชันดังนั้นจึงง่ายต่อการใช้งาน:

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

ตอนนี้ฉันสามารถทำได้:

$ ps -o pid,comm | body sort -k2
  PID COMMAND
24759 bash
31276 bash
31032 less
31177 less
31020 man
31167 man
...

$ ps -o pid,comm | body grep less
  PID COMMAND
31032 less
31177 less

ps -C COMMANDอาจเหมาะสมกว่าgrep COMMANDแต่เป็นเพียงตัวอย่าง นอกจากนี้คุณไม่สามารถใช้ถ้าคุณยังใช้ตัวเลือกตัวเลือกอื่นเช่น-C -U
มิเคล

หรือบางทีมันควรจะเรียกว่าbody? ในฐานะที่เป็นหรือbody sort body grepคิด?
มิเคล

3
เปลี่ยนชื่อจากheaderเป็นbodyเพราะคุณกำลังทำสิ่งใด ๆ บนร่างกาย หวังว่ามันสมเหตุสมผลดีกว่า
มิเคล

2
อย่าลืมเรียกbodyผู้เข้าร่วมไปป์ไลน์ทุกคนภายหลัง:ps -o pid,comm | body grep less | body sort -k1nr
บิชอป

1
@ Tim คุณก็สามารถเขียนหรือ<foo body sort -k2 body sort -k2 <fooเพียงหนึ่งตัวละครพิเศษจากสิ่งที่คุณต้องการ
Mikel

36

คุณสามารถทำให้ส่วนหัวอยู่ด้านบนเช่นนี้ด้วยการทุบตี:

command | (read -r; printf "%s\n" "$REPLY"; sort)

หรือทำด้วย perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'

2
+1 ยอดเยี่ยม ฉันคิดว่ามันคุ้มค่าที่จะรวมเป็นฟังก์ชั่นเชลล์
มิเคล

1
+1 เหตุผลว่าทำไม subshell เป็นที่นิยมหรือใด ๆ{}ok แทน()?
jonderry

2
IFS=ปิดใช้งานการแยกคำเมื่ออ่านอินพุต $REPLYผมไม่คิดว่ามันเป็นสิ่งที่จำเป็นเมื่ออ่านเพื่อ echoจะขยายเครื่องหมายแบ็กสแลชหากxpg_echoมีการตั้งค่าไว้ (ไม่ใช่ค่าเริ่มต้น) printfปลอดภัยกว่าในกรณีนั้น echo $REPLYหากไม่มีเครื่องหมายอัญประกาศจะทำให้เกิดช่องว่าง ฉันคิดว่าecho "$REPLY"ควรจะโอเค read -rจำเป็นถ้าอินพุตอาจมีเครื่องหมายแบ็กสแลช บางอย่างนี้อาจขึ้นอยู่กับรุ่นทุบตี
Andy

1
@Andy: ว้าวคุณพูดถูกกฎที่แตกต่างกันสำหรับread REPLY; echo $REPLY(เว้นวรรคนำหน้า) และread; echo $REPLY(ไม่)
มิเคล

1
@Andy: IIRC ค่าเริ่มต้นxpg_echoขึ้นอยู่กับระบบของคุณเช่นใน Solaris ฉันคิดว่าค่าเริ่มต้นเป็นจริง นี่คือเหตุผลที่ Gilles ชอบprintfมาก: มันเป็นสิ่งเดียวที่มีพฤติกรรมที่คาดเดาได้
มิเคล

23

ฉันพบรุ่น awk ที่ดีที่ทำงานได้ดีในสคริปต์:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'

1
ฉันชอบสิ่งนี้ แต่ต้องการคำอธิบายเล็กน้อย - ไพพ์อยู่ในสคริปต์ awk มันทำงานอย่างไร มันเรียกsortคำสั่งจากภายนอกหรือไม่? ไม่มีใครรู้ว่าอย่างน้อยลิงค์ไปยังหน้าที่อธิบายไปป์ใช้ภายใน awk?
Wildcard

@ Wildcard คุณสามารถตรวจสอบหน้าคู่มืออย่างเป็นทางการหรือสีรองพื้นนี้ได้
lapo

4

แฮ็ก แต่มีประสิทธิภาพ: เพิ่ม0ไปที่บรรทัดส่วนหัวทั้งหมดและ1ไปยังบรรทัดอื่นทั้งหมดก่อนที่จะเรียงลำดับ ตัดอักขระตัวแรกหลังจากเรียงลำดับ

… |
awk '{print (NR <= 2 ? "0 " : "1 ") $0}' |
sort -k 1 -k… |
cut -b 3-

3

นี่คือเสียงเพอร์เพิลเส้นวิเศษที่คุณสามารถส่งออกผ่านท่อเพื่อเรียงลำดับทุกอย่าง แต่ให้บรรทัดแรกอยู่ด้านบน: perl -e 'print scalar <>, sort <>;'


2

ฉันลองใช้command | {head -1; sort; }วิธีแก้ปัญหาและสามารถยืนยันได้ว่าจริง ๆ แล้วมันทำให้สิ่งต่าง ๆ - headอ่านในหลาย ๆ บรรทัดจากท่อจากนั้นส่งออกเพียงคนแรก ดังนั้นเอาต์พุตที่เหลือซึ่งhead ไม่ได้อ่านจะถูกส่งผ่านไปยัง - sortไม่เหลือเอาต์พุตที่เริ่มต้นจากบรรทัดที่ 2!

ผลลัพธ์คือคุณไม่มีบรรทัด (และหนึ่งบรรทัดบางส่วน!) ที่อยู่ในจุดเริ่มต้นของเอาต์พุตคำสั่งของคุณ (ยกเว้นคุณยังมีบรรทัดแรก) - ข้อเท็จจริงที่ง่ายต่อการยืนยันโดยการเพิ่มไพพ์ไปwcที่ส่วนท้ายของ ไปป์ไลน์ข้างต้น - แต่นั่นเป็นเรื่องยากเป็นพิเศษหากคุณไม่พบสิ่งนี้! ฉันใช้เวลาอย่างน้อย 20 นาทีในการพยายามหาสาเหตุว่าทำไมฉันถึงมีบางส่วน (100 ไบต์แรกหรือถูกตัดออกไป) ในผลลัพธ์ก่อนที่จะแก้ไข

สิ่งที่ฉันทำลงไปซึ่งใช้งานได้อย่างสวยงามและไม่ต้องการการรันคำสั่งสองครั้งคือ:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile
sed 1d $myfile | sort

rm $myfile

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

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile > outputfile
sed 1d $myfile | sort >> outputfile

rm $myfile

คุณสามารถใช้headbuiltin หรือlineยูทิลิตีksh93 (บนระบบที่ยังมีอยู่) หรือgnu-sed -u qหรือIFS=read -r line; printf '%s\n' "$line"ที่อ่านอินพุตทีละหนึ่งไบต์เพื่อหลีกเลี่ยงปัญหานั้น
Stéphane Chazelas

1

ฉันคิดว่านี่เป็นวิธีที่ง่ายที่สุด

ps -ef | ( head -n 1 ; sort )

หรือสิ่งนี้อาจเร็วกว่าเนื่องจากไม่ได้สร้างเชลล์ย่อย

ps -ef | { head -n 1 ; sort ; }

การใช้งานที่ยอดเยี่ยมอื่น ๆ

สับเปลี่ยนบรรทัดหลังแถวส่วนหัว

cat file.txt |  ( head -n 1 ; shuf )

ย้อนกลับบรรทัดหลังจากแถวส่วนหัว

cat file.txt |  ( head -n 1 ; tac )

2
ดูunix.stackexchange.com/questions/11856/... นี่ไม่ใช่ทางออกที่ดี
Wildcard

1
ไม่ทำงานcat file | { head -n 1 ; sort ; } > file2แสดงเฉพาะหัว
Peter Krauss

0
command | head -1; command | tail -n +2 | sort

4
สิ่งนี้เริ่มต้นcommandสองครั้ง ดังนั้นจึงถูก จำกัด ให้ใช้คำสั่งเฉพาะบางอย่าง อย่างไรก็ตามสำหรับpsคำสั่งที่ร้องขอในตัวอย่างมันจะทำงาน
2557

0

ง่ายและตรงไปตรงมา!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> 'n' ระบุหมายเลขบรรทัดและ 'd' หมายถึงการลบ

1
เช่นเดียวกับที่ jofel แสดงความคิดเห็นเมื่อหนึ่งปีครึ่งที่ผ่านมากับคำตอบของ Sarva สิ่งนี้เริ่มต้นขึ้นcommandสองครั้ง ดังนั้นจึงไม่เหมาะสำหรับใช้ในท่อ
Wildcard

0

wผมมาที่นี่มองหาวิธีแก้ปัญหาสำหรับคำสั่ง คำสั่งนี้แสดงรายละเอียดของผู้ที่เข้าสู่ระบบและสิ่งที่พวกเขากำลังทำ

หากต้องการแสดงผลลัพธ์ที่เรียง แต่ส่วนหัวถูกเก็บไว้ที่ด้านบน (มี 2 บรรทัดส่วนหัว) ฉันจึงเลือก:

w | head -n 2; w | tail -n +3 | sort

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

โปรดทราบว่าtail -n +3หมายถึง 'แสดงทุกบรรทัดจากที่ 3 เป็นต้นไป' (ดูman tailรายละเอียด)


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