측정된 시간 중 일부는 다음과 같습니다.

측정된 시간 중 일부는 다음과 같습니다.

cat큰 텍스트 파일(>2GB)이 있고 줄만 쓰고 싶다고 가정해 보겠습니다 X( Y예: 57890000 ~ 57890010).

내가 이해하는 한 파이프를 연결하거나 그 반대로 할 수 head있습니다 tail.

head -A /path/to/file | tail -B

또는 대안적으로

tail -C /path/to/file | head -D

그 중 A, B, CD파일의 행 수를 기준으로 계산할 수 있으며, XY.

그러나 이 접근 방식에는 두 가지 문제가 있습니다.

  1. A, B를 계산해야 합니다 C.D
  2. 명령은 pipe서로 전달될 수 있습니다.더 많은 것이 있습니다내가 읽고 싶은 줄보다 (예: 큰 파일의 중간에 몇 줄만 읽는 경우)

쉘에서 내가 원하는 라인만 처리하고 출력할 수 있는 방법이 있나요? (동시에 AND만 가능합니다 X.) Y?

답변1

해결책을 제안하고 싶지만 sed완전성을 위해

awk 'NR >= 57890000 && NR <= 57890010' /path/to/file

마지막 줄 뒤에서 잘라내려면:

awk 'NR < 57890000 { next } { print } NR == 57890010 { exit }' /path/to/file

속도 테스트(여기서는 macOS, 다른 시스템에서는 YMMV):

  • 100,000,000 라인 파일 생성seq 100000000 > test.in
  • 읽은 줄 수 50,000,000-50,000,010
  • 특별한 순서 없이 테스트
  • realbash내장된 시간 보고time
 4.373  4.418  4.395    tail -n+50000000 test.in | head -n10
 5.210  5.179  6.181    sed -n '50000000,50000010p;57890010q' test.in
 5.525  5.475  5.488    head -n50000010 test.in | tail -n10
 8.497  8.352  8.438    sed -n '50000000,50000010p' test.in
22.826 23.154 23.195    tail -n50000001 test.in | head -n10
25.694 25.908 27.638    ed -s test.in <<<"50000000,50000010p"
31.348 28.140 30.574    awk 'NR<57890000{next}1;NR==57890010{exit}' test.in
51.359 50.919 51.127    awk 'NR >= 57890000 && NR <= 57890010' test.in

이는 결코 정확한 벤치마크는 아니지만 각 명령의 상대 속도에 대한 좋은 아이디어를 제공할 만큼 차이가 충분히 크고 반복 가능*합니다.

*: 처음 두 개 사이의 합계를 sed -n p;q제외하면 head|tail본질적으로 동일해 보입니다 .

답변2

X부터 Y까지의 행(1부터 시작하는 번호)을 포함하려면 다음을 사용하십시오.

tail -n "+$X" /path/to/file | head -n "$((Y-X+1))"

tail첫 번째 X-1 줄은 읽고 삭제되며(이 문제는 해결되지 않음) 다음 줄은 읽고 인쇄됩니다. head요청된 행 수를 읽고 인쇄한 다음 종료합니다. 출구 head에서tail신호 파이프라인버퍼 크기(보통 몇 킬로바이트)보다 입력 파일에서 더 많은 라인을 읽지 않도록 신호를 보내고 죽습니다.

또는고리키푸르sed를 사용하는 것이 좋습니다:

sed -n -e "$X,$Y p" -e "$Y q" /path/to/file

그러나 sed 솔루션은 상당히 느립니다(적어도 GNU 유틸리티 및 Busybox 유틸리티의 경우 파이프가 느리고 sed가 빠른 운영 체제에서 대부분의 파일을 추출하는 경우 sed가 더 경쟁력이 있을 수 있습니다). 다음은 Linux에서의 빠른 벤치마크입니다. 데이터는 에서 생성되었으며 seq 100000000 >/tmp/a환경은 Linux/amd64, /tmp환경은 tmpfs이고 머신은 유휴 상태이며 스와핑되지 않습니다.

real  user  sys    command
 0.47  0.32  0.12  </tmp/a tail -n +50000001 | head -n 10 #GNU
 0.86  0.64  0.21  </tmp/a tail -n +50000001 | head -n 10 #BusyBox
 3.57  3.41  0.14  sed -n -e '50000000,50000010 p' -e '50000010q' /tmp/a #GNU
11.91 11.68  0.14  sed -n -e '50000000,50000010 p' -e '50000010q' /tmp/a #BusyBox
 1.04  0.60  0.46  </tmp/a tail -n +50000001 | head -n 40000001 >/dev/null #GNU
 7.12  6.58  0.55  </tmp/a tail -n +50000001 | head -n 40000001 >/dev/null #BusyBox
 9.95  9.54  0.28  sed -n -e '50000000,90000000 p' -e '90000000q' /tmp/a >/dev/null #GNU
23.76 23.13  0.31  sed -n -e '50000000,90000000 p' -e '90000000q' /tmp/a >/dev/null #BusyBox

사용하려는 바이트 범위를 알고 있는 경우 처음으로 직접 이동하여 더 빠르게 추출할 수 있습니다. 하지만 줄의 경우 처음부터 읽고 개행 횟수를 세어야 합니다. 청크 크기 b를 사용하여 0부터 시작하여 x(포함)에서 y(제외)까지 청크를 추출하려면 다음을 수행합니다.

dd bs="$b" seek="$x" count="$((y-x))" </path/to/file

답변3

head | tail방법은 이를 달성하는 가장 좋고 가장 "관용적인" 방법 중 하나입니다.

X=57890000
Y=57890010
< infile.txt head -n "$Y" | tail -n +"$X"

Giles가 의견에서 지적했듯이 더 빠른 방법은

< infile.txt tail -n +"$X" | head -n "$((Y - X))"

빠른 이유는 첫째X-1방법과 달리 라인은 파이프를 통과할 필요가 없습니다 head | tail.

귀하의 질문 문구는 약간 오해의 소지가 있으며 이 접근 방식에 대한 근거 없는 불안감을 설명할 수 있습니다.

  • A, B, 를 계산해야 한다고 말씀하셨는데 C, D보시다시피 파일의 줄 수는 필요하지 않으며 최대 1번의 계산이 필요하며 어쨌든 쉘이 이를 수행할 수 있습니다.

  • 파이프가 너무 많은 줄을 읽을까 봐 걱정됩니다. 사실 이는 사실이 아닙니다. tail | head파일 I/O 측면에서 얻을 수 있는 만큼 효율적입니다. 먼저 필요한 최소한의 노력을 고려하십시오.엑스파일의 3번째 줄, 유일한 일반적인 방법은 각 바이트를 읽고 계산할 때 중지하는 것입니다.엑스파일의 오프셋을 예측할 수 없기 때문에 개행 문자엑스'열. *X* 행에 도달하면 인쇄하기 전에 모든 행을 읽어야 합니다.'번째 줄. 그러므로 책을 적게 읽는 것을 피할 방법은 없습니다.철사. 이제 head -n $Y더 이상 읽지 마세요.행(가장 가까운 버퍼 단위로 반올림되지만 버퍼는 올바르게 사용하면 성능을 향상시킬 수 있으므로 오버헤드에 대해 걱정할 필요가 없습니다). 또한 tail그 이상은 읽혀지지 않으므로 가능한 한 적은 수의 줄을 읽는 head것으로 표시했습니다 head | tail(다시 말하지만 무시할 만한 무시할 만한 버퍼링도 포함). 파이프가 없는 단일 도구 접근 방식의 유일한 효율성 이점은 프로세스 수가 적다는 것입니다(따라서 오버헤드도 적습니다).

답변4

선택할 범위를 알고 있으면 첫 번째 행부터 lStart마지막 ​​행까지 다음을 lEnd계산할 수 있습니다.

lCount="$((lEnd-lStart+1))"

총 줄 수를 알고 있으면 lAll파일 끝까지의 거리도 계산할 수 있습니다.

toEnd="$((lAll-lStart+1))"

그러면 우리는 다음을 알게 될 것입니다:

"how far from the start"            ($lStart) and
"how far from the end of the file"  ($toEnd).

가장 작은 것을 선택하십시오: tailnumber다음과 같이:

tailnumber="$toEnd"; (( toEnd > lStart )) && tailnumber="+$linestart"

항상 가장 빠른 실행 명령을 사용할 수 있습니다.

tail -n"${tailnumber}" ${thefile} | head -n${lCount}

$linestart선택할 때 추가 플러스 기호("+")를 참고하십시오.

유일한 주의 사항은 총 행 수가 필요하므로 찾는 데 약간의 추가 시간이 걸릴 수 있다는 것입니다.
평소대로:

linesall="$(wc -l < "$thefile" )"

측정된 시간 중 일부는 다음과 같습니다.

lStart |500| lEnd |500| lCount |11|
real   user   sys    frac
0.002  0.000  0.000  0.00  | command == tail -n"+500" test.in | head -n1
0.002  0.000  0.000  0.00  | command == tail -n+500 test.in | head -n1
3.230  2.520  0.700  99.68 | command == tail -n99999501 test.in | head -n1
0.001  0.000  0.000  0.00  | command == head -n500 test.in | tail -n1
0.001  0.000  0.000  0.00  | command == sed -n -e "500,500p;500q" test.in
0.002  0.000  0.000  0.00  | command == awk 'NR<'500'{next}1;NR=='500'{exit}' test.in


lStart |50000000| lEnd |50000010| lCount |11|
real   user   sys    frac
0.977  0.644  0.328  99.50 | command == tail -n"+50000000" test.in | head -n11
1.069  0.756  0.308  99.58 | command == tail -n+50000000 test.in | head -n11
1.823  1.512  0.308  99.85 | command == tail -n50000001 test.in | head -n11
1.950  2.396  1.284  188.77| command == head -n50000010 test.in | tail -n11
5.477  5.116  0.348  99.76 | command == sed -n -e "50000000,50000010p;50000010q" test.in
10.124  9.669  0.448  99.92| command == awk 'NR<'50000000'{next}1;NR=='50000010'{exit}' test.in


lStart |99999000| lEnd |99999010| lCount |11|
real   user   sys    frac
0.001  0.000  0.000  0.00  | command == tail -n"1001" test.in | head -n11
1.960  1.292  0.660  99.61 | command == tail -n+99999000 test.in | head -n11
0.001  0.000  0.000  0.00  | command == tail -n1001 test.in | head -n11
4.043  4.704  2.704  183.25| command == head -n99999010 test.in | tail -n11
10.346  9.641  0.692  99.88| command == sed -n -e "99999000,99999010p;99999010q" test.in
21.653  20.873  0.744  99.83 | command == awk 'NR<'99999000'{next}1;NR=='99999010'{exit}' test.in

선택한 경로가 시작점이나 끝점에 가까울 경우 시간이 크게 바뀔 수 있다는 점에 유의하세요. 파일의 한쪽에서는 잘 실행되는 것처럼 보이는 명령이 파일의 다른 쪽에서는 매우 느릴 수도 있습니다.

관련 정보