sed converte 4 espaços em 2

14

Como converter 4 espaços em 2 espaços com sed? É possível?

Achei isso, mas ele converte tabulação em espaços:

sed -r ':f; s|^(\t*)\s{4}|\1\t|g; t f' file

chrisjlee
fonte

Respostas:

13

O script que você postou converte 4 * n espaços em n guias, somente se esses espaços forem precedidos apenas por guias.

Se você deseja substituir 4 espaços por 2 espaços, mas apenas em recuo, embora seja possível fazê-lo com sed, eu recomendo o Perl.

perl -pe 's{^((?: {4})*)}{" " x (2*length($1)/4)}e' file

Em sed:

sed -e 's/^/~/' -e ': r' -e 's/^\( *\)~    /\1  ~/' -e 't r' -e 's/~//' file

Você pode usar em seu indentlugar.

Gilles 'SO- parar de ser mau'
fonte
Eu tenhoNested quantifiers in regex; marked by <-- HERE in m/^( {4}* <-- HERE )/ at -e line 1.
eddygeek
1
@eddygeek Oh, de fato, isso e alguns outros erros. Substituí o gobbledygook pelo código perl real.
Gilles 'SO- stop be evil'
5

O caminho direto não funciona:

sed -r 's/ {4}/  /g'

Caso contrário, publique alguma entrada onde ela falhar.

Thor
fonte
1
Isso não é restrito ao início da linha. Se você o ancorar lá, ele não funcionará para várias correspondências. Portanto, estamos em unix.stackexchange.com/a/375200/259620 abaixo.
geek-merlin
Isso não funciona se você tiver uma combinação de 2 lugares e 4 espaços, porque dois recortes de dois será contado como quatro ...
Matt Fletcher
@ aexl: Isso não era um requisito pelo OP
Thor
@mattfletcher: a pergunta era sobre a substituição de 4 espaços com 2, então eu não vejo o seu ponto
Thor
4

Se apenas espaços iniciais forem convertidos:

sed 'h;s/[^ ].*//;s/    /  /g;G;s/\n *//'

Com comentários:

sed '
  h; # save a copy of the pattern space (filled with the current line)
     # onto the hold space
  s/[^ ].*//; # remove everything starting with the first non-space
              # from the pattern space. That leaves the leading space
              # characters
  s/    /  /g; # substitute every sequence of 4 spaces with 2.
  G; # append a newline and the hold space (the saved original line) to
     # the pattern space.
  s/\n *//; # remove that newline and the indentation of the original
            # line that follows it'

Veja também a 'ts'configuração e o :retabcomando do vim

Stéphane Chazelas
fonte
Usando sua solução vim, existe uma maneira de editar em lote vários arquivos como esse com o vim? Caso contrário, eu acho que eu teria que criar uma macro?
Chrisjlee # 10/12
Observe que 'ts'e :retabnão são soluções para a pergunta, mas estão relacionadas e podem ajudar a atingir seu objetivo geral. Você pode fazer vim -- *.c, :set ts=...e então :argdo retabou :argdo retab!. Consulte também a 'sw'opção e os recursos de recuo do vim.
Stéphane Chazelas
2
sed 's/^\( \+\)\1\1\1/\1\1/' file

Ele funciona dividindo os espaços iniciais em quatro instâncias do mesmo grupo (para que todos sejam iguais) e depois substituindo-os por apenas duas instâncias do grupo.

Mal
fonte
Como isso melhora as soluções existentes?
Philippos
1
Ele preserva espaços ímpares, afeta apenas os espaços em branco à esquerda, não requer o sinalizador global, pode ser facilmente ajustado para lidar com qualquer número de espaços (ou guias) nos dois lados da substituição e não utiliza comandos sed mais complexos que pode ser desanimador para usuários mais casuais. Como em muitas coisas, se é uma melhoria bastante subjetiva, mas falando por mim: encontrar isso em um script seria significativamente mais fácil de entender rapidamente do que algumas das outras soluções listadas.
Mal
Interessante. Ainda bem que perguntei. Normalmente, as referências anteriores são consideradas más, e não a bandeira global. Mas você poderia facilmente conseguir a mesma coisa com scripts mais portáteis (evitando \+). Obrigado.
Philippos
1
sed 's/    \{2,4\}\( \{0,1\}[^ ].*\)*/  \1/g' <input

Isso deve apenas espremer as principais seqüências de espaços.

mikeserv
fonte