Bash 스크립팅 및 대용량 파일 (버그) : 리디렉션에서 내장 된 읽기로 입력하면 예기치 않은 결과가 발생합니다.

jfg956

큰 파일과 bash. 이것이 바로 컨텍스트입니다.

  • 75G와 400,000,000 개 이상의 라인 (로그 파일입니다.
  • 각 줄의 처음 10자는 YYYY-MM-DD 형식의 타임 스탬프입니다.
  • 나는 그 파일을 하루에 하나의 파일로 나누고 싶다.

작동하지 않는 다음 스크립트로 시도했습니다. 내 질문은이 스크립트가 작동하지 않고 대체 솔루션이 아니라는 것입니다 .

while read line; do
  new_file=${line:0:10}_file.log
  echo "$line" >> $new_file
done < file.log

디버깅 후 new_file변수 에서 문제를 발견했습니다 . 이 스크립트 :

while read line; do
  new_file=${line:0:10}_file.log
  echo $new_file
done < file.log | uniq -c

결과는 다음과 같습니다 ( x데이터를 기밀로 유지하기 위해 es를 넣고 다른 문자는 실제 문자입니다). 통지 dh와 짧은 문자열 :

...
  27402 2011-xx-x4
  27262 2011-xx-x5
  22514 2011-xx-x6
  17908 2011-xx-x7
...
3227382 2011-xx-x9
4474604 2011-xx-x0
1557680 2011-xx-x1
      1 2011-xx-x2
      3 2011-xx-x1
...
     12 2011-xx-x1
      1 2011-xx-dh
      1 2011-xx-x1
      1 208--
      1 2011-xx-x1
      1 2011-xx-dh
      1 2011-xx-x1    
...

내 파일 형식에는 문제가 없습니다 . 스크립트 cut -c 1-10 file.log | uniq -c는 유효한 타임 스탬프 만 제공합니다. 흥미롭게도 위 출력의 일부는 다음과 같이됩니다 cut ... | uniq -c.

3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1

uniq count 후에 4474604초기 스크립트가 실패했음을 알 수 있습니다.

내가 모르는 bash에서 한계에 도달 했습니까? bash에서 버그를 발견 했습니까 (이음새가 없을 것 같음) 또는 내가 잘못한 것이 있습니까?

업데이트 :

파일의 2G를 읽은 후에 문제가 발생합니다. 이음새 read와 리디렉션은 2G보다 큰 파일을 좋아하지 않습니다. 그러나 여전히 더 정확한 설명을 찾고 있습니다.

업데이트 2 :

확실히 버그처럼 보입니다. 다음과 같이 재현 할 수 있습니다.

yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c

그러나 이것은 해결 방법으로 잘 작동합니다 (유용한 사용을 찾은 솔기 cat).

cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c 

GNU와 Debian에 버그가보고되었습니다. 영향을받는 버전은 bashDebian Squeeze 6.0.2 및 6.0.4의 4.1.5입니다.

echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu

업데이트 3 :

내 버그 보고서에 신속하게 반응 한 Andreas Schwab 덕분에 이것이이 잘못된 행동에 대한 해결책 인 패치입니다. 영향을받은 파일은 lib/sh/zread.cGilles가 더 일찍 지적한대로입니다.

diff --git a/lib/sh/zread.c b/lib/sh/zread.c index 0fd1199..3731a41 100644
--- a/lib/sh/zread.c
+++ b/lib/sh/zread.c @@ -161,7 +161,7 @@ zsyncfd (fd)
      int fd; {   off_t off;
-  int r;
+  off_t r;

  off = lused - lind;   r = 0;

r변수는의 반환 값을 유지하는 데 사용됩니다 lseek. As lseek는 파일의 시작 부분에서 오프셋을 반환합니다. 2GB를 초과하면 int값이 음수이므로 테스트 if (r >= 0)가 성공해야하는 곳에서 실패합니다.

Gilles 'SO- 그만 사악함'

bash에서 일종의 버그를 발견했습니다. 알려진 수정 사항이있는 알려진 버그입니다.

프로그램은 파일의 오프셋을 유한 크기의 정수 유형의 변수로 나타냅니다. 예전에는 모든 사람이 int거의 모든 것을 사용 했고 int유형은 부호 비트를 포함하여 32 비트로 제한되어 -2147483648에서 2147483647까지의 값을 저장할 수있었습니다. 요즘에는를 포함하여 다양한 사물에 대해 다른 유형 이름 이 있습니다 off_t. 파일의 오프셋.

기본적 off_t으로은 32 비트 플랫폼에서 32 비트 유형 (최대 2GB 허용)이고 64 비트 플랫폼에서 64 비트 유형 (최대 8EB 허용)입니다. 그러나 LARGEFILE 옵션을 사용하여 프로그램을 컴파일하는 것이 일반적입니다.이 옵션은 유형 off_t을 64 비트 너비로 전환 하고 프로그램 호출이 lseek.

32 비트 플랫폼에서 bash를 실행하고 있으며 bash 바이너리가 대용량 파일 지원으로 컴파일되지 않은 것 같습니다. 이제 일반 파일에서 한 줄을 읽을 때 bash는 내부 버퍼를 사용하여 성능을 위해 일괄 적으로 문자를 읽습니다 (자세한 내용은에서 소스 참조 builtins/read.def). 행이 완료되면 bash는 lseek다른 프로그램이 해당 파일의 위치에 관심이있는 경우 파일 오프셋을 행 끝 위치로 되감기 위해 호출 합니다. 에 대한 호출 lseek은의 zsyncfc함수에서 발생합니다 lib/sh/zread.c.

나는 소스를 자세히 읽지 않았지만 절대 오프셋이 음수 일 때 전환 지점에서 원활하게 일어나지 않을 것이라고 생각합니다. 따라서 bash는 2GB 표시를 통과 한 후 버퍼를 다시 채울 때 잘못된 오프셋에서 읽습니다.

내 결론이 틀렸고 bash가 실제로 64 비트 플랫폼에서 실행 중이거나 대용량 파일 지원으로 컴파일 된 경우 확실히 버그입니다. 배포 또는 업스트림에 보고하십시오 .

어쨌든 셸은 이러한 대용량 파일을 처리하는 데 적합한 도구가 아닙니다. 느려질 것입니다. 가능하면 sed를 사용하고 그렇지 않으면 awk를 사용하십시오.

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

Related 관련 기사

뜨겁다태그

보관