Определите, как долго табуляции 't' находятся в строке

Я думаю, вы можете написать сценарий так, чтобы использовать uuid диска вместо метки устройства блока. Например: part / --fstype=ext4 --onpart=/dev/disk/by-id/ata-ST3160815AS_6RA0C882. По крайней мере, это гарантирует согласованность при перезагрузках.

10
12.05.2018, 16:51
4 ответа

Символ 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 . Это даже лучше, так как позволяет указать позиции табуляции, но, к сожалению, в настоящее время не работает с GNUexpand(по крайней мере ), когда ввод содержит многобайтовые символы -или ширину 0 -(, например, объединение символов )или символы двойной ширины -.


¹ однако обратите внимание, что если вы это сделаете stty tab3, дисциплина строки устройства tty возьмет на себя обработку табуляции (преобразует TAB в пробелы на основе собственного представления о том, где может находиться курсор перед отправкой на терминал )и реализовать табуляцию через каждые 8 ​​столбцов. Тестирование в Linux показало, что он правильно обрабатывает символы CR, LF и BS, а также многобайтовые символы UTF -8 (при условии, что iutf8также находится на ), но это все. Предполагается, что все остальные не -управляющие символы (, включая нулевую ширину -, символы двойной ширины -)имеют ширину 1, (очевидно, что )не обрабатывает управляющие последовательности, не переносится должным образом... Это, вероятно, предназначено для терминалов, которые не могут выполнять обработку вкладок.

В любом случае, линейная дисциплина 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.

22
27.01.2020, 19:59

Также используется expand, но с манипуляциями с параметрами bash для подсчета количества пробелов:

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11
2
27.01.2020, 19:59
$ expand file | awk '{ print gsub(/ /, " ") }'
11
9
9

Утилита POSIX expandзаменяет табуляцию пробелами. Скрипт awkподсчитывает и выводит количество замен, необходимых для замены всех пробелов в каждой строке.

Чтобы избежать подсчета любых ранее существовавших пробелов во входном файле:

$ tr ' ' '@' <file | expand | awk '{ print gsub(/ /, " ") }'

, где @— символ, который гарантированно не существует во входных данных.

Если вы хотите 10 пробелов на табуляцию вместо обычных 8:

$ tr ' ' '@' <file | expand -t 10 | awk '{ print gsub(/ /, " ") }'
9 
15
13
8
27.01.2020, 19:59

Сperl:

perl -F/\\t/ -lpe '$c = 0; $F[-1] eq "" or pop @F; $_ = (map { $c += 8 - (length) % 8 } @F)[-1]' file

В качестве альтернативы:

perl -MList::Util=reduce -lpe \
    '@F = split /\t/, $_, -1; pop @F if $F[-1] ne ""; $_ = reduce { $a + $b } map { 8 - (length) % 8 } @F' file

Вы можете заменить 8 выше другим значением, если хотите, чтобы вкладки TAB имели другую длину.

2
27.01.2020, 19:59

Теги

Похожие вопросы