sed convierte 4 espacios a 2

14

¿Cómo se convierten 4 espacios en 2 espacios con sed? ¿Es posible?

Encontré esto pero convierte la pestaña en espacios:

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

chrisjlee
fuente

Respuestas:

13

La secuencia de comandos que publicó convierte 4 * n espacios en n pestañas, solo si esos espacios van precedidos solo por pestañas.

Si desea reemplazar 4 espacios por 2 espacios, pero solo en sangría, aunque es posible hacerlo con sed, recomiendo Perl en su lugar.

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

En sed:

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

Es posible que desee utilizar indenten su lugar.

Gilles 'SO- deja de ser malvado'
fuente
TengoNested quantifiers in regex; marked by <-- HERE in m/^( {4}* <-- HERE )/ at -e line 1.
eddygeek
1
@eddygeek Oh, de hecho, eso y algunos otros errores. Reemplacé el gobbledygook por el código perl real.
Gilles 'SO- deja de ser malvado'
5

No funciona la forma directa:

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

Si no, publique alguna entrada donde falla.

Thor
fuente
1
Esto no está restringido al comienzo de la línea. Si lo ancla allí, no funcionará para múltiples coincidencias. Entonces estamos en unix.stackexchange.com/a/375200/259620 a continuación.
geek-merlin
Esto no funciona si tiene una combinación de 2 espacios y 4 espacios, porque dos muescas de dos se contarán como cuatro ...
Matt Fletcher
@aexl: Eso no fue un requisito del OP
Thor
@mattfletcher: la pregunta era sobre reemplazar 4 espacios con 2, así que no veo su punto
Thor
4

Si solo se van a convertir los espacios iniciales:

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

Con comentarios:

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'

También mire la 'ts'configuración y el :retabcomando de vim

Stéphane Chazelas
fuente
Usando su solución vim, ¿hay alguna manera de editar por lotes varios archivos como ese con vim? De lo contrario, supongo que tendría que crear una macro.
chrisjlee
Tenga en cuenta que 'ts'y :retabno son soluciones a la pregunta, pero están relacionadas y pueden ayudar a abordar su objetivo general. Puedes hacer vim -- *.c, :set ts=...y luego :argdo retabo :argdo retab!. Consulte también la 'sw'opción y las capacidades de sangría propias de vim.
Stéphane Chazelas
2
sed 's/^\( \+\)\1\1\1/\1\1/' file

Funciona dividiendo los espacios iniciales en cuatro instancias del mismo grupo (para que todos sean iguales) y luego reemplazándolos con solo dos instancias del grupo.

Mal
fuente
¿Cómo mejora esto las soluciones existentes?
Philippos
1
Conserva los espacios impares, solo afecta los espacios en blanco iniciales, no requiere la bandera global, se puede ajustar fácilmente para manejar cualquier número de espacios (o pestañas) en ambos lados del reemplazo y no hace uso de comandos sed más complejos que puede ser desagradable para usuarios más ocasionales. Al igual que con muchas cosas, si es una mejora es bastante subjetivo, pero hablando por mí mismo: encontrar esto en un script sería significativamente más fácil de asimilar rápidamente que algunas de las otras soluciones enumeradas.
Mal
Interesante. Me alegro de haberlo preguntado. Por lo general, las referencias inversas se consideran malvadas en lugar de la bandera global. Pero podría lograr fácilmente lo mismo con un script más portátil (evitando \+). Gracias.
Philippos
1
sed 's/    \{2,4\}\( \{0,1\}[^ ].*\)*/  \1/g' <input

Eso solo debería exprimir las secuencias principales de espacios.

mikeserv
fuente