zaLinux.ru

awk и табуляция во вводимых и выводимых данных


Почему awk неправильно определяет границы данных, разделённых табуляцией

Следующая команда вместо ожидаемого третьего столбца вернёт пустой результат:

echo '1     2     3     4     5     6' | awk -F'\t' '{ print $3 }'

В команде вместо стандартного FS (Input field separator, разделитесь полей входных данных), которым по умолчанию является пробел, опцией -F'\t' установлен новый разделитесь, в качестве которого указан «\t», что означает символ табуляции.

Проблема предыдущей команды в том, что во входных данных поля на самом деле не разделены символами табуляции, а разделены несколькими пробелами.

То есть использовании опции -F не нужно в предыдущей команде:

echo '1     2     3     4     5     6' | awk '{ print $3 }'
3

Несмотря на то, что данные разделены несколькими пробелами, указывать это с опцией -F не нужно, поскольку она правильно истолковывает ввод. По умолчанию разделителем полей в awk служит один или больше пробелов (пробельные символы или символ табуляции), что соответствует [ \t]+ или если использовать posix классы [[:blank:]]+

Именно поэтому даже если данные на самом деле разделены табуляцией, команда awk обрабатывает их правильно:

echo '1	2	3	4' | awk '{ print $3 }'
3

В этом случае опция -F'\t' работает как и ожидается:

echo '1	2	3	4' | awk -F'\t' '{ print $3 }'
3

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


Чтобы проверить, какие именно непечатные символы присутствуют во вводимых данных, используйте cat -A. Например:

echo '1	2	3    4' | cat -A
1M-bM-^PM-^A^IM-oM-?M-=2^I3    4$

Как сделать так, чтобы awk выводила поля разделяя их табуляцией

Следующая команда выведет третью и четвёртую колонку, разделяя их пробелом:

echo '1	2	3	4	5' | awk '{ print $3,$4 }'
3 4

Если вы хотите, чтобы выводимые данные разделялись табуляцией (или любым другим символом), то его нужно установить в качестве значения OFS (output field separator, разделитесь выходных полей). Например:

echo '1	2	3	4	5' | awk 'BEGIN {OFS="\t"}; { print $2,$3 }'
2	3

OFS вставляется между полями, перечисленными через запятую, то есть следующая команда не выведет табуляции между полями (и даже не выведет пробел):

echo '1	2	3	4	5' | awk 'BEGIN {OFS="\t"}; { print $2 $3 }'
23

Кроме изменения значения OFS (output field separator, разделитесь выходных полей) вы можете указать символ табуляции в шаблоне вывода. Например, следующая команда для разделения второго и третьего поля будет использовать стандартный OFS (то есть пробел), а между третьей и четвёртой колонкой будет вставлен знак табуляции:

echo '1	2	3	4	5' | awk '{ print $2,$3"\t"$4 }'
2 3	4


Смотрите также: Уроки по Awk


Рекомендуемые статьи:

3 Комментарии

  1. redking

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

    наверное следует уточнить, что по умолчанию разделителем полей в awk служит один или больше пробельных символов, что соотвецтвует [ \t]+ или если использовать posix классы [[:blank:]]+

    В этом случае опция -F'\t' работает как и ожидается:

    echo '1 2   3   4   5' | awk -F'\t' '{ print $3 }'

    3

    кстати если просто копировать данный пример в консоль для выполнения то он не сработает, так как в нём не присутствует табуляций, а тока пробелы.

    Так как визуально трудно понять пробел это или табуляция то для подобных примеров можно использовать следующую запись:

    echo -e '1\t2\t3\t4\t5' | awk -F'\t' '{ print $3 }'

    3

    но как понять есть ли в строке табуляция или другие невидимые спец символы в виде тех же неразрывных пробелов, которые так же попадаются:

    echo -e '1 2 3 4' | awk '{print $3}'

    4

    (к сожалению при копировании сюда того же узкого неразрывного пробела он преобразовывается в обычный пробел)

    получаемые результаты порой могут ввести в ступор ))

    в таком случае для быстрой проверки на неожиданные символы подойдёт тот же cat:

    echo -e '1 2 3 4' | cat -A
    1M-bM-^@M-/2 3 4$

     

    1. Alexey (Автор записи)

      наверное следует уточнить, что по умолчанию разделителем полей в awk служит один или больше пробельных символов, что соответствует [ \t]+ или если использовать posix классы [[:blank:]]+

      Приветствую! Согласен, так намного понятнее. Я использовал данные из man awk, а там написано «FS Input field separator regular expression; a <space> by default». Непонятно, почему они написали «пробел» без упоминания табуляции. Если я правильно понимаю, то написать там «белый пробел» тоже неверно? Это доказывает ваш пример с неразрывным пробелом. А ведь этих белых пробелов немало.

      кстати если просто копировать данный пример в консоль для выполнения то он не сработает, так как в нём не присутствует табуляций, а тока пробелы.

      Да, с табуляцией меня постигла неудача. Сначала текстовый редактор их превращал в пробелы, а затем движок сайта (или плагин для форматирования кода). Я тоже подумал, что входные данные можно вводить с помощью echo -e, но ведь задумка была показать, что выглядящие одинаково данные могут оказаться не тем, что мы про них думаем (думаем там табуляция, а там пробелы).

      С помощью «&#9;», «<pre></pre>» и отказа от подсветки синтаксиса команд вроде бы удалось решить проблему — у меня теперь третий и четвёртый примеры при копировании в консоль работают как ожидается.

      в таком случае для быстрой проверки на неожиданные символы подойдёт тот же cat:

      Я добавил пример с cat в статью, причём вставил несколько непечатных символов, которые невидны в на веб-странице и сохраняются при копировании в консоль (хотя при этом некоторые символы становятся видны).

      1. redking

        Приветствую! Согласен, так намного понятнее. Я использовал данные из man awk, а там написано «FS Input field separator regular expression; a <space> by default». Непонятно, почему они написали «пробел» без упоминания табуляции.

        там всё сложно ))

        но немного проясняется если почитать Default field separator for awk на stackoverflow

Оставить комментарий

Ваш адрес email не будет опубликован.