web-dev-qa-db-fra.com

Pourquoi sed ne reconnaît-il pas\t comme un onglet?

sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename

J'attends de ce script sed qu'il insère une tabulation dans la police de chaque ligne dans $filename, mais ce n'est pas le cas. Pour une raison quelconque, il insère un t à la place .. Étrange ..

80
sixtyfootersdude

Toutes les versions de sed ne comprennent pas \t. Insérez simplement un onglet littéral à la place (appuyez sur Ctrl-V puis Tab).

108
Mark Byers

En utilisant Bash, vous pouvez insérer un caractère de tabulation par programme de la manière suivante:

TAB=$'\t' 
echo 'line' | sed "s/.*/${TAB}&/g" 
echo 'line' | sed 's/.*/'"${TAB}"'&/g'   # use of Bash string concatenation
35
sedit

@sedit était sur le bon chemin, mais définir une variable est un peu délicat.

Solution (spécifique au bash)

La façon de faire cela en bash est d’utiliser un signe dollar devant votre chaîne citée.

$ echo -e '1\n2\n3'
1
2
3

$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3

$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
    1
    2
    3

Si votre chaîne doit inclure le développement de variable, vous pouvez regrouper les chaînes entre guillemets de la manière suivante:

$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958  1
1491237958  2
1491237958  3

Explication

Dans bash $'string' provoque "l'expansion ANSI-C". Et c'est ce à quoi la plupart d'entre nous s'attendons lorsque nous utilisons des éléments tels que \t, \r, \n, etc. De: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC -Quoting

Les mots de la forme $ 'chaîne' sont traités spécialement. La Parole se développe to string, avec les caractères d'échappement avec une barre oblique inversée remplacés comme spécifié par la norme ANSI C. Les séquences d'échappement avec une barre oblique inversée, si elles sont présentes, sont décodé ...

Le résultat étendu est indiqué entre guillemets simples, comme si le signe dollar n'avait pas été été présent.

Solution (si vous devez éviter bash)

Personnellement, je pense que la plupart des efforts pour éviter bash sont stupides, car éviter les bashismes ne rend * PAS votre code portable. (Votre code sera moins fragile si vous le réduisez en bash -eu plutôt que si vous essayez d'éviter bash et utilisez sh [à moins que vous ne soyez un ninja POSIX absolu].) Mais plutôt que d'avoir un argument religieux à ce sujet, je vous donnerai simplement la MEILLEURE * réponse.

$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
    1
    2
    3

* Meilleure réponse? Oui, car un exemple de ce que la plupart des scripteurs de Shell anti-bash feraient mal dans leur code est d'utiliser echo '\t' comme dans la réponse de @ robrecord . Cela fonctionnera pour GNU echo, mais pas pour BSD. Ceci est expliqué par The Open Group à http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 C'est un exemple de la raison pour laquelle les tentatives d'éviter les bashismes échouent généralement.

17
Bruno Bronosky

J'ai utilisé quelque chose comme ceci avec un shell Bash sur Ubuntu 12.04 (LTS):

Pour ajouter une nouvelle ligne avec tab, deuxième lorsque premier correspond:

sed -i '/first/a \\t second' filename

Pour remplacer premier avec onglet, deuxième :

sed -i 's/first/\\t second/g' filename
7
Thomas Bratt

Utilisez $(echo '\t'). Vous aurez besoin de citations autour du motif.

Par exemple. Pour supprimer un onglet:

sed "s/$(echo '\t')//"
3
robrecord

Vous n'avez pas besoin d'utiliser sed pour effectuer une substitution, vous voulez simplement insérer une tabulation devant la ligne. La substitution pour ce cas est une opération coûteuse par rapport à la simple impression, surtout lorsque vous travaillez avec de gros fichiers. C'est plus facile à lire aussi que ce n'est pas une regex.

par exemple en utilisant awk

awk '{print "\t"$0}' $filename > temp && mv temp $filename
2
ghostdog74

Je l'ai utilisé sur Mac: -

sed -i '' $'$i\\\n\\\thello\n' filename

Utilisé ce lien pour référence

0
Raj Hassani

Au lieu de BSD sed, j'utilise Perl:

ct@MBA45:~$ python -c "print('\t\t\thi')" |Perl -0777pe "s/\t/ /g"
   hi
0
Cees Timmerman

sed ne supporte pas \t, pas plus que d'autres séquences d'échappement comme \n. La seule façon que j'ai trouvée de le faire était d'insérer le caractère de tabulation dans le script en utilisant sed.

Cela dit, vous pouvez envisager d’utiliser Perl ou Python. Voici un court script Python que j'ai écrit et que j'utilise pour toutes les regex des flux:

#!/usr/bin/env python
import sys
import re

def main(args):
  if len(args) < 2:
    print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
    raise SystemExit

  p = re.compile(args[0], re.MULTILINE | re.DOTALL)
  s = sys.stdin.read()
  print p.sub(args[1], s),

if __== '__main__':
  main(sys.argv[1:])
0
Roman Nurik

Je pense que d'autres ont clarifié cela de manière adéquate pour d'autres approches (sed, AWK, etc.). Cependant, mes réponses bash- spécifiques (testées sur macOS High Sierra et CentOS 6/7) suivent. 

1) Si OP souhaitait utiliser une méthode de recherche-remplacement similaire à celle proposée à l'origine, je suggérerais d'utiliser Perl pour cela, comme suit. Notes: les barres obliques inverses avant les parenthèses pour les expressions rationnelles ne devraient pas être nécessaires, et cette ligne de code indique comment il vaut mieux utiliser $1 que \1 avec l'opérateur de substitution Perl (par exemple, per Perl 5 documentation ).

Perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename

2) Cependant, comme indiqué par ghostdog74 , puisque l’opération désirée consiste en fait à simplement ajouter un onglet au début de chaque ligne avant de remplacer le fichier tmp par le fichier en entrée/cible ($filename), Je recommanderais à nouveau Perl mais avec la ou les modification (s) suivante (s):

Perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
Perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename

3) Bien sûr, le fichier tmp est superflu, il est donc préférable de tout faire «à la place» (en ajoutant le drapeau -i) et de simplifier les choses pour en faire un one-liner plus élégant avec

Perl -i -pe $'s/^/\t/' $filename
0
justincbagley