ex-command를 사용하여 두 줄이 동일한 지 확인하십시오.


9

나는 이 질문 을보고 순수 POSIX 를 사용sed 하는 내 대답을 어떻게 구현할 수 있는지 궁금해했다 .ex

요령은 sed홀드 공간과 패턴 공간을 비교하여 패턴 공간과 정확히 같은지 여부를 확인할 수는 있지만 (와 G;/^\(.*\)\n\1$/{do something}) 같은 테스트를 수행 할 방법이 없다는 것입니다 ex.

나는 빔에 내가 할 수있는 것을 알고 Y첫 번째 줄을 ANK 다음 입력 :2,$g/<C-r>0/d거의 내가 지정-그러나있어 일을 첫 번째 줄은 아무 것도 포함하지만 라인이로 덤프되는 때문에 매우 간단 영숫자 텍스트이, 참으로 위태로운이되면 정규식 비교 용 문자열이 아닙니다. (그리고 첫 번째 줄에 슬래시가 있으면 나머지 줄은 명령으로 해석됩니다!)

따라서 myfile첫 번째 줄과 동일한 모든 줄을 삭제하고 싶지만 첫 번째 줄은 삭제하지 않으려면 어떻게해야 ex합니까? 그 문제에 대해 어떻게 사용할 수 vi있습니까?

다른 줄과 정확히 일치하는 줄을 삭제하는 POSIX 방법이 있습니까?

아마도이 가상 구문과 같은 것입니다 :

:2,$g/**lines equal to "0**/d

3
명령을 작성할 수는 있지만 약간의 vimscript가 필요할 것이며 아마도 POSIX 방식이 아닐 것입니다.:execute '2,$g/\V' . escape(getline(1), '\') . '/d'
saginaw

1
@saginaw, 감사합니다. 지금까지 나에게 일어난 유일한 POSIX 접근 방식 은 within 에서 필터sed 로 사용 하고 전체 버퍼에 대한 전체 대답을 실행 하는 입니다. 물론 작동 할 입니다 (실제로는 이식성이 없습니다 ). exsedsed -i
와일드 카드

당신 말이 맞아요. 나는 당신의 초기 접근 방식이 <C-r>0아주 좋습니다. 특수 문자를 보호해야하기 때문에 Ex 명령으로 만 더 잘 수행 할 수 있는지 확실하지 않습니다. POSIX 호환 제약 조건이 없으면 매우 nomagic 스위치를 사용하고 두 번째 인수가 이스케이프 / 보호하려는 모든 문자가 포함 된 문자열 인 함수로 \V백 슬래시를 보호 한다고 생각합니다 (와도 특별한 의미를 유지하기 때문에 \V) escape().
saginaw

그러나 이전 명령에서 슬래시도 보호하는 것을 잊었습니다. 전역 명령에도 특별한 의미가 있기 때문에 패턴 구분 기호입니다. 따라서 올바른 명령은 다음과 같습니다. :execute '2,$g/\V' . escape(getline(1), '\/') . '/d'또는 세미콜론과 같이 패턴 구분 기호에 다른 문자를 사용할 수 있습니다. 이 경우 패턴에서 슬래시를 보호 할 필요가 없습니다. : 그것은 같은 줄 것이다:execute '2,$g;\V' . escape(getline(1), '\') . ';d'
새 기노

1
나는 당신의 두 번째 접근법 sed도 매우 좋습니다. Vim을 사용하면 특정 특수 작업을 다른 프로그램에 위임하는 경우가 많으며 이에 sed대한 좋은 예일 것입니다. 그건 그렇고, 당신은 sed전체 버퍼 에서 실행할 필요가 없습니다 . 버퍼의 일부에서만 실행하려는 경우 범위를 지정할 수 있습니다. 예를 들어 50과 100 사이의 행만 필터링하려면 다음과 같이 입력하십시오 :50,100!<your sed command>..
saginaw

답변:


3

정력

Vim에서는 개행 문자를 포함한 모든 문자를로 일치시킬 수 있습니다 \_.. 이것을 사용하여 전체 라인, 임의의 양, 동일한 라인과 일치하는 패턴을 구성 할 수 있습니다.

/\(^.*$\)\_.*\n\1$/

이제 파일에서 첫 번째를 포함하지 않고 첫 번째와 일치하는 모든 행을 삭제하려고합니다. 첫 번째 줄과 일치하는 마지막 줄을 삭제하는 대체 방법은 다음과 같습니다.

:1 s/\(^.*$\)\_.*\zs\n\1$//

:global대체를 반복하여 모든 행을 삭제하기에 충분한 횟수를 반복하도록 할 수 있습니다 .

:g/^/ 1s/\(^.*$\)\_.*\zs\n\1$//

POSIX 예

@saginaw는 귀하의 질문에 대한 의견으로 Vim 에서이 작업을 수행하는 더 좋은 방법을 보여 주지만 위의 기술을 POSIX ex에 적용 할 수 있습니다.

POSIX 호환 방식으로이 작업을 수행하려면 여러 줄 일치를 허용하지 않아도 역 참조를 계속 사용할 수 있습니다. 추가 작업이 필요합니다.

:g/^/ t- | s/^/@@@/ | 1t- | s/^/"/ | j! | s/^"\(.*\)@@@\1$/d/ | d x | @x

고장은 다음과 같습니다.

:g/^/                   for each line

t- |                    copy it above

s/^/@@@/ |              prefix it with something unique (@@@)
                        (do a search in the buffer first to make
                        sure it really is unique)

1t- |                   copy the first line above this one

s/^/"/ |                prefix with "

j! |                    join those two lines (no spaces)

s/^"\(.*\)@@@\1$/d/ |   if the part after the " and before the @@@
                        matches the part after the @@@, replace the line
                        with d

d x |                   delete the line into register x

@x                      execute it

따라서 현재 행이 행 1의 복제본이면 레지스터 x에가 포함 d됩니다. 실행하면 현재 줄이 삭제됩니다. 중복되지 않은 경우 주석을 시작하기 "때문에 실행될 때 no-op가 아닌 넌센스 접두사가 포함 "됩니다. 이것이 가장 달성하기 어려운 방법인지는 모르겠습니다. 처음 떠오른 것입니다!

복사 프로세스가 1 행을 일시적으로 변경하기 때문에 첫 번째 행을 삭제할 수 없습니다. 이 경우하지 않았다면 당신은 접두사 수 :g로모그래퍼 2,$대신 범위.

Vim 및 ex-vi 버전 4.0에서 테스트되었습니다.

편집하다

그리고 더 간단한 방법은 특수 문자를 이스케이프하여 검색 패턴 ( 'nomagic'설정된)을 작성하고 :global명령 을 작성한 다음 실행합니다.

:set nomagic
:1t1 | .g/^/ s#\[$^\/]#\\\&#g | s#\.\*#2,$g/^\&$/d# | d x
:@x
:set magic

중첩 된을 가질 수 있기 때문에 하나의 라이너 로이 작업을 수행 할 수 :global는 없습니다.


2

이 작업을 수행하는 유일한 POSIX 방법은와 같은 외부 필터를 사용하는 것 같습니다 sed.

예를 들어 파일의 다섯 번째 줄과 정확히 동일한 경우에만 파일의 17 번째 줄을 삭제하고 변경하지 않은 상태로 두려면 다음을 수행하십시오.

:1,17!sed '5h;17{G;/^\(.*\)\n\1$/d;s/\n.*$//;}'

( sed여기에서 전체 버퍼에서 실행하거나 5-17 행에서만 실행할 수 있지만 첫 번째 경우에는 불필요한 필터링을 수행합니다. 별다른 문제는 없으며 후자의 경우 sed5와 17 대신 명령 에 숫자 1과 13. 혼란.)

sed하나의 정방향 패스 만 수행 하므로 , 17 행과 동일한 경우에만 5 행을 역으로 삭제하고 삭제할 수있는 쉬운 방법이 없습니다. 나는 호기심의 지점으로 잠시 동안 노력했다 ... 그것은 까다 롭다 .


혁신 -다음과 같이 할 수 있습니다.

:17t 5
:5,5+!sed '1N;/^\(.*\)\n\1$/d;s/\n.*$//'

이것은 실제로 더 일반적인 방법입니다. 마찬가지로 첫 번째 명령과 동일한 결과를 제공하고 (5 번째 줄과 동일한 경우에만 17 번째 줄을 삭제) 다음과 같이 사용할 수 있습니다.

:5t 17
:17,17+!sed '1N;/^\(.*\)\n\1$/d;s/\n.*$//'

37 행과 동일한 파일의 모든 행을 삭제 하고 37 행은 그대로두고 더 광범위하게 사용 하려면 다음을 수행하십시오.

:37,$!sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//'
:37t 0
:1,37!sed '1{h;d;};G;/^\(.*\)\n\1$/d;s/\n.*$//'

결론은 여기에 두 개의 라인이 동일한 경우, 가장 좋은 도구 검사를 들면, 이다 sed ,하지 ex. 그러나 DevSolar 코멘트에 언급 ,이의 실패하지 viex- 그들은이되는 설계 유닉스 도구를 사용하여 작업에; 그것은 큰 힘입니다.


훨씬, 훨씬 어렵습니다 : 행이 존재하지 않는 경우에만, 파일의 끝에 행을 삽입 어딘가에 파일에.
와일드 카드

그것은 내 대답과 비슷한 접근법으로 가능해야합니다. 나는 그것이 하나의 라이너 일 것이라고 생각하지 않습니다!
Antony
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.