파일의 처음 몇 줄과 마지막 몇 줄을 표시하는 명령


23

행이 많은 파일이 있고 각 행에는 시작과 같이 타임 스탬프가 있습니다.

[Thread-3] (21/09/12 06:17:38:672) logged message from code.....

따라서이 로그 파일에서 자주 2 가지를 확인합니다.

  1. 전역 조건과 시작 시간이있는 처음 몇 개의 행도 제공됩니다.
  2. 마지막 정보는 다른 정보와 함께 종료 상태입니다.

파일의 처음과 마지막 몇 줄만 표시 할 수있는 빠르고 편리한 단일 명령이 있습니까?


2
글로벌 조건은 무엇이며 효과가 head and tail없습니까?
데이지

그것은 내 로그 파일의 일부입니다. 나는 정교 해 지려고 노력했다. 당신은 그것을 무시할 수 있습니다.
mtk

당신의 해결책은 나에게 잘 보입니다. 더 편리하게하려면 셸 함수로 만드십시오 (별칭도 가능).
vonbrand

@vonbrand 문제는 내가 모른다는 것입니다N
Bernhard

@Bernhard, 나는 sed(1)전문가가 아니지만 나중에 사용하기 위해 물건을 숨길 수있는 방법이 있습니다. 어쩌면 거기를보아야 할 수도 있습니다. OTOH, 나는 자주 익숙해지면 Perl (또는 무엇이든) 스크립트를 작성하여 아마도 익숙 할 것입니다.
vonbrand

답변:


12

하나의 명령으로 sed또는 awk을 사용 하여 만들 수 있습니다 . 그러나 속도가 느슨해 져서 원인이 sed되어 awk어쨌든 전체 파일을 실행해야합니다. 속도 관점에서 볼 때마다 tail+ 를 조합하여 기능을 사용하는 것이 훨씬 좋습니다 head. 입력이 파이프 인 경우 작동하지 않는 단점이 있지만 쉘이 지원하는 경우 프로세스 대체를 사용할 수 있습니다 (아래 예 참조).

first_last () {
    head -n 10 -- "$1"
    tail -n 10 -- "$1"
}

그냥 시작으로

first_last "/path/to/file_to_process"

프로세스 대체를 진행하려면 (bash, zsh, ksh와 같은 쉘만 해당) :

first_last <( command )

추신. grep"전역 조건"이 존재하는지 확인 하기 위해를 추가 할 수도 있습니다 .


-n 10기본값이 아닙니까?
l0b0

@ l0b0 예, 기본값입니다. -n 10여기에 필요하지 않습니다.
돌진

20

@rush는 head + tail을 사용하면 큰 파일에 더 효율적이지만 작은 파일 (<20 줄)의 경우 일부 줄이 두 번 출력 될 수 있습니다.

{ head; tail;} < /path/to/file

똑같이 효율적이지만 위의 문제는 없습니다.


러시 솔루션과 달리 POSIX 셸에서는 작동하지 않습니다.
Marco

2
@Marco Huh? 여기에는 POSIX 구문 만 사용됩니다. 무엇이 잘못되고 있습니까?
Gilles 'SO- 악마 그만'

2
@Gilles 공간을 놓쳤다 : {head; tail;} < filezsh에서는 작동하지만 sh에서는 실패합니다. { head; tail;} < file항상 작동합니다. 소음이 유감입니다.
Marco

@Marco, 그것에 문제가 있다면, head쉘이 아니라에 있습니다. POSIX는 head일반 파일의 경우 10 줄을 지나서 파일에 커서를 두어야합니다. POSIX가 아닌 head구현 (이 경우에는 GNU 구식의 매우 오래된 버전이 적합하지 않았지만 수십 년 동안 이야기했습니다) 또는 파일을 찾을 수없는 경우 (이름 지정된 파이프 또는 소켓과 같은 경우) 다른 솔루션도 같은 문제가 있습니다).
Stéphane Chazelas

1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
Stéphane Chazelas

9

{ head; tail; }솔루션은 파이프 (또는 소켓 또는 사용 할 수없는 다른 파일)에서 작동 head하지 않습니다. 블록으로 읽을 때 너무 많은 데이터를 소비하고 파이프에서 다시 커서를 검색 할 수 없기 때문에 tail의미 하는 것 이상으로 커서를 파일 안에 남겨 둘 수 없기 때문 입니다 선택합니다.

따라서 쉘과 같이 한 번에 한 문자 씩 읽는 도구를 사용할 수 있습니다 read(여기서는 헤드 라인과 테일 라인의 수를 인수로 사용하는 함수 사용).

head_tail() {
  n=0
  while [ "$n" -lt "$1" ]; do
    IFS= read -r line || { printf %s "$line"; break; }
    printf '%s\n' "$line"
    n=$(($n + 1))
  done
  tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5

또는 tailawk에서 예를 들어 다음과 같이 구현하십시오 .

head_tail() {
  awk -v h="$1" -v t="${2-$1}" '
    {l[NR%t]=$0}
    NR<=h
    END{
      n=NR-t+1
      if(n <= h) n = h+1
      for (;n<=NR;n++) print l[n%t]
    }'
}

sed:

head_tail() {
  sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),\$!{N;b1" -e '}' -e 'N;D'
}

(일부 sed구현에서는 패턴 공간의 크기에 대한 제한이 낮으므로 테일 라인 수의 큰 값에는 실패합니다).


4

bash프로세스 대체를 사용 하여 다음을 수행 할 수 있습니다.

make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null

줄이 순서대로 보장되지는 않지만 약 8kB보다 긴 파일의 경우에는 그럴 가능성이 높습니다. 이 8kB 컷오프는 읽기 버퍼의 일반적인 크기이며 | {head; tail;}작은 파일에서는 작동하지 않는 이유와 관련이 있습니다.

cat >/dev/null킵 할 필요가 head살아 파이프 라인을. 그렇지 않으면 tee일찍 종료되고에서 출력을 얻는 동안 tail입력이 아닌 중간 부분에서 출력됩니다.

마지막으로 왜 >/dev/null대신에 tail다른 곳으로 옮기는가 |? 다음과 같은 경우 :

make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2  # doesn't work

headstdout은 tail콘솔 이 아닌 파이프로 파이프에 공급되며 이는 우리가 원하는 것이 아닙니다.


head 또는 tail이 원하는 출력을 작성하면 stdin을 닫고 종료합니다. SIGPIPE가 시작된 곳입니다. 일반적으로 이것은 좋은 일이며 나머지 출력을 버리고 있으므로 파이프의 다른 쪽에서 파이프를 생성하는 데 계속 시간을 소비 할 이유가 없습니다.
derobert

주문이 유지 될 가능성이 높은 이유는 무엇입니까? tail더 오래 작동해야 하기 때문에 아마도 큰 파일 일 것입니다 . 그러나 짧은 입력의 경우 약 절반의 시간이 걸리지 않을 것으로 예상합니다.
Gilles 'SO- 악의를 멈추십시오'

SIGPIPE tee >(head) >(tail)는 같은 이유로 ( >(...)현재는 zsh와 bash 모두에서 지원되는 ksh 기능 임) 파이프를 사용합니다. 할 수는 ... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)있지만 에서 깨진 파이프 오류 메시지가 계속 표시 됩니다 tee.
Stéphane Chazelas

내 시스템 (bash는 4.2.37,로 coreutils 8.13)에서 tailSIGPIPE,하지에 의해 살해되는 하나입니다 tee, 그리고 tail파이프에 기록되지 않습니다. 따라서이어야 kill()합니다. 그리고 이것은 |구문을 사용할 때만 발생 합니다. stracetee전화를하지 않습니다 kill()... 그래서 어쩌면 bash?
Jander

1
@Jander, 8k 이상 먹이기seq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
Stéphane Chazelas

3

사용 ed(하지만 전체 파일을 RAM으로 읽습니다) :

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file

더 짧은 :ed -s file <<< $'11,$-10d\n,p\nq\n'
don_crissti

2

인수를 사용할 수 있도록 Stephane의 첫 번째 솔루션 (본느 또는 POSIX 셸에서 작동) :

head_tail() {
    head "$@";
    tail "$@";
}

이제 당신은 이것을 할 수 있습니다 :

head_tail -n 5 < /path/to/file

물론 이것은 하나의 파일 만보 고 Stephane의 솔루션과 마찬가지로 일반 (찾을 수있는) 파일에서만 작동한다고 가정합니다.


2

GNU 의 -u( --unbuffered) 옵션을 sed사용 sed -u 2q하면 다음에 대한 버퍼되지 않은 대안으로 사용할 수 있습니다 head -n2.

$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100

(head -n2;tail -n2)마지막 행이 head다음에 의해 소비되는 입력 블록의 일부인 경우 실패합니다 .

$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2

이것이 최고의 답변이어야합니다! 매력처럼 작동합니다!
벤 Usman

1

나는 오늘 스트림의 정면에서 마지막 줄과 몇 줄만 필요로하는 것과 같은 것을 만났고 다음을 생각해 냈습니다.

sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'

나는 이것을 읽습니다 : 첫 번째 줄의 내용으로 보류 공간을 초기화하고, 보류 공간에 2-3 행을 추가하고, EOF에서 마지막 행을 보류 공간에 추가하고 보류 및 패턴 공간을 교환하고 패턴을 인쇄하십시오 공간.

아마도 sed내가 가지고있는 것보다 더 많은 것을 가진 사람 은이 질문에 표시된 스트림 의 마지막 줄 을 인쇄하기 위해 이것을 일반화하는 방법을 알아낼 수 는 있지만 그것을 필요로하지 않았고 $주소를 기반으로 수학을 수행하는 쉬운 방법을 찾을 수 없었습니다 에서 sed혹은 때 마지막 몇 줄이에 너무 홀드 공간을 관리하여 EOF도달한다.


1

Perl을 설치 한 경우 시도해 볼 수 있습니다.

perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'

이것은 대부분의 파일에서 작동하지만 처리하기 전에 전체 파일을 메모리로 읽습니다. Perl 슬라이스에 익숙하지 않은 경우, 대괄호로 묶인 "0"은 "첫 번째 줄 가져 오기"를 의미하고 "-3 ...- 1"은 "마지막 세 줄 가져 오기"를 의미합니다. 필요에 따라 두 가지를 모두 조정할 수 있습니다. 정말 큰 파일을 처리해야하는 경우 ( '큰'것은 RAM 및 스왑 크기에 따라 다를 수 있음) 다음을 수행 할 수 있습니다.

perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'

반복 할 때마다 슬라이스를 만들기 때문에 다소 느려질 수 있지만 파일 크기에 독립적입니다.

두 명령 모두 파이프와 일반 파일 모두에서 작동해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.