Có cách nào để bỏ qua các dòng tiêu đề trong một loại UNIX không?


102

Tôi có một tệp trường có chiều rộng cố định mà tôi đang cố gắng sắp xếp bằng tiện ích sắp xếp UNIX (Cygwin, trong trường hợp của tôi).

Vấn đề là có một tiêu đề hai dòng ở đầu tệp đang được sắp xếp ở cuối tệp (vì mỗi dòng tiêu đề bắt đầu bằng dấu hai chấm).

Có cách nào để cho biết sắp xếp "chuyển hai dòng đầu tiên cho đến không được sắp xếp" hoặc chỉ định thứ tự sắp xếp các dòng dấu hai chấm lên trên cùng không - các dòng còn lại luôn bắt đầu bằng số có 6 chữ số (thực sự là khóa I đang sắp xếp trên) nếu điều đó có ích.

Thí dụ:

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

nên sắp xếp thành:

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

Đối với bản ghi: dòng lệnh tôi đang sử dụng cho đến nay là "sort -t \\ -k1.1,1.6 <file>" [dữ liệu có thể chứa khoảng trắng, nhưng sẽ không bao giờ chứa dấu gạch chéo ngược]
Rob Gilliam

Câu trả lời:


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

Các dấu ngoặc đơn tạo ra một vỏ con, bao bọc stdout để bạn có thể đặt hoặc chuyển hướng nó như thể nó đến từ một lệnh duy nhất.


Cảm ơn; Tôi chấp nhận câu trả lời này vì nó có vẻ đầy đủ và ngắn gọn nhất (và tôi hiểu nó đang làm gì!) - nó phải là "head -n 2", tuy nhiên :-)
Rob Gilliam

1
Cảm ơn, đã sửa phần 'đầu'.
BobS

4
Có cách nào để phiên bản này hoạt động trên dữ liệu tổng hợp không? Tôi đã thử với tee >(head -n $header_size) | tail -n +$header_size | sort, nhưng đầu dường như chạy theo tail|sortđường ống, vì vậy cuối cùng tiêu đề sẽ được in. Đây là điều kiện xác định hay một chủng tộc?
Damien Pollet

Bạn có thể ghép một thứ gì đó với nhau mà bạn sử dụng catđể chuyển hướng stdin đến một tệp tạm thời, sau đó chạy lệnh trên trên tệp mới đó, nhưng nó bắt đầu trở nên xấu đến mức có lẽ tốt hơn nên sử dụng một trong các giải pháp dựa trên awk được cung cấp trong các phản hồi khác.
BobS

@DamienPollet: Hãy xem câu trả lời của Dave .
Jonathan Leffler

63

Nếu bạn không ngại sử dụng awk, bạn có thể tận dụngawk khả năng ống tích hợp của

ví dụ.

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

Điều này sẽ in nguyên văn hai dòng đầu tiên và chuyển phần còn lại qua sort .

Lưu ý rằng điều này có lợi thế rất cụ thể là có thể sắp xếp có chọn lọc các phần của đầu vào dạng ống. tất cả các phương pháp khác được đề xuất sẽ chỉ sắp xếp các tệp thuần túy có thể được đọc nhiều lần. Điều này hoạt động trên bất cứ điều gì.


2
Rất hay, và nó hoạt động với các đường ống tùy ý, không chỉ các tệp!
lapo

4
Đẹp, awk không bao giờ ngừng làm tôi ngạc nhiên. Ngoài ra, bạn không cần $0, printlà đủ.
nachocab

1
@SamWatkins freeseek của câu trả lời là ít xấu xí.
.

Tùy chọn -r làm gì để sắp xếp? Đây có phải là sắp xếp ngược lại không?
gvrocha

32

Đây là một phiên bản hoạt động trên dữ liệu đường ống:

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

Nếu tiêu đề của bạn có nhiều dòng:

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

Giải pháp này là từ đây


9
đẹp. đối với trường hợp tiêu đề đơn, tôi sử dụng extract_data | (read h; echo "$h"; sort) nó đủ ngắn để nhớ. ví dụ của bạn bao gồm nhiều trường hợp cạnh hơn. :) Đây là câu trả lời tốt nhất. hoạt động trên đường ống. không có.
.

1
Ok, tôi đã phân tích điều này và có vẻ như bash đi đến độ dài đặc biệt để làm cho nó hoạt động. Nói chung, nếu bạn viết mã này bằng ngôn ngữ C hoặc ngôn ngữ khác, nó sẽ không hoạt động vì stdio sẽ đọc nhiều hơn chỉ dòng tiêu đề đầu tiên. Nếu bạn chạy nó trên một tệp có thể tìm kiếm, bash đọc một đoạn lớn hơn (128 byte trong thử nghiệm của tôi), sau đó tuần tự trở lại sau khi kết thúc dòng đầu tiên. Nếu bạn chạy nó trên một đường ống, bash đọc từng ký tự một cho đến khi nó đi qua cuối dòng.
Sam Watkins

Đẹp! Nếu bạn chỉ muốn ăn tiêu đề, nó thậm chí còn dễ dàng hơn để nhớ:extract_data | (read; sort)
Jason Suárez

Cái này gần như hoàn hảo nhưng bạn cần sử dụng "IFS = read" thay vì "read" để giữ khoảng trắng ở đầu và cuối.
Stanislav German-Evtushenko

6
Đây nên là câu trả lời được chấp nhận theo ý kiến ​​của tôi. Đơn giản, ngắn gọn và linh hoạt hơn ở chỗ nó cũng hoạt động trên dữ liệu đường ống.
Paul I

12

Trong những trường hợp đơn giản, sedcó thể thực hiện công việc một cách thanh lịch:

    your_script | (sed -u 1q; sort)

hoặc tương đương,

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

Chìa khóa nằm trong 1q- in dòng đầu tiên (tiêu đề) và thoát (để phần còn lại của đầu vào sort).

Đối với ví dụ đã cho, 2qsẽ thực hiện thủ thuật.

Công -utắc (không đệm) là bắt buộc đối với nhữngsed s (đặc biệt là GNU) mà nếu không sẽ đọc đầu vào theo từng phần, do đó sử dụng dữ liệu mà bạn muốn xem qua sort.


1
Xin chào, @Andrea; chào mừng bạn đến với Stack Overflow. Tôi e rằng câu trả lời của bạn không hoạt động, ít nhất là không hiệu quả khi tôi đang thử nghiệm nó trong Git Bash trên Windows (Tôi đã chuyển từ Cygwin, trình bao mà tôi đã sử dụng một công việc khác 6 năm trước). Lệnh sed kéo tất cả dữ liệu ra khỏi stdin, không để lại dữ liệu nào để chuyển sang sắp xếp. Thử thay đổi lệnh thành cat your_data | (sed 1q; wc -l) để xem ý tôi là gì.
Rob Gilliam

1
Điều này có thể hoạt động nếu bạn chuyển đầu vào lần thứ hai vào lệnh sed, như sau: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> sorted.csv
Harry Cramer

8

Bạn có thể sử dụng tail -n +3 <file> | sort ...(đuôi sẽ xuất nội dung tệp từ dòng thứ 3).


4
head -2 <your_file> && nawk 'NR>2' <your_file> | sort

thí dụ:

> cat temp
10
8
1
2
3
4
5
> head -2 temp && nawk 'NR>2' temp | sort -r
10
8
5
4
3
2
1

3

Chỉ mất 2 dòng mã ...

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

Đối với dữ liệu số, -n là bắt buộc. Đối với sắp xếp alpha, -n không bắt buộc.

Tệp ví dụ:
$ cat test.txt

tiêu đề
8
5
100
1
-1

Kết quả:
$ cat a.tmp

tiêu đề
-1
1
5
8
100


1
Về cơ bản đây không phải là câu trả lời giống với câu trả lời được chấp nhận sao? (Ngoại trừ cách tiếp cận của BobS đặt kết quả trên stdout, cho phép bạn gửi kết quả qua các bộ lọc khác trước khi được ghi vào tệp, nếu cần)
Rob Gilliam

1

Vì vậy, đây là một hàm bash trong đó các đối số giống hệt như sắp xếp. Hỗ trợ các tệp và đường ống.

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
}

Làm thế nào nó hoạt động. Dòng này kiểm tra xem có ít nhất một đối số và đối số cuối cùng có phải là tệp hay không.

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

Thao tác này sẽ lưu tệp vào đối số riêng biệt. Vì chúng ta sắp xóa bỏ cuộc tranh cãi cuối cùng.

        local file=${@: -1}

Ở đây chúng tôi loại bỏ đối số cuối cùng. Vì chúng tôi không muốn chuyển nó như một đối số sắp xếp.

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

Cuối cùng, chúng tôi thực hiện phần awk, truyền các đối số (trừ đối số cuối cùng nếu đó là tệp) để sắp xếp trong awk. Điều này được Dave đề xuất ban đầu và được sửa đổi để có các đối số sắp xếp. Chúng tôi dựa trên thực tế là $filesẽ trống nếu chúng tôi đang sử dụng đường ống, do đó bị bỏ qua.

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

Ví dụ sử dụng với một tệp được phân tách bằng dấu phẩy.

$ 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

Với 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)

tiền giả hệ thống có cài đặt Python (tôi không)
Rob Gilliam

0

Đây là một hàm bash shell bắt nguồn từ các câu trả lời khác. Nó xử lý cả tệp và đường ống. Đối số đầu tiên là tên tệp hoặc '-' cho stdin. Các đối số còn lại được chuyển để sắp xếp. Một vài ví dụ:

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

Hàm shell:

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

Câu trả lời này giống với câu trả lời của Ian Sherbin nhưng cách triển khai của tôi là: -

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 

Điều này sẽ làm những gì bạn muốn.


1) Thao tác này chỉ xóa dòng tiêu đề và sắp xếp phần còn lại, nó không sắp xếp mọi thứ bên dưới dòng tiêu đề để giữ nguyên tiêu đề. 2) nó chỉ loại bỏ dòng đầu tiên, khi tiêu đề thực sự là hai dòng (đọc câu hỏi). 3) Tại sao bạn sử dụng "cat file_name.txt | sed 1d" khi "sed 1d <file_name.txt" hoặc thậm chí chỉ "sed 1d file_name.txt" cũng có tác dụng tương tự?
Rob Gilliam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.