มีวิธีละเว้นบรรทัดส่วนหัวในการจัดเรียง UNIX หรือไม่?


102

ฉันมีไฟล์ฟิลด์ความกว้างคงที่ซึ่งฉันกำลังพยายามจัดเรียงโดยใช้ยูทิลิตี้การจัดเรียง UNIX (Cygwin ในกรณีของฉัน)

ปัญหาคือมีส่วนหัวสองบรรทัดที่ด้านบนของไฟล์ซึ่งจะถูกจัดเรียงไว้ที่ด้านล่างของไฟล์ (เนื่องจากแต่ละบรรทัดส่วนหัวขึ้นต้นด้วยเครื่องหมายจุดคู่)

มีวิธีบอก sort หรือไม่ว่า "ส่งสองบรรทัดแรกไปยังไม่เรียงลำดับ" หรือระบุลำดับที่เรียงลำดับบรรทัดโคลอนไปด้านบน - บรรทัดที่เหลือจะเริ่มต้นด้วยตัวเลข 6 หลักเสมอ (ซึ่งจริงๆแล้วคือคีย์ I 'm sorting on) ถ้าช่วยได้

ตัวอย่าง:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
500123TSTMY_RADAR00
222334NOTALINEOUT01
477821USASHUTTLES21
325611LVEANOTHERS00

ควรจัดเรียงเป็น:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
222334NOTALINEOUT01
325611LVEANOTHERS00
477821USASHUTTLES21
500123TSTMY_RADAR00

สำหรับบันทึก: บรรทัดคำสั่งที่ฉันใช้จนถึงตอนนี้คือ "sort -t \\ -k1.1,1.6 <file>" [ข้อมูลสามารถมีช่องว่างได้ แต่จะไม่มีแบ็กสแลช]
Rob Gilliam

คำตอบ:


127
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

วงเล็บจะสร้าง subshell โดยรวม stdout เพื่อให้คุณสามารถไพพ์หรือเปลี่ยนทิศทางได้ราวกับว่ามันมาจากคำสั่งเดียว


ขอบคุณ; ฉันยอมรับคำตอบนี้เนื่องจากดูเหมือนจะสมบูรณ์และกระชับที่สุด (และฉันเข้าใจว่ามันกำลังทำอะไรอยู่!) - ควรเป็น "head -n 2" แม้ว่า :-)
Rob Gilliam

1
ขอบคุณแก้ไขส่วน 'หัว'
BobS

4
มีวิธีให้เวอร์ชันนี้ทำงานกับข้อมูลแบบ piped-in หรือไม่ ฉันลองใช้tee >(head -n $header_size) | tail -n +$header_size | sortแล้ว แต่ดูเหมือนว่าหัวจะวิ่งไปตามtail|sortท่อดังนั้นส่วนหัวจึงถูกพิมพ์ในตอนท้าย นี่เป็นปัจจัยกำหนดหรือเงื่อนไขการแข่งขัน?
Damien Pollet

คุณอาจรวมบางสิ่งที่คุณใช้catเพื่อเปลี่ยนเส้นทาง stdin ไปยังไฟล์ชั่วคราวจากนั้นเรียกใช้คำสั่งด้านบนในไฟล์ใหม่นั้น แต่มันเริ่มน่าเกลียดพอที่จะใช้โซลูชันที่ใช้ awk ได้ดีกว่า คำตอบอื่น ๆ
BobS

ดู: @DamienPollet เดฟ 's คำตอบ
Jonathan Leffler

66

หากคุณไม่สนใจที่จะใช้awkคุณสามารถใช้ประโยชน์จากawkความสามารถของท่อในตัว

เช่น.

extract_data | awk 'NR<3{print $0;next}{print $0| "sort -r"}' 

sortพิมพ์นี้สองบรรทัดแรกคำต่อคำและท่อผ่านส่วนที่เหลือ

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


2
ดีมากและใช้ได้กับไพพ์โดยพลการไม่ใช่แค่ไฟล์เท่านั้น!
Lapo

4
สวยงาม awk ไม่เคยหยุดทำให้ฉันประหลาดใจ นอกจากนี้คุณไม่จำเป็นต้อง$0, printเป็นพอ
nachocab

1
@SamWatkins freeseek ของ คำตอบที่น่าเกลียดน้อย
fess.

ตัวเลือก -r ทำอะไรเพื่อจัดเรียง? นี่ควรจะเป็นแบบย้อนกลับหรือไม่?
gvrocha

32

นี่คือเวอร์ชันที่ใช้งานได้กับข้อมูลไปป์:

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

หากส่วนหัวของคุณมีหลายบรรทัด:

(for i in $(seq $HEADER_ROWS); do read -r; printf "%s\n" "$REPLY"; done; sort)

วิธีแก้ปัญหานี้มาจากที่นี่


9
ดี. สำหรับกรณีส่วนหัวเดียวฉันใช้ extract_data | (read h; echo "$h"; sort) มันสั้นพอที่จะจำได้ ตัวอย่างของคุณครอบคลุมกรณี edge เพิ่มเติม :) นี่คือคำตอบที่ดีที่สุด ทำงานบนท่อ ไม่เป็นไร
fess.

1
ตกลงฉันรัดมันและดูเหมือนว่าการทุบตีจะมีความยาวเป็นพิเศษในการทำงานนี้ โดยทั่วไปหากคุณเขียนโค้ดเป็นภาษา C หรือภาษาอื่นจะใช้ไม่ได้เพราะ stdio จะอ่านมากกว่าบรรทัดส่วนหัวแรก หากคุณเรียกใช้บนไฟล์ที่สามารถค้นหาได้ bash จะอ่านชิ้นส่วนที่ใหญ่กว่า (128 ไบต์ในการทดสอบของฉัน) จากนั้นจะกลับไปที่หลังสิ้นสุดบรรทัดแรก หากคุณรันบนไปป์ bash จะอ่านทีละอักขระจนกว่าจะผ่านจุดสิ้นสุดของบรรทัด
Sam Watkins

ดี! ถ้าคุณแค่อยากกินส่วนหัวจำง่ายกว่า:extract_data | (read; sort)
Jason Suárez

อันนี้เกือบจะสมบูรณ์แบบ แต่คุณต้องใช้ "IFS = read" แทน "read" เพื่อเว้นวรรคนำหน้าและต่อท้าย
Stanislav German-Evtushenko

6
นี่ควรเป็นคำตอบที่ยอมรับได้ในความคิดของฉัน เรียบง่ายกระชับและยืดหยุ่นมากขึ้นเนื่องจากยังทำงานกับข้อมูลแบบไปป์
Paul I

13

ในกรณีง่ายๆsedสามารถทำงานได้อย่างสง่างาม:

    your_script | (sed -u 1q; sort)

หรือเทียบเท่า

    cat your_data | (sed -u 1q; sort)

คีย์อยู่ใน1q- พิมพ์บรรทัดแรก (ส่วนหัว) และออก (ปล่อยให้ส่วนที่เหลือของอินพุตเป็นsort)

สำหรับตัวอย่างที่ระบุ 2qจะทำเคล็ดลับ

-uสวิทช์ (unbuffered) เป็นสิ่งจำเป็นสำหรับผู้ที่seds (สะดุดตา, GNU ของ) ที่อาจจะอ่านเข้าในชิ้นจึงต้องใช้ข้อมูลที่คุณต้องการที่จะผ่านไปsortแทน


1
สวัสดี @Andrea; ยินดีต้อนรับสู่ Stack Overflow ฉันกลัวว่าคำตอบของคุณจะใช้ไม่ได้ผลอย่างน้อยก็ไม่ใช่เมื่อฉันทดสอบใน Git Bash บน Windows (ฉันย้ายมาจาก Cygwin ซึ่งเป็นเชลล์ที่ฉันใช้งานอื่นเมื่อ 6 ปีก่อน) คำสั่ง sed จะดึงข้อมูลทั้งหมดออกจาก stdin โดยไม่ให้ข้อมูลส่งผ่านไปยังการจัดเรียง ลองเปลี่ยนคำสั่งเป็น cat your_data | (sed 1q; wc -l) เพื่อดูว่าฉันหมายถึงอะไร
Rob Gilliam

1
สิ่งนี้สามารถทำงานได้หากคุณส่งอินพุตเป็นครั้งที่สองไปยังคำสั่ง sed เช่น cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> sorted.csv
Harry Cramer



3

ใช้รหัสเพียง 2 บรรทัด ...

head -1 test.txt > a.tmp; 
tail -n+2 test.txt | sort -n >> a.tmp;

สำหรับข้อมูลตัวเลขจำเป็นต้องมี -n สำหรับการเรียงลำดับอัลฟาไม่จำเป็นต้องใช้ -n

ไฟล์ตัวอย่าง:
$ cat test.txt

ส่วนหัว
8
5
100
1
-1

ผลลัพธ์:
$ cat a.tmp

ส่วนหัว
-1
1
5
8
100


1
นี่ไม่ใช่คำตอบเดียวกับคำตอบที่ยอมรับหรือไม่? (ยกเว้นวิธีการของ BobS ทำให้ผลลัพธ์อยู่ใน stdout ทำให้คุณสามารถส่งผลลัพธ์ผ่านตัวกรองอื่น ๆ ก่อนที่จะเขียนลงไฟล์หากจำเป็น)
Rob Gilliam

1

นี่คือฟังก์ชันทุบตีที่อาร์กิวเมนต์เหมือนกับการเรียงลำดับ รองรับไฟล์และท่อ

function skip_header_sort() {
    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then
        local file=${@: -1}
        set -- "${@:1:$(($#-1))}"
    fi
    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file
}

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

    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then

ซึ่งจะบันทึกไฟล์เพื่อแยกอาร์กิวเมนต์ เนื่องจากเรากำลังจะลบอาร์กิวเมนต์สุดท้าย

        local file=${@: -1}

ที่นี่เราลบอาร์กิวเมนต์สุดท้าย เนื่องจากเราไม่ต้องการส่งเป็นอาร์กิวเมนต์แบบเรียงลำดับ

        set -- "${@:1:$(($#-1))}"

สุดท้ายเราทำส่วน awk ส่งอาร์กิวเมนต์ (ลบอาร์กิวเมนต์สุดท้ายหากเป็นไฟล์) เพื่อจัดเรียงใน awk สิ่งนี้ได้รับการแนะนำโดย Dave และแก้ไขเพื่อรับอาร์กิวเมนต์แบบเรียงลำดับ เราอาศัยความจริงที่ว่า$fileจะว่างเปล่าหากเราวางท่อจึงถูกเพิกเฉย

    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file

ตัวอย่างการใช้งานกับไฟล์ที่คั่นด้วยลูกน้ำ

$ cat /tmp/test
A,B,C
0,1,2
1,2,0
2,0,1

# SORT NUMERICALLY SECOND COLUMN
$ skip_header_sort -t, -nk2 /tmp/test
A,B,C
2,0,1
0,1,2
1,2,0

# SORT REVERSE NUMERICALLY THIRD COLUMN
$ cat /tmp/test | skip_header_sort -t, -nrk3
A,B,C
0,1,2
2,0,1
1,2,0

0

ด้วย Python:

import sys
HEADER_ROWS=2

for _ in range(HEADER_ROWS):
    sys.stdout.write(next(sys.stdin))
for row in sorted(sys.stdin):
    sys.stdout.write(row)

ก่อนที่ระบบจะติดตั้ง Python (ของฉันไม่ได้)
Rob Gilliam

0

นี่คือฟังก์ชัน bash shell ที่ได้มาจากคำตอบอื่น ๆ จัดการทั้งไฟล์และไปป์ อาร์กิวเมนต์แรกคือชื่อไฟล์หรือ "-" สำหรับ stdin อาร์กิวเมนต์ที่เหลือจะถูกส่งไปเพื่อจัดเรียง ตัวอย่างสองสามตัวอย่าง:

$ hsort myfile.txt
$ head -n 100 myfile.txt | hsort -
$ hsort myfile.txt -k 2,2 | head -n 20 | hsort - -r

ฟังก์ชั่นเชลล์:

hsort ()
{
   if [ "$1" == "-h" ]; then
       echo "Sort a file or standard input, treating the first line as a header.";
       echo "The first argument is the file or '-' for standard input. Additional";
       echo "arguments to sort follow the first argument, including other files.";
       echo "File syntax : $ hsort file [sort-options] [file...]";
       echo "STDIN syntax: $ hsort - [sort-options] [file...]";
       return 0;
   elif [ -f "$1" ]; then
       local file=$1;
       shift;
       (head -n 1 $file && tail -n +2 $file | sort $*);
   elif [ "$1" == "-" ]; then
       shift;
       (read -r; printf "%s\n" "$REPLY"; sort $*);
   else
       >&2 echo "Error. File not found: $1";
       >&2 echo "Use either 'hsort <file> [sort-options]' or 'hsort - [sort-options]'";
       return 1 ;
   fi
}

0

นี่เหมือนกับคำตอบของ Ian Sherbin แต่การใช้งานของฉันคือ: -

cut -d'|' -f3,4,7 $arg1 | uniq > filetmp.tc
head -1 filetmp.tc > file.tc;
tail -n+2 filetmp.tc | sort -t"|" -k2,2 >> file.tc;

-4
cat file_name.txt | sed 1d | sort 

สิ่งนี้จะทำในสิ่งที่คุณต้องการ


1) สิ่งนี้จะลบเฉพาะบรรทัดส่วนหัวและจัดเรียงส่วนที่เหลือ แต่จะไม่เรียงลำดับทุกอย่างที่อยู่ด้านล่างบรรทัดส่วนหัวทำให้ส่วนหัวไม่เสียหาย 2) จะลบบรรทัดแรกเท่านั้นเมื่อส่วนหัวเป็นสองบรรทัด (อ่านคำถาม) 3) ทำไมคุณถึงใช้ "cat file_name.txt | sed 1d" เมื่อ "sed 1d <file_name.txt" หรือแม้แต่ "sed 1d file_name.txt" ก็มีผลเหมือนกัน
Rob Gilliam
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.