Como imprimir seqüências de caracteres separadas por TAB no bash?

9

Estou tentando imprimir duas seqüências de caracteres separadas por um TAB. Eu tentei:

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

Ambos imprimem:

foo     bar

Onde o espaço em branco entre os dois é na verdade 5 espaços (conforme a seleção da saída com o mouse em Putty).

Também tentei usar CTRL + V e pressionar TAB ao digitar o comando, com o mesmo resultado.

Qual é a maneira correta de forçar a impressão de uma guia como guia, para que eu possa selecionar a saída e copiá-la para outro lugar, com guias?

E a questão secundária: por que o bash está expandindo as guias em espaços?

Atualização : Aparentemente, este é um problema do Putty: /superuser/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -spaces

Asu
fonte
Por que simplesmente não escapar disso? printf '%s\\t%s\n' foo bar
Valentin Bajrami
@steeldriver Graças que é muito parecido com o que eu preciso, mas em última análise, não há uma solução ...
Asu
1
@Valentin que gera foo\tbar...
wjandrea
1
Apesar do fato de você já saber que tem um problema com o seu terminal: o Bash, por si só, interpreta $'\t'como tabulador. Assim, você sempre pode concatenar seqüências de caracteres como esta - por exemplo, como atribuição: v='This is'$'\t''a test'e imprimi-la literalmente, por exemploprintf '%s' "$v"
rexkogitans

Respostas:

9

Como o ilkkachu disse, esse não é um problema do bash, mas do emulador de terminal que converte abas em espaços na saída.

A verificação de diferentes terminais, putty, xterm e konsole converte guias em espaços, enquanto urxvt e gnome-terminal não. Portanto, outra solução é trocar de terminal.

JoL
fonte
3
Isso também pode ser feito pelo driver tty após a execução stty tab3.
Stéphane Chazelas
14

o espaço em branco entre os dois é na verdade 5 espaços.

Não, não é. Não está na saída de echoou printf.

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

Qual é a maneira correta de forçar a impressão de uma guia como guia, para que eu possa selecionar a saída e copiá-la para outro lugar, com guias?

Esta é uma questão diferente. Não se trata do shell, mas do emulador de terminal, que converte as guias em espaços na saída. Muitos, mas nem todos fazem isso.

Pode ser mais fácil redirecionar a saída com guias para um arquivo e copiá-la a partir daí, ou usar unexpanda saída para converter espaços em guias. (Embora também não possa saber em que espaço em branco eram guias, e converter tudo em guias, se possível.) Isso obviamente dependeria do que exatamente você precisa fazer com a saída.

ilkkachu
fonte
Eu quis dizer que quando tento selecionar a saída, ela está sendo tratada como 5 espaços. Obrigado pelo 'od -c' para verificar o conteúdo da saída do comando.
Asu
1
@Asu Acho que ele entende isso. Sua solução é obter a saída por outros meios, já que o emulador de terminal não garante abas como abas quando você as seleciona na janela. No entanto, acabei de verificar e, enquanto o putty, o xterm e o konsole convertem abas em espaços, o urxvt e o gnome-terminal não. Portanto, outra solução é trocar de terminal.
JOL
@JoL Sim, essa é a conclusão que eu só vim aqui para um minuto atrás, e eu acho que seria a resposta aceite se os cuidados de alguém para publicá-la como tal ...
Asu
1
@Asu, sim, pensei em apenas contornar o problema manualmente. Seria irritante ter que fazer isso, mas admito que não havia percebido que existem emuladores de terminal que suportam a cópia de guias. Mudar para um que sim, seria obviamente uma solução muito melhor!
Ilkkachu
4

Em printf '%s\t%s\n' foo bar, printfproduz foo<TAB>bar<LF>.

f, o, b, aE rsão caracteres gráficos individuais de largura.

Ao receber esses caracteres, o terminal exibirá um glifo correspondente e moverá o cursor uma coluna para a direita, a menos que já tenha atingido a borda direita da tela (papel nas tele-máquinas de escrever originais), caso em que poderá alimentar uma linha e retorne à borda esquerda da tela (quebra automática) ou apenas descarte o caractere, dependendo do terminal e de como ele foi configurado.

<Tab>e <LF>são dois caracteres de controle . <LF>(aka newline) é o delimitador de linha no texto Unix, mas para terminais, apenas alimenta uma linha (mova o cursor uma posição para baixo). Portanto, o driver do terminal no kernel irá convertê-lo para <CR>(retornar à borda esquerda da tela), <LF>(cursor para baixo) ( stty onlcrgeralmente ativado por padrão).

<Tab> diz ao terminal para mover o cursor até a próxima parada de tabulação (que na maioria dos terminais tem 8 posições à parte, por padrão, mas também pode ser configurada para ser configurada em qualquer lugar) sem preencher a lacuna com espaços em branco.

Portanto, se esses caracteres forem enviados para um terminal com tabulação a cada 8 colunas enquanto o cursor estiver no início de uma linha vazia, isso resultará em:

foo     bar

impresso na tela nessa linha. Se eles forem enviados enquanto o cursor estiver na terceira posição em uma linha que contenha xxxxyyyyzzzz, isso resultará em:

xxfooyyybarz

Em terminais que não oferecem suporte à tabulação, o driver do terminal pode ser configurado para converter essas guias em seqüências de espaços. ( stty tab3)

O caractere SPC, nas tele-máquinas de escrever originais, moveria o cursor para a direita, enquanto backspace ( \b) o moveria para a esquerda. Agora, nos terminais modernos, o SPC se move para a direita e também apaga (escreve um caractere de espaço conforme o esperado). Então o pingente de \btinha que ser algo mais novo que o ASCII. Na maioria dos terminais modernos, é, na verdade, uma sequência de caracteres: <Esc>, [, C.

Existem mais seqüências de escape para mover os ncaracteres para a esquerda, direita, cima, baixo ou em qualquer posição da tela. Existem outras seqüências de escape para apagar (preencher em branco) partes de linhas ou regiões da tela, etc.

Essas seqüências são geralmente usados por aplicativos visuais como vi, lynx, mutt, dialogem que o texto é escrito em posições arbitrárias na tela.

Agora, todos os emuladores de terminal X11 e alguns outros não-X11 como o GNU screenpermitem selecionar áreas da tela para copiar e colar. Quando você seleciona uma parte do que vê no vieditor, não deseja copiar todas as sequências de escape usadas para produzir essa saída. Você deseja selecionar o texto que vê lá.

Por exemplo, se você executar:

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

Que simula uma sessão editor onde você entra abC, volte para o início, substitua abcom AC, Ccom B, mover para a próxima parada de tabulação, em seguida, mais uma coluna à direita, em seguida, duas colunas à esquerda, em seguida, digite D.

Entende:

ABC    D

Ou seja, ABCuma lacuna de 4 colunas e D.

Se você selecionar com o mouse em xtermou putty, eles serão armazenados na seleção ABC, 4 caracteres de espaço e D, não abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D.

O que acaba na seleção é o que foi enviado, printfmas pós-processado pelo driver do terminal e pelo emulador de terminal.

Para outros tipos de transformação, consulte o <U+0065><U+0301>( eseguido de um acento agudo combinado) alterado para <U+00E9>( éa forma pré-composta) por xterm.

Ou echo abcisso acaba sendo traduzido ABCpelo driver do terminal antes de ser enviado ao terminal após a stty olcuc.

Agora, <Tab>like <LF>é um daqueles poucos caracteres de controle que, às vezes, são encontrados em arquivos de texto (também <CR>nos arquivos de texto do MSDOS, e às vezes <FF>para quebra de página).

Portanto, alguns emuladores de terminal optam por copiá-los quando possível nos buffers de copiar e colar para preservá-los (esse geralmente não é o caso <CR>nem o caso <LF>).

Por exemplo, em terminais baseados em VTE como gnome-terminal, você pode ver que, quando você seleciona a saída printf 'a\tb\n'em uma linha vazia, gnome-terminalna verdade armazena a\tbna seleção X11 em vez de a7 espaços e b.

Mas para a saída printf 'a\t\bb\n', ele armazena a, 6 espaços e b, e por printf 'a\r\tb\n', a, 7 espaços e b.

Há outros casos em que os terminais tentam copiar a entrada real, como quando você seleciona duas linhas após a execução, printf 'a \nb\n'onde esse espaço invisível à direita será preservado. Ou, ao selecionar duas linhas, não inclui um caracter LF quando as duas linhas resultam da quebra automática na margem direita.

Agora, se você deseja armazenar a saída printfno CLIPBOARD X11, é melhor fazê-lo diretamente, como:

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

Observe que quando você cola isso no xtermou na maioria dos outros terminais, xtermna verdade o substitui \npor \rporque esse é o caractere xtermenviado quando você pressiona Enter(e o driver do terminal pode convertê-lo novamente para \n).

Stéphane Chazelas
fonte
Isso é muito perspicaz, obrigado. Eu tentei a solução xclip e funciona. Mas isso não faz exatamente o que eu tinha em mente e requer o X11. Pode ser que isso seja útil em algum momento, obrigado!
quer
@Asu, X11é o que lida com a seleção copiar e colar em emuladores de terminal como xtermou puttyno Unix. Outros emuladores de terminal podem ter seu próprio mecanismo de copiar e colar e maneiras de armazenar conteúdo arbitrário, como os comandos readbuf e registerna tela do GNU.
Stéphane Chazelas