Как напечатать строки, разделенные TAB в Bash?

9

Я пытаюсь напечатать две строки, разделенные вкладкой. Я пытался:

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

Они оба печатают:

foo     bar

Где пробел между ними на самом деле 5 пробелов (согласно выбору вывода с помощью мыши в Putty).

Я также пытался использовать CTRL + V и нажимать клавишу TAB при наборе команды, с тем же результатом.

Как правильно заставить вкладку печататься как вкладку, чтобы я мог выбрать вывод и скопировать его в другое место с вкладками?

И второстепенный вопрос: почему bash расширяет табуляции в пробелы?

Обновление : По-видимому, это проблема Putty: /superuser/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -пространствами

Асу
источник
5
Связанный:
Символ
Почему бы просто не избежать этого? printf '%s\\t%s\n' foo bar
Валентин Байрами
@steeldriver Спасибо, это очень похоже на то, что мне нужно, но в конечном итоге решения не существует ...
Асу
1
@Valentin Это выводит foo\tbar...
wjandrea
1
Несмотря на тот факт, что вы уже знаете, что у вас есть проблемы с вашим терминалом: Bash сам по себе интерпретируется $'\t'как табулятор. Таким образом, вы всегда можете объединить строки, подобные этой, например, как присваивание: v='This is'$'\t''a test'и напечатать это буквально, например,printf '%s' "$v"
rexkogitans

Ответы:

9

Как сказал ilkkachu, проблема связана не с bash, а с эмулятором терминала, который преобразует табуляцию в пробелы на выходе.

При проверке разных терминалов, putty, xterm и konsole конвертируют вкладки в пробелы, а urxvt и gnome-Terminal - нет. Итак, другое решение заключается в переключении терминалов.

Йол
источник
3
Это также может быть сделано драйвером tty после запуска stty tab3.
Стефан Шазелас
14

пробел между ними на самом деле 5 пробелов.

Нет, это не так. Не в выводе echoили printf.

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

Как правильно заставить вкладку печататься как вкладку, чтобы я мог выбрать вывод и скопировать его в другое место с вкладками?

Это другая проблема. Дело не в оболочке, а в эмуляторе терминала, который преобразует вкладки в пробелы на выходе. Многие, но не все, делают это.

Может быть проще перенаправить вывод с вкладками в файл и скопировать его оттуда, либо использовать unexpandв выводе для преобразования пробелов во вкладки. (Хотя он также не может знать, какие пробелы были с самого начала вкладками, и, если возможно, преобразует их все во вкладки.) Это, конечно, будет зависеть от того, что именно нужно делать с выводом.

ilkkachu
источник
Я имел в виду, что когда я пытаюсь выбрать выход, он рассматривается как 5 пробелов. Спасибо за 'od -c', чтобы проверить содержимое вывода команды.
Асу
1
@ Думаю, он это понимает. Его решение состоит в том, чтобы получить вывод с помощью других средств, поскольку эмулятор терминала не гарантирует, что вкладки будут оставаться вкладками, когда вы выбираете их в окне. Однако я только что проверил, и хотя putty, xterm и konsole конвертируют вкладки в пробелы, urxvt и gnome-терминал не делают. Итак, другое решение заключается в переключении терминалов.
JoL
@JoL Да, к этому выводу я только что пришел минуту назад, и я думаю, что это будет принятый ответ, если кто-то захочет опубликовать его как таковой ...
Асу
1
@ Асу, да, я подумал о том, чтобы обойти эту проблему вручную. Было бы неприятно делать это, но потом я признаю, что не понял, что существуют эмуляторы терминала, которые поддерживают копирование вкладок. Переход на тот, который делает, конечно, будет гораздо лучшим решением!
ilkkachu
4

В printf '%s\t%s\n' foo bar, printfделает вывод foo<TAB>bar<LF>.

f, o, b, aИ rявляются одной шириной графических символов.

После получения этих символов терминал отобразит соответствующий глиф и переместит курсор на один столбец вправо, если только он уже не достиг правого края экрана (бумага в оригинальных пишущих машинках)), и в этом случае он может подать строку и вернитесь к левому краю экрана (обтекание) или просто откажитесь от символа в зависимости от терминала и его настройки.

<Tab>и <LF>два управляющих символа. <LF>(aka newline) - это разделитель строк в тексте Unix, но для терминалов он просто подает строку (переместите курсор на одну позицию вниз). Таким образом, драйвер терминала в ядре фактически переведет его в <CR>(возврат к левому краю экрана), <LF>(курсор вниз) ( stty onlcrобычно по умолчанию включен).

<Tab> говорит терминалу переместить курсор к следующей позиции табуляции (которая на большинстве терминалов по умолчанию разделена на 8 позиций, но также может быть настроена для установки в любом месте) без заполнения пробела.

Поэтому, если эти символы отправляются в терминал с табуляцией через каждые 8 ​​столбцов, а курсор находится в начале пустой строки, это приведет к:

foo     bar

напечатан на экране в этой строке. Если они отправляются, когда курсор находится на третьей позиции в строке, которая содержит xxxxyyyyzzzz, это приведет к:

xxfooyyybarz

На терминалах, которые не поддерживают табулирование, драйвер терминала может быть сконфигурирован для преобразования этих вкладок в последовательности пробелов. ( stty tab3).

Символ SPC в оригинальных пишущих машинках будет перемещать курсор вправо, а backspace ( \b) - влево. Теперь в современных терминалах SPC перемещается вправо и также стирает (пишет символ пробела, как вы ожидаете). Так что подвеска \bдолжна была быть чем-то более новым, чем ASCII. На большинстве современных терминалов, на самом деле это последовательность символов: <Esc>, [, C.

Есть больше escape-последовательностей для перемещения nсимволов влево, вправо, вверх, вниз или в любую позицию на экране. Существуют и другие escape-последовательности для удаления (заполнения пустыми) частей линий или областей экрана и т. Д.

Эти последовательности , как правило , используется визуальными приложениями , таких как vi, lynx, mutt, dialogгде текст написан в произвольных позициях на экране.

Теперь все эмуляторы терминала X11 и несколько других эмуляторов, отличных от X11, таких как GNU, screenпозволяют выбирать области экрана для копирования и вставки. Когда вы выбираете часть того, что видите в viредакторе, вы не хотите копировать все escape-последовательности, которые использовались для создания этого вывода. Вы хотите выбрать текст, который вы видите там.

Например, если вы запустите:

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

Что имитирует редактор сеанса , когда вы входите abC, вернуться к началу, замените abс AC, Cс B, перейти на следующую позицию табуляции, а затем еще один столбец вправо, затем два столбца слева, а затем ввести D.

Вы видите:

ABC    D

То есть ABCпробел в 4 столбца и D.

Если вы выберите это с помощью мыши в xtermили putty, они будут хранить в выделении ABC, 4 пробела и D, не abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D.

То, что заканчивается в выборе, - это то, что было отправлено, printfно обработано как драйвером терминала, так и эмулятором терминала.

Для других видов трансформации см. <U+0065><U+0301>eпоследующим комбинированным острым акцентом), измененным на <U+00E9>( éпредварительно составленная форма) xterm.

Или echo abcэто заканчивается переводом ABCна драйвер терминала перед отправкой на терминал после a stty olcuc.

Теперь, <Tab>похоже <LF>, один из тех немногих управляющих символов, которые иногда встречаются в текстовых файлах (также <CR>в текстовых файлах MSDOS, а иногда и <FF>для разрыва страницы).

Поэтому некоторые эмуляторы терминалов предпочитают копировать их, когда это возможно, в буферах копирования и вставки, чтобы сохранить их (как правило, это не так и <CR>не <LF>так).

Например, в терминалах на основе VTE, таких как gnome-terminal, вы можете видеть, что, когда вы выбираете вывод в printf 'a\tb\n'пустой строке, gnome-terminalфактически сохраняет a\tbв выборе X11 вместо a7 пробелов и b.

Но для вывода printf 'a\t\bb\n', он хранит a, 6 мест и b, и для printf 'a\r\tb\n', a, 7 пространств и b.

Есть и другие случаи, когда терминалы будут пытаться скопировать фактический ввод, например, когда вы выберете две строки после запуска, printf 'a \nb\n'где это невидимое конечное пространство будет сохранено. Или при выборе двух строк не включает в себя символ НЧ, когда две строки являются результатом переноса с правого поля.

Теперь, если вы хотите сохранить выходные данные printfв CLIPBOARD X11select, лучше всего сделать это напрямую так:

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

Обратите внимание , что при вставке , что xtermи большинство других терминалов, xtermфактически заменяет , что \nс , \rпотому что это символ xtermпосылает при нажатии Enter(и драйвер терминала может перевести его обратно \n).

Стефан Шазелас
источник
Это очень проницательно, спасибо. Я пробовал решение xclip, и оно работает. Но это не совсем то, что я имел в виду, и требует X11. Может быть, это пригодится в какой-то момент, спасибо!
Асу
@Asu X11- это то , что обрабатывает выбор копирования-вставки в эмуляторах терминала, таких как xtermили puttyв Unix. Другие эмуляторы терминала может есть у них собственный механизм копирования и вставки и способы хранения произвольного содержания там, вроде readbuf и registerкоманды в окне GNU.
Стефан Шазелас