Почему 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
Связанные статьи:
- Как удалить newline (символ новой строки) из вывода команд и файлов в командной строке Linux (80%)
- Как вывести от определённого столбца до последнего в командной строке Linux (80%)
- Как разбить большой файл (текстовый или бинарный) на файлы меньшего размера (РЕШЕНО) (70%)
- Уроки по Awk (60%)
- Как отфильтровать текст, находящийся между двумя определёнными строками (60%)
- Как отформатировать XML в командной строке чтобы он стал удобным для чтения (RANDOM - 50%)
наверное следует уточнить, что по умолчанию разделителем полей в awk служит один или больше пробельных символов, что соотвецтвует [ \t]+ или если использовать posix классы [[:blank:]]+
кстати если просто копировать данный пример в консоль для выполнения то он не сработает, так как в нём не присутствует табуляций, а тока пробелы.
Так как визуально трудно понять пробел это или табуляция то для подобных примеров можно использовать следующую запись:
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$
Приветствую! Согласен, так намного понятнее. Я использовал данные из man awk, а там написано «FS Input field separator regular expression; a <space> by default». Непонятно, почему они написали «пробел» без упоминания табуляции. Если я правильно понимаю, то написать там «белый пробел» тоже неверно? Это доказывает ваш пример с неразрывным пробелом. А ведь этих белых пробелов немало.
Да, с табуляцией меня постигла неудача. Сначала текстовый редактор их превращал в пробелы, а затем движок сайта (или плагин для форматирования кода). Я тоже подумал, что входные данные можно вводить с помощью echo -e, но ведь задумка была показать, что выглядящие одинаково данные могут оказаться не тем, что мы про них думаем (думаем там табуляция, а там пробелы).
С помощью «	», «<pre></pre>» и отказа от подсветки синтаксиса команд вроде бы удалось решить проблему — у меня теперь третий и четвёртый примеры при копировании в консоль работают как ожидается.
Я добавил пример с cat в статью, причём вставил несколько непечатных символов, которые невидны в на веб-странице и сохраняются при копировании в консоль (хотя при этом некоторые символы становятся видны).
там всё сложно ))
но немного проясняется если почитать Default field separator for awk на stackoverflow