문자열 교체를 위한 비라인 지향 도구?

문자열 교체를 위한 비라인 지향 도구?

나는 최근에 물었다.질문다른 특정 문자 뒤에 개행 문자가 나타나는 경우 이를 제거하는 방법에 대해 설명합니다.

Unix 텍스트 처리 도구는 매우 강력하지만 거의 모든 도구가 텍스트 줄을 처리하므로 입력이 사용 가능한 메모리에 맞는 경우 대부분 괜찮습니다.

하지만 개행 문자가 전혀 포함되지 않은 대용량 파일에서 텍스트 시퀀스를 바꾸려면 어떻게 해야 합니까?

예를 들어 입력을 한 줄씩 읽지 않고 <foobar>다음으로 바꾸시겠습니까? \n<foobar>(한 줄만 있고 길이가 2.5G이기 때문입니다.)

답변1

이런 유형의 문제에 직면했을 때 가장 먼저 떠오르는 것은 레코드 구분 기호를 변경하는 것입니다. 대부분의 도구에서 이는 \n기본적으로 설정되어 있지만 변경할 수 있습니다. 예를 들어:

  1. perl -0x3E -pe 's/<foobar>/\n$&/' file
    

    설명

    • -0: 입력 레코드 구분 기호를 주어진 문자로 설정합니다.16진수 값. 이 경우 >16진수 값이 로 설정됩니다 3E. 일반적인 형식은 -0xHEX_VALUE. 이것은 라인을 관리 가능한 덩어리로 나누는 트릭일 뿐입니다.
    • -pe: 에서 제공하는 스크립트를 적용한 후 각 입력 줄을 인쇄합니다 -e.
    • s/<foobar>/\n$&/: 간단한 대체. $&이 경우 일치하는 항목은 입니다 <foobar>.
  2. awk '{gsub(/foobar>/,"\n<foobar>");printf "%s",$0};' RS="<" file
    

    설명

    • RS="<": 입력 레코드 구분 기호를 로 설정합니다 >.
    • gsub(/foobar>/,"\n<foobar>"): 의 모든 경우를 foobar>로 대체합니다 \n<foobar>. RS로 설정되었기 때문에 <모든 것이 <입력 파일에서 제거되므로(이것이 작동 방식임 ) 일치 ( 없이 )하고 로 바꿔야 awk합니다 .foobar><\n<foobar>
    • printf "%s",$0: 대체 후 현재 "줄"을 인쇄합니다. 는 의 $0현재 레코드이므로 .awk<

다음 명령으로 생성된 2.3GB의 한 줄 파일에서 이를 테스트했습니다.

for i in {1..900000}; do printf "blah blah <foobar>blah blah"; done > file
for i in {1..100}; do cat file >> file1; done
mv file1 file

awkperl사용된 메모리 양은 모두 무시할 수 있습니다.

답변2

그사르 (일반 검색 및 바꾸기)바로 이 목적에 매우 유용한 도구입니다.

이 질문에 대한 대부분의 답변은 레코드 기반 도구와 다양한 트릭을 사용하여 문제에 적응하도록 합니다. 예를 들어 기본 레코드 구분 문자를 각 레코드를 처리하기에 너무 크게 만들지 않도록 입력에서 자주 발생하는 것으로 가정되는 문자로 전환하는 등입니다.

대부분의 경우 이는 매우 훌륭하고 심지어 읽기에도 좋습니다. 나는 awk, tr, sedBourne Shell과 같이 어디에서나 사용할 수 있는 도구를 사용하여 쉽고 효율적으로 해결할 수 있는 문제를 좋아합니다 .

바이너리 검색을 수행하고 임의의 내용이 포함된 임의의 대용량 파일을 바꾸는 것은 이러한 표준 유닉스 도구에 적합하지 않습니다.

여러분 중 일부는 이것이 부정 행위라고 생각할 수도 있지만 작업에 적합한 도구를 사용하는 것이 어떻게 잘못될 수 있는지 모르겠습니다. 이 경우 gsar라이센스가 부여 된 C 프로그램입니다.GPL v2, 그래서 이 매우 유용한 도구에 대한 패키지가 두 국가 모두에 없다는 사실이 상당히 놀랍습니다.젠투,빨간 모자, 도 아니다우분투.

gsar의 바이너리 변형을 사용합니다.Boyer-Moore 문자열 검색 알고리즘.

사용법은 간단합니다.

gsar -F '-s<foobar>' '-r:x0A<foobar>'

여기서 는 -F"필터" 모드, 즉 읽기 stdin쓰기를 의미합니다 stdout. 파일을 조작하는 방법도 있습니다. -s검색 문자열과 -r대체를 지정합니다. 콜론 표기법을 사용하여 임의의 바이트 값을 지정할 수 있습니다.

대소문자 구분 모드가 지원되지만( -i) 정규식은 지원되지 않습니다. 알고리즘이 검색 문자열의 길이를 사용하여 검색을 최적화하기 때문입니다.

이 도구는 grep. gsar -b일치하는 검색 문자열의 바이트 오프셋을 출력하고 파일 이름과 일치하는 항목 수를 인쇄합니다. 와 gsar -l결합하는 것과 같습니다 .grep -lwc

이 도구는 다음에 의해 작성되었습니다.토르모드 차베리(초기) 및한스 피터 베른(개량).

답변3

대상 문자열과 대체 문자열의 길이가 같은 좁은 경우에는메모리 매핑구출하러 올 수 있어요. 이는 교체를 내부에서 수행해야 하는 경우 특히 유용합니다. 기본적으로 파일을 프로세스의 가상 메모리에 매핑하는 것이며 64비트 주소 지정을 위한 주소 공간은 엄청납니다.파일이 반드시 물리적 메모리에 한꺼번에 매핑되는 것은 아닙니다., 따라서 시스템에서 사용할 수 있는 실제 메모리 크기의 몇 배에 달하는 파일을 처리할 수 있습니다.

foobar다음은 다음으로 대체되는 Python 예제입니다.XXXXXX

#! /usr/bin/python
import mmap
import contextlib   
with open('test.file', 'r+') as f:
 with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)) as m:
   pos = 0
   pos = m.find('foobar', pos)
   while pos > 0:
    m[pos: pos+len('XXXXXX')] = 'XXXXXX'
    pos = m.find('foobar', pos)

답변4

Awk는 연속적인 레코드에서 작동합니다. 레코드 구분 기호로 모든 문자를 사용할 수 있습니다(많은 구현에서 널 바이트 제외). 일부 구현에서는 레코드 구분 기호로 임의의 정규식(빈 문자열과 일치하지 않음)을 지원하지만 이는 레코드 구분 기호가 저장되기 전에 각 레코드의 끝에서 잘리기 때문에 다루기 어려울 수 있습니다 $0(GNU awk는 변수를 RT레코드 구분 기호로 설정합니다) 현재 레코드의 끝에서 제거되었습니다). 기본적으로 줄 바꿈이고 입력 레코드 구분 기호와 독립적으로 설정되는 print출력 레코드 구분 기호를 사용하여 출력을 종료 합니다 .ORSRS

awk -v RS=, 'NR==1 {printf "input up to the first comma: %s\n", $0}'

를 사용하여 개행 문자를 해당 문자로 바꾸면 다른 도구( sort, , …) 에 대한 레코드 구분 기호로 다른 문자를 효과적으로 선택할 수 있습니다 .sedtr

tr '\n,' ',\n' |
sed 's/foo/bar/' |
sort |
tr '\n,' ',\n'

많은 GNU 텍스트 유틸리티는 구분 기호로 개행 문자 대신 널 바이트 사용을 지원합니다.

관련 정보