Como remover letras duplicadas usando sed?

5

Usando o sed, como posso remover cartas duplicadas do HEADERS em um arquivo de texto?

NNAAMMEE
       nice - run a program with modified scheduling priority

SSYYNNOOPPSSIISS
       nice     [-n    adjustment]    [-adjustment]    [--adjustment=adjustment] [command [a$

Acima é um exemplo. Eu quero que a saída após a análise com sed seja:

NAME
       nice - run a program with modified scheduling priority

SYNOPSIS
       nice     [-n    adjustment]    [-adjustment]    [--adjustment=adjustment] [command [a$
    
por slm 10.09.2013 / 23:34

7 respostas

9

Método 1

Você pode usar este comando sed para fazer isso:

$ sed 's/\([A-Za-z]\)\+//g' file.txt

Exemplo

Usando sua entrada de amostra acima, criei um arquivo, sample.txt .

$ sed 's/\([A-Za-z]\)\+//g' sample.txt 
NAME
       nice - run a program with modified scheduling priority

       SYNOPSIS
              nice     [-n    adjustment]    [-adjustment] [--adjustment=adjustment] [command [a$

Método 2

Existe também este método que remove todos os caracteres duplicados:

$ sed 's/\(.\)//g' file.txt 

Exemplo

$ sed 's/\(.\)//g' sample.txt 
NAME
    nice - run a program with modified scheduling priority

    SYNOPSIS
       nice   [-n  adjustment]  [-adjustment] [-adjustment=adjustment] [command [a$

Método 3 (apenas em maiúsculas)

O OP perguntou se você poderia modificá-lo para que apenas os caracteres maiúsculos fossem removidos. Veja como usar um método modificado 1.

Exemplo

$ sed 's/\([A-Z]\)\+//g' sample.txt 
NAME
       nice - run a program with modified scheduling priority

       SYNOPSIS
              nice     [-n    adjustment]    [-adjustment] [--adjustment=adjustment] [command [a$

Detalhes dos métodos acima

Todos os exemplos fazem uso de uma técnica na qual quando um caractere é encontrado pela primeira vez no conjunto de caracteres A-Z ou a-z, o valor é salvo. Quebra automática de parentes em torno dos caracteres diz sed para salvá-los para mais tarde. Esse valor é então armazenado em uma variável temporária que você pode acessar imediatamente ou mais tarde. Essas variáveis são denominadas \ 1 e \ 2.

Então o truque que estamos usando é a primeira letra.

\([A-Za-z]\)

Então nos viramos e usamos o valor que acabamos de salvar como um caractere secundário que deve ocorrer logo após o primeiro acima, portanto:

\([A-Za-z]\).

Em sed , também estamos usando o recurso de pesquisa e substituição, s/../../g . O g significa que estamos fazendo isso globalmente.

Então, quando encontramos um caractere, seguido por outro, nós o substituímos por apenas um do mesmo caractere.

    
por 11.09.2013 / 00:52
3

Este comando remove todas as letras duplas:

sed 's/\([[:alpha:]]\)//g'

significa o texto dentro de \(…\) , portanto, este comando significa: sempre que houver um caractere alfabético seguido por si, substitua apenas por esse caractere alfabético.

Isso transformará, e. command em comand . Seria melhor restringir a transformação para onde ela é necessária: linhas sem recuo.

sed '/^[[:alpha:]]/ s/\([[:alpha:]]\)//g'

Este texto é uma página man renderizada para terminais onde o negrito é representado por overstrike: C\bC é representado em negrito, onde \b é o caractere de retrocesso (número de caractere 8, também conhecido como ^ H). Se os caracteres de controle ainda estiverem lá, esqueça as letras duplicadas e, em vez disso, remova o overstrike.

sed -e 's/.\b//g'

Se você tiver uma maneira de formatar a saída, transofmr C\bC para negrito e _\bC para sublinhar.

sed -e 's/\(.\)\b/\e[1m\e[22m/g' -e 's/_\b\(.\)/\e[4m\e[24m/g' |
sed -e 's/\e[22m\e[1m//g' -e 's/\e[24m\e[4m//g'

Se o seu sed não entender os escapes da barra invertida, use os caracteres literais (Ctrl + H para \b e Ctrl + [para \e ).

    
por 11.09.2013 / 02:43
2

Esta não é uma tarefa trivial. Uma simples substituição por letras duplas seria desastrosa. Pense no que isso faria com palavras como "atenção" ou "esquecimento" ou (mais relevante para o seu caso) "comando". O script abaixo é uma primeira tentativa ingênua de uma solução. Faz uso de um dicionário para determinar quais palavras realmente têm letras duplicadas.

#!/usr/bin/perl

use strict;
use warnings;

my $input_file = shift//die "No file name given\n";
my $dictionary = shift//'/usr/share/dict/words';
open my $if,'<',$input_file or die "$input_file: $!\n";
open my $dict,'<',$dictionary or die "$dictionary: $!\n";
my %dictionary;
for(<$dict>){
    chomp;
    $dictionary{$_}++;
}
close $dictionary;

LINE: while(<$if>){
    chomp;

    WORD: for my $word ( split /\s+/ ){
            print "$word " and next WORD if exists $dictionary{lc $word};

            SUBSTITUTION: while($word=~ s{([A-Z])}{$1}i){
                exists $dictionary{lc $word} and last SUBSTITUTION;
            } #END SUBSTITUTION
            print "$word ";

     } #END WORD

     print "\n";

} #END LINE

Chame como

[user@host]./myscript.pl input_file optional_dictionary_file >output_file

Se você não fornecer um segundo argumento, o arquivo de dicionário será padronizado como /usr/share/dict/words , que deve estar disponível em um GNU / Linux decente.

Aviso de isenção: isso não foi testado.

Advertências:

  • Ele irá quebrar pelo menos com palavras hifenizadas (usa espaços para decidir o que é uma "palavra").
  • Ele só remove maiúsculas duplicadas para evitar mexer no conteúdo da própria página man .
  • Isso causará estragos em hexadecimais como 0xFFFF .
  • Provavelmente muitos mais que não consigo ver.
por 11.09.2013 / 01:10
1

Parece que o seu exemplo é de páginas masculinas.

O MAN foi originalmente concebido em teletipo. Para negrito, a sequência é XX. O provavelmente se perdeu na sua saída para o editor, daí os caracteres duplos.

O vi pode facilmente remover estes.

Veja: link

    
por 16.10.2015 / 13:04
0

Você pode restringir as alterações às sequências afetadas com algo parecido com isto:

eval sed $(
for i in NAME SYNOPSIS DESCRIPTION "RETURN VALUE" ENVIRONMENT FILES EXAMPLES DIAGNOSTICS ERRORS "SEE ALSO" "CONFORMING TO" HISTORY AUTHORS BUGS; do
  dup=$(for j in $(seq 0 ${#i}); do printf "%s%s" "${i:j:1}" "${i:j:1}"; done)
  printf " -e \"s/%s/%s/\"" "$dup" "$i"
done)
    
por 11.09.2013 / 02:15
0

Em Python:

Método 1 # Usando a própria função:

#!/usr/bin/env python
from __future__ import print_function
import sys

def RemoveDupliChar(Word):
        NewWord = " "
        index = 0
        for char in Word:
               if char != NewWord[index]:
                       NewWord += char
                       index += 1
        print(NewWord.strip())

with open(sys.argv[1],'r') as InputFile:
        for line in InputFile:
                if line.isupper():
                        RemoveDupliChar(line)
                else:
                        print(line,end='')

Método 2 # Usando itertools.groupby :

Obrigado a @ falstretu

#!/usr/bin/env python
from __future__ import print_function
import itertools
import sys

with open(sys.argv[1],'r') as InputFile:
        for line in InputFile:
                if line.isupper():
                        print(''.join(ch for ch, _ in itertools.groupby(line)))
                else:
                        print(line,end='')

Execução:

root@ubuntu:~# python remove_duplicate_char.py Input.txt
NAME
       nice - run a program with modified scheduling priority

SYNOPSIS
       nice     [-n    adjustment]    [-adjustment]    [--adjustment=adjustment] [command [a$
    
por 14.09.2013 / 05:48
0

Tente:

sed -e 's/\([A-Za-z]\)//g'  

Basta remover o \+ e, em seguida, apenas as letras duplicadas serão reduzidas a uma única letra. (Funciona assumindo que todos os caracteres foram duplicados)

Experimente este pequeno teste:

echo "PPaayy Atttteenttiioonn ttoo aallll ccoommmmaanndds" > test.txt
sed -e 's/\([A-z]\)//g' < test.txt > test2.txt
cat test2.txt
    
por 23.12.2015 / 16:34