Символ TAB
является управляющим символом, который при отправке на терминал¹ заставляет курсор терминала перемещаться к следующей позиции табуляции. По умолчанию в большинстве терминалов позиции табуляции расположены на расстоянии 8 столбцов, но это настраивается.
Вы также можете использовать табуляцию с нерегулярными интервалами:
$ tabs 3 9 11; printf '\tx\ty\tz\n'
x y z
Только терминал знает, на сколько столбцов справа TAB будет перемещать курсор.
Вы можете получить эту информацию, запросив позицию курсора из терминала до и после отправки вкладки.
Если вы хотите выполнить этот расчет вручную для данной строки и предполагать, что эта строка напечатана в первом столбце экрана, вам необходимо:
- знать, где находятся табуляции?
- знать ширину отображения каждого символа
- знать ширину экрана
- решить, хотите ли вы обрабатывать другие управляющие символы, такие как
\r
(который перемещает курсор в первый столбец) или \b
который перемещает курсор назад ...)
Это может быть упрощено, если вы предполагаете, что табуляция занимает каждые 8 столбцов, строка умещается на экране и нет других управляющих символов или символов (или не символов), которые ваш терминал не может отображать должным образом.
С GNU wc
, если строка хранится в $line
:
width=$(printf %s "$line" | wc -L)
width_without_tabs=$(printf %s "$line" | tr -d '\t' | wc -L)
width_of_tabs=$((width - width_without_tabs))
wc -L
дает ширину самой широкой линии на входе. Это делается с помощью wcwidth(3)
определения ширины символов и с учетом того, что табуляция занимает каждые 8 столбцов.
Для систем не-GNU и с теми же предположениями см. Подход @ Kusalananda . Это даже лучше, так как позволяет задавать позиции табуляции, но, к сожалению, в настоящее время не работает с GNU expand
(по крайней мере), когда ввод содержит многобайтовые символы или символы 0 ширины (например, символы объединения) или символы двойной ширины.
¹ обратите внимание, что, если вы это сделаете stty tab3
, дисциплина строки устройства tty возьмет на себя обработку вкладок (преобразует TAB в пробелы на основе своего собственного представления о том, где может находиться курсор перед отправкой в терминал), и реализация табуляции останавливается через каждые 8 столбцов. При тестировании в Linux, он, кажется, правильно обрабатывает символы CR, LF и BS, а также многобайтовые символы UTF-8 ( iutf8
также включены), но это все. Предполагается, что все другие неконтролирующие символы (включая символы нулевой ширины и двойной ширины) имеют ширину 1, он (очевидно) не обрабатывает escape-последовательности, не переносится должным образом ... Это, вероятно, предназначено для терминалов, которые не могу сделать обработку вкладок.
В любом случае дисциплина линий tty должна знать, где находится курсор, и использует те эвристики, описанные выше, потому что при использовании icanon
редактора строк (например, когда вы вводите текст для таких приложений, cat
которые не реализуют собственный редактор строк), когда вы нажмите TabBackspace, строка дисциплины должна знать, сколько символов BS нужно отправить, чтобы стереть этот символ табуляции для отображения. Если вы измените расположение вкладок (например, с помощью tabs 12
), вы заметите, что вкладки не стираются должным образом. То же самое, если вы вводите символы двойной ширины перед нажатием TabBackspace.
² Для этого вы можете отправлять символы табуляции и запрашивать позицию курсора после каждого. Что-то типа:
tabs=$(
saved_settings=$(stty -g)
stty -icanon min 1 time 0 -echo
gawk -vRS=R -F';' -vORS= < /dev/tty '
function out(s) {print s > "/dev/tty"; fflush("/dev/tty")}
BEGIN{out("\r\t\33[6n")}
$NF <= prev {out("\r"); exit}
{print sep ($NF - 1); sep=","; prev = $NF; out("\t\33[6n")}'
stty "$saved_settings"
)
Затем вы можете использовать это как expand -t "$tabs"
решение @ Kusalananda.
x
) перед вызовом, вexpand
противном случае вы бы также посчитали пробелы, которые изначально были во входных данных.