POSIX.2의 마지막 토큰에서 EOF로 텍스트 가져오기

POSIX.2의 마지막 토큰에서 EOF로 텍스트 가져오기

다음과 같이 표시된 줄이 있는 텍스트가 있습니다.

aaa
---
bbb
---
ccc

마지막 토큰(독점)의 텍스트를 EOF로 가져와야 합니다. 이 경우에는

ccc

POSIX.2에 우아한 방법이 있습니까? 이제 두 번의 실행을 사용합니다. 첫 번째 실행 nlgrep해당 줄 번호가 있는 마지막 실행입니다. 그런 다음 줄 번호를 추출하여 sed문제 블록을 추출하는 데 사용했습니다 .

텍스트 세그먼트는 매우 클 수 있으므로 텍스트를 버퍼에 추가하는 것과 같은 일부 텍스트 추가 방법을 사용하는 것이 걱정됩니다. 마커가 발견되면 버퍼를 비워 EOF에서 마지막 청크 버퍼를 갖게 됩니다.

답변1

세그먼트가 매우 크지 않는 한(예: 대용량 파일 시스템을 제어하는 ​​작은 임베디드 시스템이기 때문에 실제로 그렇게 많은 RAM을 절약할 수 없는 경우) 단일 패스가 실제로 더 나은 접근 방식입니다. 속도가 더 빨라질 뿐만 아니라 가장 중요한 것은 소스를 스트림으로 만들 수 있고, 읽은 후 저장하지 않은 모든 데이터가 손실된다는 점입니다. sed도 할 수 있지만 이것은 실제로 awk의 작업입니다.

sed -n -e 's/^---$//' -e 't a' \
       -e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
       -e ':a' -e 'h'              # you are not expected to understand this
awk '{if (/^---$/) {chunk=""}      # separator ==> start new chunk
      else {chunk=chunk $0 RS}}    # append line to chunk
     END {printf "%s", chunk}'     # print last chunk (without adding a newline)

2단계 방법을 사용해야 하는 경우 마지막 구분 기호의 줄 오프셋을 결정하고 이를 인쇄합니다. 또는 바이트 오프셋을 결정하고 그것으로부터 인쇄합니다.

</input/file tail -n +$((1 + $(</input/file         # print the last N lines, where N=…
                               grep -n -e '---' |   # list separator line numbers
                               tail -n 1 |          # take the last one
                               cut -d ':' -f 1) ))  # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
    {pos+=length($0 RS)}        # pos contains the current byte offset in the file
    /^---$/ {last=pos}          # last contains the byte offset after the last separator
    END {print last+1}          # print characters from last (+1 because tail counts from 1)
')

부록: POSIX 이상의 것이 있는 경우 레코드 구분 기호가 RS정규 표현식이 되도록 허용하는 awk의 일반 확장에 의존하는 간단한 일회용 버전이 있습니다(POSIX는 단일 문자만 허용). 완전히 정확하지는 않습니다. 파일이 레코드 구분 기호로 끝나는 경우 빈 레코드 대신 마지막 레코드 구분 기호 앞의 블록을 인쇄합니다. 사용된 두 번째 버전은 RT결함을 방지하지만 RTGNU awk에만 해당됩니다.

awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'

답변2

lnum=$(($(sed -n '/^---$/=' file | sed '$!d') +1)); sed -n "${lnum},$ p" file 

첫 번째는 sed"---" 라인의 라인 번호를 출력합니다.
두 번째는 sed첫 번째 sed의 출력에서 ​​마지막 번호를 추출하고...
해당 번호에 1을 추가하여 "ccc" 블록의 시작을 가져옵니다.
세 번째 'sed'는 "ccc" 블록의 시작 부분부터 EOF로 출력됩니다 .

고쳐 쓰다 (Gilles 방법에 대한 수정된 정보)

알았어 난 방법을 알고 싶어글렌 잭맨의 tac그래서 저는 세 가지 답변(작성 당시)을 시간 테스트했습니다... 각 테스트 파일에는 100만 줄(각 줄 번호)이 포함되어 있었습니다.
모든 답변은 예상대로입니다 ...

여기에 시간이 있습니다 ...


자일스 sed(일방 통행)

# real    0m0.470s
# user    0m0.448s
# sys     0m0.020s

자일스 awk(일방 통행)

# very slow, but my data had a very large data block which awk needed to cache.

자일스"2단계"(첫 번째 방법)

# real    0m0.048s
# user    0m0.052s
# sys     0m0.008s

자일스"더블 패스"(두 번째 방법)...매우 빠르다

# real    0m0.204s
# user    0m0.196s
# sys     0m0.008s

자일스"2회 패스"(세 번째 방법)

# real    0m0.774s
# user    0m0.688s
# sys     0m0.012s

자일스'gawk'(RT 방식)...매우 빠르다, POSIX는 아닙니다.

# real    0m0.221s
# user    0m0.200s
# sys     0m0.020s

글렌 잭맨...매우 빠르다, POSIX는 아닙니다.

# real    0m0.022s
# user    0m0.000s
# sys     0m0.036s

프레드 베어

# real    0m0.464s
# user    0m0.432s
# sys     0m0.052s

마이키 메서

# real    0m0.856s
# user    0m0.832s
# sys     0m0.028s

답변3

2단계 전략이 옳은 것처럼 보였습니다. 대신 sed를 사용하겠습니다 awk(1). 두 채널은 다음과 같습니다.

$ LINE=`awk '/^---$/{n=NR}END{print n}' file`

줄 번호를 가져옵니다. 그런 다음 해당 줄 번호에서 시작하는 모든 텍스트를 에코합니다.

$ awk "NR>$LINE" file

너무 많은 버퍼링이 필요하지 않습니다.

답변4

그냥 사용할 수 있습니다ed

ed -s infile <<\IN
.t.
1,?===?d
$d
,p
q
IN

작동 방식: t현재 ( .) 줄을 복사합니다. 시작 시 항상 마지막 줄 ed(구분 기호가 마지막 줄에 있는 경우), 1,?===?d이전 일치 항목까지의 모든 줄을 삭제하고( ed여전히 마지막 줄) $d삭제합니다( 중복) 마지막 줄은 ,p텍스트 버퍼를 인쇄하고( w파일을 제자리에서 편집하기 위해 대체됨) 마지막으로 q종료됩니다 ed.


입력에 구분 기호가 하나 이상 있다는 것을 알고 있으면(또한 인쇄되는지는 상관하지 않음)

sed 'H;/===/h;$!d;x' infile

더 짧아질 것입니다.
작동 방식: 이전 버퍼에 모든 줄을 추가하고, H일치하는 항목이 발견되면 이전 버퍼를 덮어쓰며, 버퍼가 변경되면(그리고 자동으로 인쇄됨) lat를 제외한 모든 줄을 삭제합니다.hd$x

관련 정보