Pourquoi sed ne reconnaît-il pas \ t comme un onglet?
105
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Je m'attends à ce que ce sedscript insère un tabdevant chaque ligne, $filenamemais ce n'est pas le cas. Pour une raison quelconque, il insère un tfichier.
Comme sed peut varier entre les plates-formes (en particulier, BSD / MacOSX par rapport à Linux), il peut être utile de spécifier la plate-forme sur laquelle vous utilisez sed.
Ah oui; pour clarifier: toutes les versions de sed ne comprennent pas \tdans la partie de remplacement de l'expression (il est reconnu \tdans la partie de correspondance de motif très bien)
John Weldon
3
awwwwwwwwwwwwwwwwwww, ok c'est assez intéressant. Et étrange. Pourquoi voudriez-vous le faire reconnaître à un endroit mais pas à l'autre ...?
sixtyfootersdude
2
Appelé à partir d'un script, cela ne fonctionnera pas: les onglets seraient ignorés par sh. Par exemple, le code suivant d'un script shell ajoutera $ TEXT_TO_ADD, sans le faire précéder d'une tabulation: sed "$ {LINE} a \\ $ TEXT_TO_ADD" $ FILE.
Vous étiez sur la bonne voie avec l' $'string'explication mais manquez. En fait, je soupçonne, en raison de l'utilisation extrêmement maladroite que vous avez probablement une compréhension incomplète (comme la plupart d'entre nous le font avec bash). Voir mon explication ci-dessous: stackoverflow.com/a/43190120/117471
Bruno Bronosky
1
N'oubliez pas que BASH ne développera pas les variables comme $TABles guillemets simples, vous devrez donc les utiliser entre guillemets doubles.
nealmcb
Attention à utiliser *des guillemets doubles à l'intérieur ... cela sera traité comme un glob, pas comme l'expression régulière que vous souhaitez.
levigroker
28
@sedit était sur le bon chemin, mais il est un peu difficile de définir une variable.
Solution (spécifique à bash)
La façon de faire cela dans bash est de mettre un signe dollar devant votre chaîne entre guillemets simples.
$ echo -e '1\n2\n3'123
$ echo -e '1\n2\n3'| sed 's/.*/\t&/g'
t1
t2
t3
$ echo -e '1\n2\n3'| sed $'s/.*/\t&/g'123
Si votre chaîne doit inclure une extension de variable, vous pouvez mettre les chaînes entre guillemets comme ceci:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3'| sed "s/.*/$timestamp"$'\t&/g'149123795811491237958214912379583
Les mots de la forme $ 'string' sont traités spécialement. Le mot se développe en chaîne , avec les caractères d'échappement par barre oblique inverse remplacés comme spécifié par la norme ANSI C. Les séquences d'échappement de backslash, si présentes, sont décodées ...
Le résultat développé est entre guillemets simples, comme si le signe dollar n’était pas présent.
Solution (si vous devez éviter le bash)
Personnellement, je pense que la plupart des efforts pour éviter les bash sont ridicules, car éviter les bashismes ne rend PAS votre code portable. (Votre code sera moins fragile si vous le limitez bash -euque si vous essayez d'éviter de bash et d'utiliser sh[sauf si vous êtes un ninja POSIX absolu].) Mais plutôt que d'avoir un argument religieux à ce sujet, je vais juste vous donner le MEILLEUR * répondre.
$ echo -e '1\n2\n3'| sed "s/.*/$(printf '\t')&/g"123
* Meilleure réponse? Oui, car un exemple de ce que la plupart des scripts shell anti-bash feraient de mal dans leur code est l'utilisation echo '\t'comme dans la réponse de @ robrecord . Cela fonctionnera pour l'écho GNU, mais pas pour l'écho BSD. Cela est expliqué par The Open Group à http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 Et ceci est un exemple de pourquoi essayer d'éviter les bashismes échoue généralement.
C'est drôle que vous utilisiez une fonctionnalité spécifique "GNU echo" (interprétant \ t comme un caractère de tabulation) pour résoudre un bogue spécifique "BSD sed" (interprétant \ t comme 2 caractères séparés). Vraisemblablement, si vous avez "GNU echo", vous auriez aussi "GNU sed". Dans ce cas, vous n'aurez pas besoin d'utiliser echo. Avec BSD, l'écho echo '\t'va produire 2 caractères séparés. Le moyen portable POSIX est d'utiliser printf '\t'. C'est pourquoi je dis: n'essayez pas de rendre votre code portable en n'utilisant pas bash. C'est plus dur que tu ne le penses. L'utilisation bashest la chose la plus portable que la plupart d'entre nous puissent faire.
Bruno Bronosky
3
Vous n'avez pas besoin d'utiliser sedpour faire une substitution alors qu'en fait, 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 car ce n'est pas une expression régulière.
sedne prend pas en charge \t, ni d'autres séquences d'échappement comme \nd'ailleurs. Le seul moyen que j'ai trouvé pour le faire était d'insérer le caractère de tabulation dans le script en utilisantsed .
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 expressions rationnelles de flux:
Je pense que d' autres ont permis de clarifier ce suffisamment pour d' autres approches ( sed, AWK, etc.). Cependant, mes bashréponses spécifiques (testées sur macOS High Sierra et CentOS 6/7) suivent.
1) Si OP voulait utiliser une méthode de recherche et de remplacement similaire à ce qu'il avait proposé à l'origine, je suggérerais d'utiliser perlpour cela, comme suit. Notes: les barres obliques inverses avant les parenthèses pour les regex ne devraient pas être nécessaires, et cette ligne de code reflète comment il $1est préférable de l'utiliser \1qu'avec perlun opérateur de substitution (par exemple, selon la documentation Perl 5 ).
2) Cependant, comme l'a souligné ghostdog74 , puisque l'opération souhaitée est en fait d' ajouter simplement un onglet au début de chaque ligne avant de changer le fichier tmp en fichier d'entrée / cible ( $filename), je recommanderais à perlnouveau mais avec la modification suivante (s):
3) Bien sûr, le fichier tmp est superflu , il est donc préférable de tout faire `` en place '' (ajouter un -idrapeau) et de simplifier les choses en un one-liner plus élégant avec
Réponses:
Toutes les versions de
sed
comprennent\t
. Insérez simplement une tabulation littérale à la place (appuyez sur Ctrl- Vpuis Tab).la source
\t
dans la partie de remplacement de l'expression (il est reconnu\t
dans la partie de correspondance de motif très bien)En utilisant Bash, vous pouvez insérer un caractère TAB par programme comme ceci:
la source
$'string'
explication mais manquez. En fait, je soupçonne, en raison de l'utilisation extrêmement maladroite que vous avez probablement une compréhension incomplète (comme la plupart d'entre nous le font avec bash). Voir mon explication ci-dessous: stackoverflow.com/a/43190120/117471$TAB
les guillemets simples, vous devrez donc les utiliser entre guillemets doubles.*
des guillemets doubles à l'intérieur ... cela sera traité comme un glob, pas comme l'expression régulière que vous souhaitez.@sedit était sur le bon chemin, mais il est un peu difficile de définir une variable.
Solution (spécifique à bash)
La façon de faire cela dans bash est de mettre un signe dollar devant votre chaîne entre guillemets simples.
Si votre chaîne doit inclure une extension de variable, vous pouvez mettre les chaînes entre guillemets comme ceci:
Explication
Dans bash
$'string'
provoque "l'expansion ANSI-C". Et c'est - ce que la plupart d' entre nous attendent lorsque nous utilisons des choses comme\t
,\r
,\n
, etc. De: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-QuotingSolution (si vous devez éviter le bash)
Personnellement, je pense que la plupart des efforts pour éviter les bash sont ridicules, car éviter les bashismes ne rend PAS votre code portable. (Votre code sera moins fragile si vous le limitez
bash -eu
que si vous essayez d'éviter de bash et d'utilisersh
[sauf si vous êtes un ninja POSIX absolu].) Mais plutôt que d'avoir un argument religieux à ce sujet, je vais juste vous donner le MEILLEUR * répondre.* Meilleure réponse? Oui, car un exemple de ce que la plupart des scripts shell anti-bash feraient de mal dans leur code est l'utilisation
echo '\t'
comme dans la réponse de @ robrecord . Cela fonctionnera pour l'écho GNU, mais pas pour l'écho BSD. Cela est expliqué par The Open Group à http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 Et ceci est un exemple de pourquoi essayer d'éviter les bashismes échoue généralement.la source
J'ai utilisé quelque chose comme ça avec un shell Bash sur Ubuntu 12.04 (LTS):
Pour ajouter une nouvelle ligne avec tabulation, en second lieu lorsque la première correspond:
Pour remplacer d' abord par tabulation, ensuite :
la source
\\t
et non\t
.Utilisez
$(echo '\t')
. Vous aurez besoin de citations autour du motif.Par exemple. Pour supprimer un onglet:
la source
echo '\t'
va produire 2 caractères séparés. Le moyen portable POSIX est d'utiliserprintf '\t'
. C'est pourquoi je dis: n'essayez pas de rendre votre code portable en n'utilisant pas bash. C'est plus dur que tu ne le penses. L'utilisationbash
est la chose la plus portable que la plupart d'entre nous puissent faire.Vous n'avez pas besoin d'utiliser
sed
pour faire une substitution alors qu'en fait, 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 car ce n'est pas une expression régulière.par exemple en utilisant awk
la source
J'ai utilisé ceci sur Mac:
Utilisé ce lien pour référence
la source
sed
ne prend pas en charge\t
, ni d'autres séquences d'échappement comme\n
d'ailleurs. Le seul moyen que j'ai trouvé pour le faire était d'insérer le caractère de tabulation dans le script en utilisantsed
.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 expressions rationnelles de flux:
la source
Au lieu de BSD sed, j'utilise perl:
la source
Je pense que d' autres ont permis de clarifier ce suffisamment pour d' autres approches (
sed
,AWK
, etc.). Cependant, mesbash
réponses spécifiques (testées sur macOS High Sierra et CentOS 6/7) suivent.1) Si OP voulait utiliser une méthode de recherche et de remplacement similaire à ce qu'il avait proposé à l'origine, je suggérerais d'utiliser
perl
pour cela, comme suit. Notes: les barres obliques inverses avant les parenthèses pour les regex ne devraient pas être nécessaires, et cette ligne de code reflète comment il$1
est préférable de l'utiliser\1
qu'avecperl
un opérateur de substitution (par exemple, selon la documentation Perl 5 ).2) Cependant, comme l'a souligné ghostdog74 , puisque l'opération souhaitée est en fait d' ajouter simplement un onglet au début de chaque ligne avant de changer le fichier tmp en fichier d'entrée / cible (
$filename
), je recommanderais àperl
nouveau mais avec la modification suivante (s):3) Bien sûr, le fichier tmp est superflu , il est donc préférable de tout faire `` en place '' (ajouter un
-i
drapeau) et de simplifier les choses en un one-liner plus élégant avecla source