Encontre e substitua usando expressões regulares

26

Eu tenho um arquivo com vários padrões de usuário. Quero alterar parte do texto, mas estou tendo dificuldades para encontrar um substituto e um correspondente. Usando o seguinte exemplo:

###############################################################################
# Trackpad, mouse, keyboard, Bluetooth accessories, and input                 #
###############################################################################

# Trackpad: enable tap to click for this user and for the login screen
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

Eu gostaria de substituir # Trackpad: ...porrunning "Trackpad: ..."

Quebrando o problema, eu vim com algo usando um testador de regex:

/\n\n#\s(.*)/g

Se eu tentar usar isso no Vim, não funcionará para mim:

:/\n\n#\s(.*)/running "\1"/g

Acho que meu problema se resume a duas perguntas específicas:

  1. Como evitar a pesquisa de \ncaracteres e garantir #que não apareça no final do grupo de pesquisa?
  2. Como posso usar efetivamente grupos de captura?

Existem ótimas respostas abaixo. Difícil escolher entre os três, no entanto, sinto que a resposta escolhida é a mais precisa para minhas especificações originais. Eu recomendo que você tente todas as três respostas com o arquivo real para ver como você se sente sobre elas.

squarefrog
fonte

Respostas:

18

Só para deixar claro… acredito que você pediu que este fosse o resultado da substituição?

###############################################################################
# Trackpad, mouse, keyboard, Bluetooth accessories, and input                 #
###############################################################################

running "Trackpad: enable tap to click for this user and for the login screen"
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

Nesse caso, recomendo o seguinte comando:

:%s/\n\n#\s\+\(.*\)/^M^Mrunning "\1"/

Explicação do padrão

:s/PATTERN/REPLACEMENT/é o comando substituto . O sinal de porcentagem :%sfaz com que funcione em todo o arquivo, e não apenas na linha atual.

O \n\ndiz que a linha de interesse deve ocorrer após uma linha em branco. Se você não se importasse com a linha em branco anterior, ^seria suficiente.

#\s\+corresponde a um caractere de hash seguido por um ou mais caracteres de espaço em branco. \(.*\)captura todo o texto subsequente na linha.

Explicação do texto de substituição

^M^Minsere duas extremidades das linhas para substituir as \n\nque estavam presentes no padrão. Caso contrário, o texto seria movido para o final da linha que precede a linha em branco. Para digitar cada um ^M, pressione Ctrl-V Ctrl-M.

Em seguida, insira a sequência running, seguida pelo que foi capturado entre parênteses, entre aspas duplas.

200_success
fonte
Não consigo editar sua resposta, mas acredito que você quis dizer :%s/\v\n\n#\s+(.*)/^M^Mrunning "\1"/(adicionou a bandeira "mágica"). É realmente difícil escolher uma resposta correta para minha pergunta original, mas acho que essa resposta é a mais próxima da resposta original esperada. É também o único que funciona em todo o arquivo sem a necessidade de selecionar um intervalo.
Squarefrog
11
IIRC você pode usar em \rvez de ^Mobter novas linhas?
Muru
11

Eu usaria algo como:

:s/^#\s\+\(.\{-}\):/running "\1":/
  • ^#para corresponder ao #caractere ancorado no início da linha (isso responde à pergunta 1)
  • \s\+ para corresponder a qualquer espaço em branco uma ou mais vezes
  • \( iniciar um grupo (isso responde à pergunta 2)
  • .\{-}\corresponder a qualquer caractere 0 ou mais vezes de maneira não gananciosa ; isso é diferente, .*na medida em que tenta corresponder o mínimo possível e não tanto quanto possível. Tente adicionar um :caractere no comentário para ver por que isso importa
  • \) para finalizar o subgrupo.
  • : corresponde a um literal :

Em seguida, substituímos isso pelo texto que você deseja e usamos \1para se referir ao grupo que capturamos.

Eu vim com algo usando um testador de regex

A sintaxe da expressão regular é um pouco parecida com a sintaxe do wiki: existem várias, todas elas são parecidas à primeira vista, nenhuma delas é obviamente melhor do que qualquer outra, mas há muitas diferenças.

Hoje, as chamadas expressões regulares "compatíveis com Perl" são o padrão de fato na maioria dos idiomas, mas as expressões regulares do Vim não são compatíveis com expressões compatíveis com Perl! A sintaxe do vim regexp remonta a pelo menos os anos 70, quando Perl nem estava por perto.

Você pode ver isso nos subgrupos, onde você deve usar \(e não ((isso é compatível com a sintaxe 'básica' do POSIX, mas não com a sintaxe 'estendida' mais comum do POSIX ou a sintaxe Perl). Você pode controlar isso adicionando a \vbandeira em um padrão (consulte :help /\vpara obter detalhes), isso a tornará "mais compatível", mas não completamente (você ainda precisa usar .{-}correspondências não gananciosas, por exemplo)

Portanto, isso pode explicar por que o uso de "um testador de regex" quase, mas não completamente, funciona.

http://www.vimregex.com/ como uma boa visão geral / cheatsheet de regexps do Vim.

Martin Tournoij
fonte
11
Ótima resposta, especialmente com o porquê regex! = Regex. Quanto à sua pesquisa e substituição, é um pouco complicado, pois o comentário pode ou não ter um :. Consulte o arquivo completo para detalhes github.com/squarefrog/dotfiles/blob/master/osx/...
squarefrog
8

Você pode:

  • pesquise linhas que não terminem em #::/[^#]$/
  • substituir #\s(.*)no início da linha:s/\v^#\s(.*)/running "\1"/

Para usar grupos, você precisa:

  • escapar dos colchetes para que eles se tornem parte da sintaxe regex: \(.*\)ou
  • use "magic" iniciando a expressão com \v:s/\v...

Combinando:

:/[^#]$/s/\v^#\s(.*)/running "\1"/
muru
fonte
11
Isso funciona de maneira brilhante, obrigado por incluir uma explicação do significado das tags, e não apenas do texto que eu precisava escrever.
Squarefrog 29/04
Obrigado por deixar claro que os colchetes precisam ser escapados para se tornar parte da sintaxe regex. Eu sempre tive um problema com os grupos regex em fazê-los trabalhar.
Adama