Substituir espaços no início da linha por “-”

4
 wqdq
 wqdqgrhehr
 cnkzjncicoajc
 hello space
    oejwfoiwejfow
    wqodojw
    more spaces
    more

Este é o meu arquivo e eu gostaria de fazer isso com sed :

-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--

Eu tenho que usar o loop para fazer isso ou existe alguma abordagem diferente? Eu tentei isso:

user:~$ sed -n  '
: loop
  s/^ /-/
  s/[^-] /-/p
t loop' spaces
    
por Muffy 10.05.2017 / 22:07

4 respostas

9

Com sed , você precisaria de um loop como:

sed -e :1 -e 's/^\( *\) /-/; t1' < file

Ou faça algo como:

sed '
s/ */&\
/; # add a newline after the leading spaces
h; # save a copy on the hold space
y/ /-/; # replace *every* space with -
G; # append our saved copy
s/\n.*\n//; # remove the superflous part' < file

Com perl , você pode fazer coisas como:

perl -pe 's{^ *}{$& =~ y/ /-/r}e' < file

ou

perl -pe 's/(^|\G) /-/g' < file

\G em correspondências PCRE (com largura zero) no final da correspondência anterior (em //g contexto). Então, aqui, estamos substituindo um espaço que segue o início da linha ^ ou o final da correspondência anterior (ou seja, o espaço substituído anteriormente).

(esse também funcionaria com sed implementações que suportam PCREs como ssed -R ).

Com awk , você pode fazer algo como:

awk '
  match($0, /^ +/) {
    space = substr($0, 1, RLENGTH)
    gsub(" ", "-", space)
    $0 = space substr($0, RLENGTH+1)
  }
  {print}' < file

Se você quiser converter também as guias (onde, por exemplo, <space><tab>foo seria convertido em --------foo ), você poderá pré-processar a entrada com expand . Com o GNU expand , você pode torná-lo expand -i , de modo que apenas as guias entre os espaços em branco iniciais na linha sejam convertidas. Você pode especificar a distância entre as paradas de tabulação (a cada 8 colunas, por padrão) com a opção -t .

Para generalizar isso para todos os caracteres de espaçamento horizontal, ou pelo menos aqueles que estão na categoria [:blank:] em sua localidade, isso se torna mais complicado.

Se não fosse pelo caractere TAB, seria apenas uma questão de:

perl -Mopen=locale -MText::CharWidth=mbswidth -pe 's/^\h+/"-" x mbswidth($&)/e'

Mas o caractere TAB que é um caractere controle tem uma largura de -1 com esse mbswidth() , enquanto na realidade ele tem uma largura variável de 1 a 8 colunas dependendo de onde ele é encontrado na linha.

O comando expand cuida de expandi-lo para o número de espaços correto , mas várias implementações, incluindo o GNU expand , não acertam quando há caracteres multi-byte ( como todos os caracteres em branco, exceto aba, espaço em locales UTF-8), e até mesmo alguns que suportam caracteres de múltiplos bytes podem ser enganados por caracteres de largura zero ou de largura dupla (como U + 3000, que está no[:blank:] % de classes em locais típicos do GNU, pelo menos). Então, seria necessário fazer a expansão da tabulação manualmente:

perl -Mopen=locale -MText::CharWidth=mbswidth -pe 's{^\h+}{
  $s = $&;
  while ($s =~ /(.*?)\t(.*)/) {
    $s = $1 . (" " x ((7-mbswidth($1)) % 8 + 1)) . $2;
  }
  "-" x mbswidth($s)}e'
    
por 10.05.2017 / 22:30
0

Stephane já forneceu a solução sed adequada. Aqui está uma pequena e mais explícita alternativa do Python 3:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as f:
    for line in f:
        beginning = True
        for char in line:
            if beginning and char == " ":
                print("-",end="")
            else:
               beginning = False
               print(char,end="")

Execução de teste:

# This is the input text
$ cat -A input.txt
 wqdq$
 wqdqgrhehr$
 cnkzjncicoajc$
 hello space$
    oejwfoiwejfow$
    wqodojw$
    more spaces$
    more$
    $
 $
  $

# And this is the output with the given python script
$ ./add_dashes.py ./input.txt                                                                                            
-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--
    
por 10.05.2017 / 23:32
0

Outra abordagem awk :

awk 'match($0, /^[[:space:]]+/){ p=""; l=RLENGTH; while(l--) p=p"-";
     sub(/^[[:space:]]+/,p); print}' yourfile

A saída:

-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--

match($0, /^[[:space:]]+/) - corresponde à sequência dos espaços principais

l=RLENGTH - o tamanho da sequência correspondente de cada linha

while(l--) p=p"-" - construindo a substring de substituição

Alternativa Python abordagem 3.x:

Script

space_to_hyphen.py :

import sys, re
with open(sys.argv[1], 'r') as f:  # reading input file
    for l in f.read().splitlines():
        m = re.match(r'^ +', l)    # capture sequence of leading spaces 
        print(l if not m else l.replace(' ', '-', m.end()))

Uso :

python3 space_to_hyphen.py yourfile
    
por 11.05.2017 / 09:18
0

Trabalhando

Nós configuramos um loop do-while e continuamos convertendo o último espaço adjacente ao primeiro não-espaço enquanto a linha ainda tem um espaço à esquerda.

sed -e '
   :loop
      /^ /s/ \([^ ]\|$\)/-/
   tloop
' filename.ext


while IFS= read -r l; do
   read -r ll <<<"$(printf '%ss\n' "$l")"
   printf '%s%s\n' \
      "$(seq -s= 0 "$(expr "$l" : '[   ]*')" | tr = - | tr -cd -)" \
      "${ll%?}"
done < filename.ext

Resultado

-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--

Funcionamentos

  • Configure um loop while para ler o arquivo linha por linha com um IFS definido como NULL . Isso tem o objetivo de preservar todos os espaços em branco na linha.
  • Em seguida, faça uma leitura simulada da mesma linha com o padrão IFS . Isso cortará qualquer espaço em branco principal. Adicionamos um caractere não-linear fictício no final para evitar o colapso das novas linhas finais no estágio de expansão de comando. Nós nos despirmos no momento da impressão.
  • O comando expr tem o objetivo de encontrar o número de correspondências, no nosso caso, espaço em branco na borda principal da linha.
  • Usando esse número, geramos uma sequência de traços por meio dos comandos seq e tr apropriadamente configurados.
  • Por fim, imprimimos os traços juntamente com a linha trimmed , ou seja, a linha lida por meio do IFS padrão.
por 11.05.2017 / 04:36