대용량 텍스트 파일 찾기 바꾸기 하기 (메모리 문제 없이)

,

bash 커맨드라인을 이용하는 방법(윈도우에선 git bash 사용)

bash를 사용한다면 sed 명령어로 간단하게 해결할 수 있습니다.

sed -i 's|search|raplace|g' my-dump.sql

위 명령어에서 search에 찾는 문자열을, replace에 바꿀 문자열을 넣으면 됩니다. 이스케이프를 해야 한다면 \로 하면 됩니다.

윈도우를 사용하고 있다고 해도 git bash를 이용하면 bash의 sed 명령어를 사용할 수 있습니다.

맥에 기본 탑재돼 있는 sed-i 옵션 사용법이 달라 위 명령어대로 하면 에러가 납니다. 아래와 같이 쓰면 됩니다.

sed -i'' 's|search|raplace|g' my-dump.sql

PHP를 이용하는 방법

MySQL4에서 5로 넘어오면서 테이블을 만들 때 TYPE=MyISAM 이라고 쓰던 걸 ENGINE=MyISAM 으로 쓰게 됐다고 한다. (정확한 버전대는 모르겠다.)

여튼간에 4에서 돌아가는 DB가 백업파일을 압축해서 받으니까 한 160메가쯤 됐다. 압축을 푸니 600메가쯤 된다. TYPE=MyISAM 부분에서 에러를 뿜으며 임포트가 안 된다. 골치가 아팠다.

에디트플러스에서 열어 봤다. 메모리 부족으로 프로그램이 꺼진다. 결국 스크립트를 돌리는 방법밖에 없는 것 같다는 생각을 했다.

그래서 처음엔 PHP의 file_get_contents 함수를 사용했다. 그런데 파일이 대용량이니까 php.ini에 들어가서 메모리를 1기가나 할당해 줘도 뻗어버린다. (memory_limit = 1024M 로 수정했다는 말이다.) 에러 메시지는 아래와 같았다.

PHP Fatal error:  Allowed memory size of 838860800 bytes exhausted (tried to allocate 638960364 bytes)

뭔가 순차적으로 읽어서 조작하는 방법은 없나? 하고 찾아 봤다. file 함수 말고 fopen 함수를 사용하면 될 것 같아서 관련 코드를 찾았다. 찾은 코드를 살짝 변경했다. 일단 str_replace를 하게 했고, 그 뒤 echo 로 바뀐 문자열을 출력해 주게 했다. 그리고 이걸 파일로 만들도록 쉘의 기본 기능인 > 명령어를 사용했다. 그렇게 하니 php.ini에 설정된 메모리 한계가 128M이었는데도 뻗지 않고 잘 돌아갔다.

쉘의 표준출력을 파일로 저장하는 >(리디렉션) 기호

명령어 > 파일명

윈도우, 리눅스, 맥의 쉘에는 모두 > 기호를 이용해 표준 출력을 화면으로 표시하지 않고 파일로 작성하는 기능이 있다.

ls > ls.txt

이렇게 써 주면 ls.txt 파일이 생성된다. 아무 것도 출력은 안 되고 말이다. ls.txt 파일을 열어 보면 파일 목록이 들어가 있는 것을 볼 수 있다. (윈도우 터미널엔 ls가 없으니 dir로 테스트해 보라.)

아래에서 php를 실행해 결과를 파일로 저장하는 방법을 설명한다.

메모리 걱정 없는 PHP 문자열 변환 코드

아래 코드로 PHP 파일을 하나 만든다.

그리고 그걸 쉘에서 실행하는데, > 기호를 이용해 실행 결과를 파일로 저장한다.

실행은 서버에서 해도 되고, 로컬 개발 컴퓨터에서 해도 된다.

<?php
/*
 * File name: str_replace.php
 * Author: mytory
 * URL: https://mytory.net/archives/3168
 */

if ($argc < 4) {
    die("Usage: php script.php <filename> <search_string> <replace_string>\n");
}

$file_name = realpath($argv[1]);

if (!file_exists($file_name)) {
    die("File not found: " . $file_name . "\n");
}

$search_string = $argv[2];
$replace_string = $argv[3];

$fp = fopen($file_name, "rb") or die("fopen failed");

while (!feof($fp)) {
    echo str_replace($search_string, $replace_string, fgets($fp));
}

fclose($fp) or die("fclose failed");

feof($handle)는 파일의 끝에 도달했는지 묻는 함수다. 끝이면 TRUE를, 그렇지 않으면 FALSE를 반환한다. 에러가 발생했다면? TRUE를 반환한다.

fgets($handle)은 파일을 한 줄씩 읽어들이는 함수다. fgets($handle, 1024) 형식으로 몇 바이트씩 읽어들일지도 정해줄 수 있다. 위 코드에선, 한 줄씩 읽어야 변환을 제대로 할 수 있기 때문에 한 줄씩 읽도록 했다.

실행하는 방법

쉘에서는 아래처럼 실행하면 된다.

php str_replace.php mydump.sql "TYPE=MyISAM" "ENGINE=MyISAM" > mydump.replaced.sql

글로벌 명령어로 만들기

맥이나 리눅스에서는 글로벌 명령어로 만들 수도 있다. 인터프리터를 맨 위에 명시해 주면 된다.

#!/usr/bin/env php
<?php
// ...

이렇게 해서 파일을 저장한 뒤 bash에서 아래와 같이 처리해 준다.

chmod +x str_replace.php  #실행 권한을 준다
sudo mv str_replace.php /usr/local/bin/str_replace  # 어디서나 실행될 수 있는 경로(PATH)로 파일 이동

이렇게 처리하고 나면 아래와 같이 어디서나 사용할 수 있다.

str_replace mydump.sql "TYPE=MyISAM" "ENGINE=MyISAM" > mydump.replaced.sql

카테고리 글 목록 👉

,

대표글

“대용량 텍스트 파일 찾기 바꾸기 하기 (메모리 문제 없이)” 에 하나의 답글

  1. dir > dir.txt 도 된다니 참 신기하네요 ㅎㅎㅎ

댓글 남기기