Simples substituição de guias sed misteriosamente falhando


44

Isso deve ser muito simples, mas por algum motivo não está funcionando:

sed -i.bak -E 's/\t/  /' file.txt

Em vez de substituir caracteres de tabulação, está substituindo tcaracteres. Eu tentei todas as variações que pude pensar, brincando com citações, etc. Pesquisei no Google e encontrei todo mundo usando expressões muito semelhantes e elas parecem funcionar para elas.

O -Eé uma coisa do OS X. Eu pensei que a falha poderia ser o resultado de alguma peculiaridade estranha do OS X sed, então eu tentei com o Ruby também (sem o -i) e obtive o mesmo resultado:

ruby -pe '$_.gsub!(/\t/,"  ")' < file.txt > file.new

Estou usando o Bash 3.2.51 no OS X e no iTerm, embora não consiga ver como isso poderia ser terrivelmente relevante. Eu não defini nenhuma variável de ambiente estranha, embora eu possa postar alguma que você ache relevante.

O que pode estar errado?

ATUALIZAÇÃO : Devo ter feito algum outro erro ou erro de digitação quando eu tentei a versão Ruby, desde Gilles salienta que faz o trabalho (e eu já não tinha ele me orientar errado!). Não tenho certeza do que aconteceu, mas tenho certeza de que deve ter sido meu erro.


5
Pode ser que você deva tentar substituir o \tna sedinstrução por CTRL-V<TAB>onde <TAB>está a tecla Tab e a CTRL-Vtecla Control e vpressionadas juntas.
unxnut

se o ruby ​​também estiver recebendo respostas erradas, pode ser sua biblioteca regexp. (Eu testei os seus comandos e os dois substituem a guia por 2 espaços.) Portanto, se você instalar o Gnu sed, esperamos que também instale a biblioteca correta.
Ctrl-alt-delor 18/07/2014

Respostas:


64

A sintaxe \tpara um caractere de tabulação no sed não é padrão. Essa fuga é uma extensão GNU sed . Você encontra muitos exemplos on-line que o usam porque muitas pessoas usam o GNU sed (é a implementação sed no Linux não incorporado). Mas o OS X sed , como outro * BSD sed, não suporta \ttab e, em vez disso, trata \tcomo significando barra invertida seguida por t.

Existem muitas soluções, como:

  • Use um caractere de tabulação literal.

    sed -i.bak 's/  /  /' file.txt
    
  • Use trou printfpara produzir um caractere de tabulação.

    sed -i.bak "s/$(printf '\t')/  /" file.txt
    sed -i.bak "s/$(echo a | tr 'a' '\t')/  /" file.txt
    
  • Use a sintaxe de string do bash, permitindo escapes de barra invertida .

    sed -i.bak $'s/\t/  /' file.txt
    
  • Use Perl, Python ou Ruby. O snippet de Ruby que você postou funciona.


Para scripts sed que estão contidos em um ...sedscript (usado via -fopção), os caracteres da guia literal parecem a única possibilidade para mim. Ao editar isso com o vim, set noexpandtabé importante.
Tobias

Aviso: use essa técnica "caractere de tabulação literal" se desejar que seu colega de trabalho volte atrás de você e quebre seu script posteriormente. Use essa trtécnica somente se você quiser que seu colega de trabalho lhe apunhale quando ele ler seu script.
Bruno Bronosky

A segunda aspas duplas está extraviada no segundo bloco de código? Eu tive que movê-lo para onde está atualmente a aspas simples.
Ellen Spertus

Obrigado pelo link para a sintaxe do string bash ... Eu não tinha ideia (e esta é a melhor opção, IMHO).
levigroker 24/10

sed $'s/<regex>/\t/' file.txtfunciona para inserir, mas $parece quebrar meu script quando tento incluir parte do regex na minha substituição, ou seja, sed $'s,\(ontology/[0-9]\+\),\t\txxx\1xxx\t\t,'dá `xxxxxx` com o meu valor de correspondência esperado substituído por ``. Existe um equivalente ao \1usar a sintaxe de string do bash? Edit: é suposto o caractere unicode U + 231C no meio do xxx <U + 231C> xxx.
Josh

14

Use uma citação específica do Bash que permita usar seqüências de caracteres como em C, para que um caractere de tabulação real seja passado para sed, não uma sequência de escape:

sed -i.bak -E $'s/\t/  /' file.txt

11
Também chamado de "ANSI-C", citando se outros desejam procurar mais informações sobre ele.
wisbucky

2
Parece funcionar em qualquer shell bourne, funciona também em UNIXes que não sejam do bash. Porém, não funciona em variantes-csh.
jornane 15/11

3
sed -i $'s/\t/  /g' file.txt 

funciona para mim no OS X e é o mesmo comando que eu uso no linux o tempo todo.


Observe que isso substitui todas as guias em todas as linhas, enquanto o OP pretende substituir apenas a primeira (a julgar pelo comando que eles usam).
Kusalananda

1

Como observado, nem todas as sedimplementações suportam a notação \tcomo uma guia horizontal.

Você pode facilmente conseguir sua substituição com:

 perl -pi.old -e 's{\t+}{ }g' file.txt

Isso realiza uma substituição in situ que preserva o arquivo original como "* .old". O Perl permite delimitadores alternativos para o clássico, /tornando a expressão muito mais legível (ou seja, desprovida da síndrome do "palito de dentes inclinado").

O +diz um ou mais repetições de um caractere de tabulação devem ser substituídos. O gmodificador permite substituições globais no final de cada linha.


0

Você também pode usar echodentro sed:

sed -i "s/$(echo '\t')//g"


Note que echo '\t'apenas produzirá \tna implementação de alguns shells de echo.
Kusalananda

0

Se você quer um mais poderoso sed(suportado \te mais) do que o OS X, instale o GNU sed .


Como também não funcionou com Ruby, não sei por que concluiria que sedo problema do OS X é. Você tem um motivo para acreditar que esse é o problema? Eu ficaria feliz em instalar o GNU sed se tivesse motivos para acreditar que resolveria o problema, mas parece que eu já descartei isso.
Iconoclast

Com Ruby, você terá que usar apenas uma barra invertida:ruby -pe '$_.gsub!(/\t/," ")' < file.txt
vinc17

0

Se não há problema em exigir bashou zshcomo um shell, então esta é a solução mais fácil que consigo pensar:

sed "s/$(echo -n -e "\t")/ /" file.txt

Observe, no entanto, que os echosinalizadores ( -ne -e) são indefinidos no POSIX, portanto, um shell em conformidade com o POSIX não requer a compreensão desses sinalizadores, mas muitos o farão por motivos de compatibilidade.


-1

Estou surpreso que ninguém tenha sugerido a solução muito simples de: sed -i.bak -E 's/\\\t/ /' file.txt Isso deve funcionar.

Você precisa escapar da fuga (daí os 3 \ s) para permitir ao sed entender que você está tentando usar um caractere \ t na expressão regular quando tudo é substituído ...


Por que três barras invertidas especificamente?
Michael Homer

3
Se eu usar o GNU sed, \ basta um, pois não é necessário escapar. O problema é que o BSD sednão suporta essa sintaxe para guias.
Iconoclast

Não funciona no meu El Capitan.
Franklin Yu

-4

Isso funcionou para mim.

sed -e 's / [\ t] / / g'


3
Isso ocorre porque você usa o GNU sed. Não é isso que o OP usa.
Kusalananda
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.