web-dev-qa-db-fra.com

Remplacement simple des onglets échouant mystérieusement

Cela devrait être vraiment simple, mais pour une raison quelconque, cela ne fonctionne pas:

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

Au lieu de remplacer les caractères de tabulation, il remplace les caractères t. J'ai essayé toutes les variantes auxquelles je pouvais penser, en jouant avec les citations, etc. J'ai recherché sur Google et trouvé tout le monde en utilisant des expressions assez similaires et elles semblent fonctionner pour elles.

Le -E est une chose OS X. Je pensais que l'échec pourrait être le résultat d'une bizarrerie bizarre du sed d'OS X, alors je l'ai essayé avec Ruby également (sans le -i), et a obtenu le même résultat:

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

J'utilise Bash 3.2.51 sur OS X et iTerm, bien que je ne vois pas comment ces éléments pourraient être terriblement pertinents. Je n'ai pas défini de variables d'environnement étranges, bien que je puisse poster celles que vous pensez être pertinentes.

Qu'est-ce qui pourrait mal?

[~ # ~] update [~ # ~]: J'ai dû faire une autre erreur ou faute de frappe quand j'ai essayé la version Ruby, car Gilles souligne que cela le fait fonctionne (et j'ai jamais lui a fait me diriger faux!). Je ne suis pas sûr de ce qui s'est passé, mais je suis presque sûr que ce doit être mon erreur.

46
iconoclast

La syntaxe \t pour un caractère de tabulation dans sed n'est pas standard. Cette fuite est un extension GNU sed . Vous trouverez de nombreux exemples en ligne qui l'utilisent parce que beaucoup de gens utilisent GNU sed (c'est l'implémentation sed sur Linux non embarqué). Mais OS X sed , comme les autres * BSD sed, ne prend pas en charge \t pour l'onglet et traite à la place \t comme une barre oblique inverse suivie de t.

Il existe de nombreuses solutions, telles que:

  • Utilisez un caractère de tabulation littéral.

    sed -i.bak 's/  /  /' file.txt
    
  • Utilisez tr ou printf pour produire un caractère de tabulation.

    sed -i.bak "s/$(printf '\t')/  /" file.txt
    sed -i.bak "s/$(echo a | tr 'a' '\t')/  /" file.txt
    
  • Utilisez bash's syntaxe de chaîne permettant les échappements antislash .

    sed -i.bak $'s/\t/  /' file.txt
    
  • Utilisez Perl, Python ou Ruby. L'extrait Ruby que vous avez publié fonctionne).

Utilisez un Bash spécifique entre guillemets qui vous permet d'utiliser des chaînes comme en C, de sorte qu'un vrai caractère de tabulation soit passé à sed, pas une séquence d'échappement:

sed -i.bak -E $'s/\t/  /' file.txt
14
Cristian Ciupitu
sed -i $'s/\t/  /g' file.txt 

fonctionne pour moi sur OS X et est la même commande que j'utilise sur linux tout le temps.

3
user193377

Comme indiqué, toutes les implémentations de sed ne prennent pas en charge la notation de \t en tant qu'onglet horizontal.

Vous pouvez facilement réaliser votre substitution avec:

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

Cela effectue un remplacement in situ qui préserve votre fichier d'origine en tant que "* .old". Perl permet des délimiteurs alternatifs pour le classique / rendant l'expression beaucoup plus lisible (c'est-à-dire dépourvue du syndrome du "cure-dent penchée").

Le + indique qu'une ou plusieurs répétitions d'un caractère de tabulation doivent être remplacées. Le modificateur g permet des remplacements globaux jusqu'à la fin de chaque ligne.

1
JRFerguson

S'il est correct d'exiger bash ou zsh en tant que shell, alors c'est la solution la plus simple à laquelle je peux penser:

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

Notez cependant que echo drapeaux (-n et -e) ne sont pas définis dans POSIX, donc un Shell conforme POSIX n'a ​​pas besoin de comprendre ces drapeaux, mais beaucoup le seront pour des raisons de compatibilité.

0
Mecki

Vous pouvez également utiliser echo dans sed:

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

0
saulR

Si vous voulez un sed plus puissant (supportant \t et plus) que celui sous OS X, installez GNU sed .

0
vinc17