Comment imprimer des chaînes séparées par TAB en bash?

9

J'essaie d'imprimer deux chaînes séparées par un TAB. J'ai essayé:

echo -e 'foo\tbar'
printf '%s\t%s\n' foo bar

Tous deux impriment:

foo     bar

Où l'espace entre les deux est en fait de 5 espaces (selon la sélection de la sortie avec la souris dans Putty).

J'ai également essayé d'utiliser CTRL + V et d'appuyer sur TAB lors de la frappe de la commande, avec le même résultat.

Quelle est la bonne façon de forcer l’impression de l’onglet en tant qu’onglet, afin de pouvoir sélectionner la sortie et la copier ailleurs, avec des onglets?

Et la question secondaire: pourquoi bash étend-il les onglets dans les espaces?

Mise à jour : Apparemment, c'est un problème de Putty: /superuser/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -les espaces

Asu
la source
Pourquoi ne pas y échapper? printf '%s\\t%s\n' foo bar
Valentin Bajrami
@steeldriver Merci, c'est très similaire à ce dont j'ai besoin, mais finalement il n'y a pas de solution ...
Asu
1
@Valentin That outputs foo\tbar...
wjandrea
1
Malgré ce fait, vous savez déjà que vous avez un problème avec votre terminal: Bash en lui-même interprète $'\t'comme tabulateur. Ainsi, vous pouvez toujours concaténer des chaînes comme celle-ci - par exemple comme affectation: v='This is'$'\t''a test'Et l'imprimer littéralement, par exempleprintf '%s' "$v"
rexkogitans

Réponses:

9

Comme ilkkachu l'a dit, ce n'est pas un problème avec bash, mais avec l'émulateur de terminal qui convertit les tabulations en espaces en sortie.

La vérification de différents terminaux, putty, xterm et konsole convertit les onglets en espaces, contrairement à urxvt et gnome-terminal. Une autre solution consiste donc à changer de terminal.

JoL
la source
3
Cela peut également être fait par le pilote tty après avoir exécuté stty tab3.
Stéphane Chazelas
14

l'espace entre les deux est en fait de 5 espaces.

Non ce n'est pas. Pas dans la sortie de echoou printf.

$ echo -e 'foo\tbar' | od -c
0000000   f   o   o  \t   b   a   r  \n
0000010

Quelle est la bonne façon de forcer l’impression de l’onglet en tant qu’onglet, afin de pouvoir sélectionner la sortie et la copier ailleurs, avec des onglets?

C'est un problème différent. Il ne s'agit pas du shell mais de l'émulateur de terminal, qui convertit les tabulations en espaces en sortie. Beaucoup, mais pas tous.

Il peut être plus facile de rediriger la sortie avec des tabulations vers un fichier et de la copier à partir de là, ou d'utiliser unexpandsur la sortie pour convertir des espaces en tabulations. (Bien qu'il ne puisse pas non plus savoir quels espaces étaient des onglets pour commencer, et les convertira tous en onglets, si possible.) Cela dépendra bien sûr de ce que vous devez faire exactement avec la sortie.

ilkkachu
la source
Je voulais dire que lorsque j'essaie de sélectionner la sortie, elle est traitée comme 5 espaces. Merci pour le 'od -c' de vérifier le contenu de la sortie de la commande.
Asu
1
@Asu je pense qu'il comprend cela. Sa solution consiste à obtenir la sortie par d'autres moyens car l'émulateur de terminal n'est pas garanti de laisser les onglets comme des onglets lorsque vous les sélectionnez dans la fenêtre. Cependant, je viens de vérifier et tandis que putty, xterm et konsole convertissent les onglets en espaces, urxvt et gnome-terminal ne le font pas. Une autre solution consiste donc à changer de terminal.
JoL
@JoL Oui, c'est la conclusion à laquelle je viens d'arriver il y a une minute, et je pense que ce serait la réponse acceptée si quelqu'un veut l'afficher en tant que telle ...
Asu
1
@Asu, oui, j'ai pensé à contourner le problème manuellement. Ce serait ennuyeux de devoir le faire, mais j'avoue que je n'avais pas réalisé qu'il existe des émulateurs de terminaux qui prennent en charge la copie des onglets. Changer pour celui qui le fait, serait bien sûr une bien meilleure solution!
ilkkachu
4

Dans printf '%s\t%s\n' foo bar, printffait la sortie foo<TAB>bar<LF>.

f, o, b, aEt rsont des caractères graphiques à simple largeur.

À la réception de ces caractères, le terminal affichera un glyphe correspondant et déplacera le curseur d'une colonne vers la droite, à moins qu'il n'ait déjà atteint le bord droit de l'écran (papier dans les télé-machines à écrire d'origine)), auquel cas il peut alimenter une ligne et revenir au bord gauche de l'écran (envelopper) ou simplement jeter le caractère en fonction du terminal et de la façon dont il a été configuré.

<Tab>et <LF>sont deux caractères de contrôle . <LF>(aka newline) est le délimiteur de ligne dans le texte Unix, mais pour les terminaux, il alimente simplement une ligne (déplacez le curseur d'une position vers le bas). Ainsi, le pilote de terminal dans le noyau le traduira en fait <CR>(retour au bord gauche de l'écran), <LF>(curseur vers le bas) ( stty onlcrgénéralement activé par défaut).

<Tab> indique au terminal de déplacer le curseur vers le taquet de tabulation suivant (qui sur la plupart des terminaux sont distants de 8 positions par défaut mais peut également être configuré pour être défini n'importe où) sans remplir l'espace avec des blancs.

Donc, si ces caractères sont envoyés à un terminal avec des tabulations toutes les 8 colonnes alors que le curseur est au début d'une ligne vide, cela se traduira par:

foo     bar

imprimé sur l'écran à cette ligne. S'ils sont envoyés alors que le curseur est en troisième position dans une ligne qui contient xxxxyyyyzzzz, cela se traduira par:

xxfooyyybarz

Sur les terminaux qui ne prennent pas en charge la tabulation, le pilote de terminal peut être configuré pour traduire ces onglets en séquences d'espaces. ( stty tab3).

Le caractère SPC, dans les télé-machines à écrire d'origine, déplacerait le curseur vers la droite, tandis que backspace ( \b) le déplacerait vers la gauche. Désormais, dans les terminaux modernes, SPC se déplace vers la droite et efface également (écrit un caractère d'espace comme vous vous en doutez). Le pendentif \bdevait donc être quelque chose de plus récent que ASCII. Sur la plupart des terminaux, il est en fait une séquence de caractères: <Esc>, [, C.

Il existe plus de séquences d'échappement pour déplacer les npersonnages vers la gauche, la droite, le haut, le bas ou à n'importe quelle position sur l'écran. Il existe d'autres séquences d'échappement pour effacer (remplir avec des blancs) des parties de lignes ou régions de l'écran, etc.

Ces séquences sont généralement utilisés par des applications visuelles comme vi, lynx, mutt, dialogoù le texte est écrit à des positions arbitraires sur l'écran.

Maintenant, tous les émulateurs de terminaux X11 et quelques autres non-X11 comme GNU screenvous permettent de sélectionner des zones de l'écran pour copier-coller. Lorsque vous sélectionnez une partie de ce que vous voyez dans l' viéditeur, vous ne voulez pas copier toutes les séquences d'échappement qui ont été utilisées pour produire cette sortie. Vous souhaitez sélectionner le texte que vous y voyez.

Par exemple, si vous exécutez:

printf 'abC\rAC\bB\t\e[C\b\bD\n'

Ce qui simule une session d'éditeur où vous entrez abC, revenez au début, remplacez abpar AC, Cavec B, passez à la tabulation suivante, puis une autre colonne à droite, puis deux colonnes à gauche, puis entrez D.

Tu vois:

ABC    D

Autrement dit, ABCun écart de 4 colonnes et D.

Si vous sélectionnez cela avec la souris dans xtermou putty, ils stockeront dans la sélection ABC, 4 caractères d'espacement et Dnon abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D.

Ce qui finit dans la sélection est ce qui a été envoyé par le printfpost-traitement et l'émulateur de terminal, mais post-traité.

Pour d'autres types de transformation, voir le <U+0065><U+0301>( esuivi d'un accent aigu combiné) changé en <U+00E9>( éla forme pré-composée) par xterm.

Ou echo abcqui finit par être traduit ABCpar le pilote du terminal avant d'être envoyé au terminal après a stty olcuc.

Maintenant, <Tab>comme <LF>est l'un de ces quelques caractères de contrôle qui se trouvent parfois dans les fichiers texte (également <CR>dans les fichiers texte MSDOS et parfois <FF>pour les sauts de page).

Certains émulateurs de terminaux choisissent donc de les copier lorsque cela est possible dans les tampons de copier-coller pour les conserver (ce n'est généralement pas le cas, <CR>ni <LF>si).

Par exemple, dans les terminaux basés sur VTE comme gnome-terminal, vous pouvez voir que, lorsque vous sélectionnez la sortie de printf 'a\tb\n'sur une ligne vide, gnome-terminalstocke a\tben fait dans la sélection X11 au lieu de a, 7 espaces et b.

Mais pour la sortie printf 'a\t\bb\n', il stocke a, 6 places et b, et printf 'a\r\tb\n', a, 7 places et b.

Il y a d'autres cas où les terminaux essaieront de copier l'entrée réelle, comme lorsque vous sélectionnez deux lignes après l'exécution printf 'a \nb\n'où cet espace de fin invisible sera préservé. Ou lorsque la sélection de deux lignes n'inclut pas de caractère LF lorsque les deux lignes résultent d'un habillage à la marge de droite.

Maintenant, si vous voulez stocker la sortie de printfdans le CLIPBOARD X11select, le mieux est de le faire directement comme avec:

printf 'foo\tbar\n' | xclip -sel c

Notez que lorsque vous collez cela dans xtermou la plupart des autres terminaux, xtermremplace réellement cela \npar \rcar c'est le caractère xtermenvoyé lorsque vous appuyez sur Enter(et le pilote de terminal peut le traduire à nouveau \n).

Stéphane Chazelas
la source
C'est très perspicace, merci. J'ai essayé la solution xclip et ça marche. Mais il ne fait pas exactement ce que j'avais en tête et nécessite X11. Peut-être que cela vous sera utile à un moment donné, merci!
Asu
@Asu, X11c'est ce qui gère la sélection copier-coller dans les émulateurs de terminaux comme xtermou puttysur Unix. D'autres émulateurs de terminaux peuvent avoir leur propre mécanisme de copier-coller et des moyens de stocker du contenu arbitraire, comme les commandes readbuf et registerdans l'écran GNU.
Stéphane Chazelas