UNIX 정렬에서 헤더 행을 무시하는 방법이 있습니까?


102

UNIX (제 경우에는 Cygwin) 정렬 유틸리티를 사용하여 정렬하려는 고정 너비 필드 파일이 있습니다.

문제는 파일 맨 위에 두 줄 헤더가 있고 파일 맨 아래로 정렬된다는 것입니다 (각 헤더 행이 콜론으로 시작됨).

정렬을 "정렬되지 않은 상태에서 처음 두 줄을 전달"하거나 콜론 줄을 맨 위로 정렬하는 순서를 지정하는 방법이 있습니까? 나머지 줄은 항상 6 자리 숫자로 시작합니다 (실제로는 I 그게 도움이된다면 정렬 중입니다.

예:

: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

답변:


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

괄호는 단일 명령에서 나온 것처럼 파이프하거나 리디렉션 할 수 있도록 stdout을 감싸는 서브 쉘을 만듭니다.


감사; 그것은 가장 완벽하고 간결한 보인다 나는이 대답을 받아들이는거야 (그리고 나는 무엇을하고 있는지 이해!) - 그것은 "2 -n 머리", :-) 생각해야한다
롭 길리엄

1
감사합니다. '머리'부분을 수정했습니다.
BobS

4
이 버전이 파이프 된 데이터에서 작동하도록하는 방법이 있습니까? 으로 시도 tee >(head -n $header_size) | tail -n +$header_size | sort했지만 머리가 tail|sort파이프 뒤에서 실행되는 것처럼 보이 므로 헤더가 결국 인쇄됩니다. 이것이 결정적입니까? 아니면 경쟁 조건입니까?
Damien Pollet 2014

catstdin을 임시 파일로 리디렉션하는 데 사용 하는 부분을 모은 다음 해당 새 파일에서 위의 명령을 실행할 수 있지만, 다음과 같은 awk 기반 솔루션 중 하나를 사용하는 것이 더 나을만큼 추악하기 시작했습니다. 다른 응답.
BobS 2014

@DamienPollet : Dave대답을 참조하십시오 .
Jonathan Leffler

63

을 사용해도 괜찮다면 의 내장 파이프 기능 awk을 활용할 수 있습니다.awk

예.

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

이렇게하면 처음 두 줄이 그대로 인쇄되고 나머지 줄은 sort.

이것은 파이프 된 입력의 일부를 선택적으로 정렬 할 수 있다는 매우 특별한 이점이 있습니다. 제안 된 다른 모든 방법은 여러 번 읽을 수있는 일반 파일 만 정렬합니다. 이것은 무엇이든 작동합니다.


2
매우 훌륭하고 파일뿐만 아니라 임의의 파이프에서도 작동합니다!
lapo 2014.11.24

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) 기억하기에 충분히 짧습니다. 귀하의 예는 더 많은 경우를 다룹니다. :) 이것이 최고의 답변입니다. 파이프에서 작동합니다. awk.
fess.

1
좋아, 나는 이것을 추적했고 bash는이 작업을 수행하기 위해 특별한 길이로 이동하는 것 같습니다. 일반적으로 이것을 C 또는 다른 언어로 코딩하면 stdio가 첫 번째 헤더 행 이상을 읽으므로 작동하지 않습니다. 검색 가능한 파일에서 실행하면 bash는 더 큰 청크 (내 테스트에서 128 바이트)를 읽은 다음 lseeks는 첫 번째 줄 끝 뒤로 돌아갑니다. 파이프에서 실행하면 bash는 줄 끝을 통과 할 때까지 한 번에 한 문자 씩 읽습니다.
Sam Watkins 2015

좋은! 그냥 헤더를 먹고 싶은 경우, 기억에도 쉽게 :extract_data | (read; sort)
제이슨 수아레스

이것은 거의 완벽하지만 선행 및 후행 공백을 유지하려면 "읽기"대신 "IFS = 읽기"를 사용해야합니다.
Stanislav German-Evtushenko 2017-06-23

6
이것은 제 생각에 받아 들여진 대답이어야합니다. 파이프 데이터에서도 작동한다는 점에서 간단하고 간결하며 유연합니다.
Paul I

12

간단한 경우 sed에는 작업을 우아하게 수행 할 수 있습니다.

    your_script | (sed -u 1q; sort)

또는 동등하게

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

키는 1q-print first line (header) 및 종료 (나머지 입력은 sort)에 있습니다.

주어진 예 2q에서 트릭을 수행합니다.

-u스위치 (버퍼링)이 그 필요 sed당신이 통과하도록 (특히, GNU의) 그렇지 않으면 덩어리에서 입력을 읽을 것입니다하여 데이터를 소모의 sort대신.


1
안녕하세요, @Andrea; Stack Overflow에 오신 것을 환영합니다. 적어도 Windows의 Git Bash에서 테스트 할 때 답변이 작동하지 않는 것이 두렵습니다 (6 년 전에 다른 작업을 사용하던 쉘인 Cygwin에서 이동했습니다). 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

8

사용할 수 있습니다 tail -n +3 <file> | sort ...(tail은 세 번째 줄에서 파일 내용을 출력합니다).


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

예:

> 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

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
이것은 기본적으로 받아 들여진 대답과 같은 대답이 아닙니까? (필요한 경우 가볍게 침의 접근 방식을 제외하고, 당신이 파일에 기록되기 전에 다른 필터를 통해 결과를 보낼 수 있도록, 표준 출력에 결과를두고)
롭 길리엄

1

그래서 여기에 인수가 정확히 정렬과 같은 bash 함수가 있습니다. 지원 파일 및 파이프.

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)

시스템이 파이썬이 설치되어 사전은 - 가정한다 (광산하지 않습니다)
롭 길리엄

0

다음은 다른 답변에서 파생 된 bash 셸 함수입니다. 파일과 파이프를 모두 처리합니다. 첫 번째 인수는 파일 이름 또는 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) "sed 1d <file_name.txt"또는 "sed 1d file_name.txt"가 동일한 효과를 가질 때 왜 "cat file_name.txt | sed 1d"를 사용합니까?
Rob Gilliam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.