Vim에서 전역 검색 및 바꾸기, 커서 위치에서 시작하여 둘러싸 기


112

/ 일반 모드 명령으로 검색 할 때 :

/\vSEARCHTERM

Vim은 커서 위치에서 검색을 시작하고 아래쪽으로 계속해서 위쪽으로 래핑합니다. 그러나 :substitute명령을 사용하여 검색하고 바꿀 때 :

:%s/\vBEFORE/AFTER/gc

대신 Vim은 파일 상단에서 시작합니다.

빔 검색 및 커서 위치에서 시작하여 대체 실시합니다 만들 수있는 방법이 그 끝에 도달 한 번 정상에 주변의 포장은?


2
\vpattern- '매우 매직'패턴 : 영숫자가 아닌 문자는 특수 정규식 기호로 해석됩니다 (이스케이프 필요 없음)
Carlos Araya

현재 위치에서 시작하고 파일을 사용하는 대신 파일을 감싸는 점은 무엇 %입니까? 어쨌든 전체 파일을 검토하는 것에 대한 합리적인 사용이 보이지 않습니다.
tymik

@tymik 목적은 커서에서 검색을 시작하고 각 결과를 단계별로 살펴 보는 것이 었습니다. 하지만 몇 년 동안 Vim을 사용하지 않았습니다.
basteln

답변:


50

1.  2 단계 대체를 사용하여 동작을 달성하는 것은 어렵지 않습니다.

:,$s/BEFORE/AFTER/gc|1,''-&&

먼저, 현재 행에서 시작하여 파일 끝까지 각 행에 대해 대체 명령이 실행됩니다.

,$s/BEFORE/AFTER/gc

:substitute 명령을 사용하여 동일한 검색 패턴, 대체 문자열 및 플래그를 사용하여 명령을 반복합니다 :& (참조 :help :&).

1,''-&&

그러나 후자는 파일의 첫 번째 행에서 이전 컨텍스트 표시가 설정된 행까지의 행 범위에서 1을 뺀 범위에서 대체를 수행합니다. 처음부터:substitute 명령은 실제 ​​대체를 시작하기 전에 커서 위치를 저장 주소  ''가 지정된 행은 대체 명령이 실행되기 전에 현재 행이었던 행입니다. ( '' 주소는 ' 의사 표시를 나타냅니다 . 자세한 내용 은 :help :range및 참조 :help '')

두 번째 명령 ( | 명령 구분 기호 뒤 — 참조 :help :bar)은 첫 번째 명령 에서 패턴 또는 플래그가 변경 될 때 변경이 필요하지 않습니다.

2.  타이핑을 저장하기 위해 명령 줄에서 위의 대체 명령의 골격을 가져 오기 위해 다음과 같이 일반 모드 매핑을 정의 할 수 있습니다.

:noremap <leader>cs :,$s///gc\|1,''-&&<c-b><right><right><right><right>

후행 <c-b><right><right><right><right> 부분은 커서를 명령 줄의 시작 부분 ( <c-b>)으로 이동 한 다음 오른쪽으로 4 자 ( <right> × 4)로 이동하여 처음 두 개의 슬래시 기호 사이에 배치하여 사용자가 검색 패턴을 입력 할 수 있도록하는 데 필요합니다. . 원하는 패턴과 교체가 준비되면을 눌러 결과 명령을 실행할 수 있습니다 Enter.

( 위의 매핑 //대신에 ///패턴을 입력하는 것을 선호하는 경우 오른쪽 화살표를 사용하여 커서를 이미 존재하는 분리 슬래시 시작 위로 이동하는 대신 분리 슬래시를 직접 입력 한 다음 대체 문자열을 입력하는 것을 고려할 수 있습니다. 교체 부품.)


1
이 솔루션의 유일한 문제는 last (l) 및 quit (q) 옵션이 두 번째 대체 명령 실행을 중지하지 않는다는 것입니다.
Steve Vermeulen

@eventualEntropy 다른 'q'를 누르라는 메시지에 대한 내 솔루션을 아래에서 참조하십시오.
q335r49

2
이것을 키 매핑에 넣는 경우 파이프를 이스케이프하는 것을 잊지 마십시오.
jazzabeanie

11
세상에, 그 모든 임의의 문자가 의미하는 바는 무엇입니까? 어떻게 배웠습니까?
rocky raccoon

2
@Tropilio : 그런 다음 다음과 같이 시도해 볼 수 있습니다 :noremap <leader>R :,$s///gc\|1,''-&&<c-b><right><right><right><right>.. 그것은두고 :,$s///gc|1,''-&&처음 두 개의 슬래시 기호 사이의 커서를 명령 줄에. 원하는 패턴과 대체를 입력 하면 Enter 키 를 눌러 결과 명령을 실행할 수 있습니다 .
ib.

174

이미 전체 파일 %1,$의미하는 약칭 인 범위를 사용하고 있습니다. 현재 줄에서 끝으로 이동하려면을 사용 .,$합니다. 마침표는 현재 줄을 $의미하고 마지막 줄을 의미합니다. 따라서 명령은 다음과 같습니다.

:.,$s/\vBEFORE/AFTER/gc

그러나 .또는 현재 행을 가정 할 수 있으므로 제거 할 수 있습니다.

:,$s/\vBEFORE/AFTER/gc

더 많은 도움이 필요하면

:h range

감사합니다. 이미 알고있었습니다. 문서의 끝에 도달하면 검색 및 바꾸기가 래핑되기를 원합니다. :., $ s를 사용하면 그렇게하지 않고 "패턴을 찾을 수 없음"이라고 만 표시됩니다.
basteln 2011 년

7
"다음 10 개 라인"의 경우 :10:s/pattern/replace/gc
here

10
OP가 찾던 것이 아니더라도 매우 도움이되었습니다. 감사합니다!
Tobias J

51

%1,$
(Vim help => :help :%1, $ (전체 파일)과 같음 )에 대한 바로 가기입니다 .)

. 커서 위치이므로 할 수 있습니다.

:.,$s/\vBEFORE/AFTER/gc

문서의 처음부터 커서까지 바꾸려면

:1,.s/\vBEFORE/AFTER/gc

기타

:help range거의 모든 명령이 범위와 함께 작동하므로 범위에 대한 매뉴얼을 읽는 것이 좋습니다 .


감사합니다. 이미 알고있었습니다. 문서의 끝에 도달하면 검색 및 바꾸기가 래핑되기를 원합니다. :., $ s를 사용하면 그렇게하지 않고 "패턴을 찾을 수 없음"이라고 만 표시됩니다.
basteln 2011 년

@basteln : 게시물에 오타가 있습니다. // 복사하여 붙여 넣으셨습니까?
sehe

따라서 모든 것을 교체하고 싶지만 확인을 요청하려면 현재 위치에서 시작하고 싶습니다. 그럼 두 번만하세요.
mb14 2014 년

대신 n 및 &를 사용할 수 있습니다.
mb14 2014 년

흠, n과 &가 최선의 해결책이라고 생각하지만 그것이 정확히 어떻게되어야하는지는 아니지만 그러나 확실히 충분합니다, 감사합니다! :)
basteln 2011 년

4

검색을 종료하면 엄청난 함수를 작성하지 않고 파일의 시작 부분으로 돌아 간다는 사실에 대한 해결책을 마침내 찾았습니다.

내가 이것을 생각해내는 데 얼마나 오래 걸 렸는지 믿지 못할 것입니다. 줄 바꿈 여부를 묻는 메시지를 추가하기 만하면됩니다. 사용자가 q다시 누르면 줄 바꿈하지 않습니다. 따라서 기본적으로 ! qq대신 탭하여 검색을 종료하십시오 q. (포장하려면을 입력하십시오 y.)

:,$s/BEFORE/AFTER/gce|echo 'Continue at beginning of file? (y/q)'|if getchar()!=113|1,''-&&|en

나는 실제로 이것을 핫키에 매핑했습니다. 예를 들어 현재 위치에서 시작하여 커서 아래의 모든 단어를 검색하고 바꾸려면 다음을 사용하십시오 q*.

exe 'nno q* :,$s/\<<c-r>=expand("<cword>")<cr>\>//gce\|echo "Continue at beginning of file? (y/q)"\|if getchar()==121\|1,''''-&&\|en'.repeat('<left>',77)

제 생각에는이 접근 방식 ( 제 대답에 제안 된 두 명령 사이에 추가 프롬프트를 삽입)은 사용성을 향상시키지 않고 불필요한 복잡성을 추가합니다 . 에서 원래 버전 이 (와 최초의 대체 명령을 종료하면 q, l또는 Esc 키 ) 및 상단에서 계속하고 싶지 않아, 당신은 이미 눌러 q또는 Esc 키를 다시 바로 두 번째 명령을 종료합니다. 즉, 제안 된 프롬프트 추가는 이미 존재하는 기능을 효과적으로 복제하는 것처럼 보입니다.
ib.

야 .. 접근 해 봤어? "let"의 다음 3 개 항목을 "unlet"으로 바꾸고, 이것이 핵심 입니다. 단락을 계속 편집 한다고 가정 해 보겠습니다 . 접근 방식 "y, y, y를 누르십시오. 이제 q를 누르십시오. 이제 단락의 첫 번째 줄에서 검색을 시작합니다. 종료하려면 q를 다시 누르십시오. 이제 편집을 계속하려면 이전 위치로 돌아가십시오. Ok, g ;. 그래서 기본적으로 : yyyqq ... 혼란스러워집니다 ... g; (또는 u ^ R) 내 방법 : yyyqq
q335r49

물론 이상적인 방법은 yyyq방향을 바꾸지 않는 점프없이 계속 편집하는 것입니다. 그러나 yyyqq사용 편의성 측면에서 더블 탭을 거의 단일 탭이라고 생각하면 거의 비슷합니다.
q335r49

원래 명령이나 프롬프트가있는 버전 모두 해당 시나리오에서 커서를 이전 위치로 되 돌리지 않습니다. 전자는 맨 위에서 패턴의 첫 번째 발생 ( q두 번째 누르는 순간)에 사용자를 둡니다 . 후자는 교체 된 마지막 항목 (첫 번째 항목 q을 누르기 직전)에 커서를 둡니다 .
ib.

1
에서 원래 버전 이 자동으로 (사용자 입력없이 이전의 위치로 커서를 반환하는 것이 바람직 경우, Ctrl 키 + O ), 하나는 바로 추가 할 수 있습니다 norm!``명령을 : :,$s/BEFORE/AFTER/gc|1,''-&&|norm!``.
ib.

3

다음 은 2 단계 접근 방식 ( :,$s/BEFORE/AFTER/gc|1,''-&&) 또는 중간 "파일 시작 부분에서 계속 하시겠습니까?"로 검색을 래핑하는 것에 대한 우려를 해결하는 매우 대략적인 내용입니다. 접근하다:

" Define a mapping that calls a command.
nnoremap <Leader>e :Substitute/\v<<C-R>=expand('<cword>')<CR>>//<Left>

" And that command calls a script-local function.
command! -nargs=1 Substitute call s:Substitute(<q-args>)

function! s:Substitute(patterns)
  if getregtype('s') != ''
    let l:register=getreg('s')
  endif
  normal! qs
  redir => l:replacements
  try
    execute ',$s' . a:patterns . 'gce#'
  catch /^Vim:Interrupt$/
    return
  finally
    normal! q
    let l:transcript=getreg('s')
    if exists('l:register')
      call setreg('s', l:register)
    endif
  endtry
  redir END
  if len(l:replacements) > 0
    " At least one instance of pattern was found.
    let l:last=strpart(l:transcript, len(l:transcript) - 1)
    " Note: type the literal <Esc> (^[) here with <C-v><Esc>:
    if l:last ==# 'l' || l:last ==# 'q' || l:last ==# '^['
      " User bailed.
      return
    endif
  endif

  " Loop around to top of file and continue.
  " Avoid unwanted "Backwards range given, OK to swap (y/n)?" messages.
  if line("''") > 1
    1,''-&&"
  endif
endfunction

이 함수는 몇 가지 해킹을 사용하여 맨 위로 돌아 가야하는지 확인합니다.

  • 사용자가 "l", "q"또는 "Esc"를 눌렀을 때 줄 바꿈을하지 않습니다.이 중 어느 것이 든 중단하려는 것을 나타냅니다.
  • "s"레지스터에 매크로를 기록하고 마지막 문자를 검사하여 마지막 키 누름을 감지합니다.
  • "s"레지스터를 저장 / 복원하여 기존 매크로를 덮어 쓰지 마십시오.
  • 명령을 사용할 때 이미 매크로를 기록하고 있다면 모든 베팅이 해제됩니다.
  • 인터럽트로 올바른 일을하려고합니다.
  • 명시적인 가드로 "뒤로 범위"경고를 피합니다.

1
나는 이것을 작은 플러그인으로 추출하여 여기 GitHub에 넣었습니다 .
wincent 2015

방금 플러그인을 설치하면 매력처럼 작동합니다 !! 이것을 만들어 주셔서 대단히 감사합니다!
Tropilio

1

나는 파티에 늦었지만 그렇게하지 않기 위해 그렇게 늦은 스택 오버플로 응답자에게 너무 자주 의존했습니다. reddit 및 stackoverflow에 대한 힌트를 수집했으며 가장 좋은 방법은 \%>...c검색 에서 패턴 을 사용하는 것입니다.이 패턴은 커서 이후에만 일치합니다.

즉, 다음 교체 단계에서 패턴을 엉망으로 만들고 입력하기가 어렵습니다. 이러한 효과에 대응하기 위해 사용자 지정 함수는 나중에 검색 패턴을 필터링하여 재설정해야합니다. 아래를 참조하십시오.

나는 다음 발생을 대체하고 그 이후에 다음으로 점프하는 매핑으로 자신을 다 투었습니다 (어쨌든 내 목표였습니다). 나는 이것을 기반으로 글로벌 대체가 이루어질 수 있다고 확신합니다. :%s/.../.../g아래 패턴이 일치 항목을 필터링하는 것과 같은 것을 목표로하는 솔루션을 작업 할 때 유의 하세요. 이 커서 위치에 남아 모든 행 그러나 단일 대체가 완료된 후에 정리되므로 바로 그 효과를 잃고 다음 경기이므로 모든 경기를 하나씩 진행할 수 있습니다.

fun! g:CleanColFromPattern(prevPattern) return substitute(a:prevPattern, '\V\^\\%>\[0-9]\+c', '', '') endf nmap <F3>n m`:s/\%><C-r>=col(".")-1<CR>c<C-.r>=g:CleanColFromPattern(getreg("/"))<CR>/~/&<CR>:call setreg("/", g:CleanColFromPattern(getreg("/")))<CR>``n


감사합니다. 늦은 답변이 도움이되는 경우가 많다는 데 동의합니다. 나는 개인적으로 오랫동안 (이런 이유로) 더 이상 Vim을 사용하지 않지만, 다른 많은 사람들이 이것이 도움이 될 것이라고 확신합니다.
basteln
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.