Команда AWK не работает должным образом, выдавая & данные в качестве вывода.

Вопрос или проблема

У меня есть файл, в котором мне нужно выяснить, пустая ли какая-то колонка. Файл разделен на части с помощью ;

Некоторые данные содержат & в слове, а команда awk не работает с этим.

Допустим, у меня есть файл (dedup_company_output.txt), который содержит следующие данные:

"c1";"c2";"c3";"c4";"c5"
900;"DAVIES TURNER & CO LTD";;315235;1
909;"DAVIES TURNER CO LTD";;444;1
909;"DAVIES TURNER CO LTD";;;1

Теперь в первой строке я не уверен, почему мы получаем & вместо &, и поэтому, когда я выполняю следующую команду, это дает мне неправильный результат.

awk -F';' '$4 == ""' dedup_company_output.txt> result.txt

В результате я получаю

900;"DAVIES TURNER & CO LTD";;315235;1
909;"DAVIES TURNER CO LTD";;;1

в то время как я ожидал только

909;"DAVIES TURNER CO LTD";;;1

Нужно ли мне что-то изменить в этой команде, чтобы включить & как часть слова, или мне нужно изменить файл?

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

В вашем примере на второй строке & представлен как &, и этот закрывающий ; ошибочно интерпретируется awk как разделитель полей. Таким образом, для этой строки он считает "DAVIES TURNER &AMP вторым полем, CO LTD" третьим, а четвертое пустым. Это дает ложное совпадение.

Если вы хотите использовать awk, вам, вероятно, следует рассмотреть возможность изменения исходного файла, чтобы не экранировать специальные символы, или изменить разделитель полей на что-то отличное от ;.

В качестве альтернативы вы можете использовать инструмент, такой как miller, для работы с полноценными CSV файлами.

Используя mlr:

$ mlr --csv -N --fs=";" filter '$4 == ""' file
$ mlr --csv --headerless-csv-output --fs=";" filter '$c4 == ""' file #Для фильтрации с использованием заголовков

Использование -N для CSV файла добавит числовые заголовки, такие как 1,2 и т.д., как с --implicit-csv-header, но -N не печатает строку заголовка. С этим $4 поле может быть использовано так же, как в awk.

Вторая команда использует заголовок $c4 для фильтрации. С --headerless-csv-output она не выводит строку заголовка.

Как предложил @AdminBee, следует использовать инструмент, осведомленный о CSV.

&AMP не имеет значения, ; внутри кавычек и является причиной сбоя вашего скрипта. Существует много способов справиться с этим, но самый простой способ, когда у вас только некоторые более ранние поля, которые могут быть в кавычках и содержать FS, как в вашем случае, — это подсчитывать поля с конца, а не с начала, например, используя любой awk:

$ awk -F';' '$(NF-1) == ""' dedup_company_output.txt
909;"DAVIES TURNER CO LTD";;;1

Если этого недостаточно или вы хотите узнать больше о проблеме, то смотрите whats-the-most-robust-way-to-efficiently-parse-csv-using-awk для получения дополнительной информации.

Вы определили разделитель входных полей как ;, но этот символ также появляется как данные, что заставляет awk думать, что следующая строка действительно имеет 6 полей:

900;"DAVIES TURNER & CO LTD";;315235;1
   ^                   ^        ^^      ^

ЗАМЕТКИ:

  • awk видит 5 ; символов (а не 4 как в других строках)
  • awk видит 3-е, 4-е и 5-е поля как CO LTD", >пусто< и 325235, соответственно
  • поскольку 4-е поле >пусто< ($4==""), эта строка появляется в вашем выводе

Если вы используете GNU awk 4.0+, вы можете использовать переменную FPAT, чтобы определять поля по содержимому (в отличие от -F, который определяет поля по разделителю). В этом случае вы хотите сказать (g)awk, что поля разделены точками с запятой и обёрнуты в необязательные двойные кавычки, одновременно позволяя точке с запятой появляться как данные.

Вот одна идея для awk для этого случая:

awk '
BEGIN    { FPAT = "([^;]+)|(\"[^\"]+\")" }
$4 == ""
' input.txt

#######
# или как однострочная команда

awk 'BEGIN {FPAT = "([^;]+)|(\"[^\"]+\")"} $4==""' input.txt

Это создаёт:

909;"DAVIES TURNER CO LTD";;;1

Ответ или решение

Когда вы сталкиваетесь с проблемами в работе с утилитой awk, особенно при обработке данных, содержащих специальные символы и кавычки, важно учитывать несколько факторов.

Проблема

Вы упомянули, что используете файл с разделителем ;, в котором некоторые поля могут содержать сам разделитель, если они обернуты в кавычки. В вашем конкретном случае строка, содержащая &, обрабатывается как часть данных, но приводит к неожиданному количеству полей. Как следствие, команда awk -F';' '$4 == ""' dedup_company_output.txt возвращает больше строк, чем вы ожидали.

Анализ данных

В вашем файле есть две строки, которые приводят к путанице:

  • 900;"DAVIES TURNER &amp;AMP; CO LTD";;315235;1
  • 909;"DAVIES TURNER CO LTD";;;1

Первое поле обрабатывается неверно из-за того, что awk видит нечетное количество ;, а именно 5, что создаёт дополнительное поле в записи. Это происходит, потому что awk не понимает, что ; внутри кавычек является частью данных, а не разделителем.

Решение

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

awk 'BEGIN { FPAT = "([^;]+)|(\"[^\"]+\")" } $4 == ""' dedup_company_output.txt

Пояснение:

  1. BEGIN { FPAT = "([^;]+)|(\"[^\"]+\")" } – Это устанавливает паттерн для разбиения полей, который позволяет awk учитывать как текстовые поля без кавычек, так и текстовые поля с кавычками.
  2. $4 == "" – Это условие проверяет, пустое ли четвертое поле.

Альтернативный подход

Если вы хотите использовать более специализированный инструмент для работы с CSV, вы можете попробовать mlr (Miller):

mlr --csv -N --fs=";" filter '$4 == ""' dedup_company_output.txt

или, используя заголовки:

mlr --csv --headerless-csv-output --fs=";" filter '$c4 == ""' dedup_company_output.txt

Заключение

Использование стандартных инструментов, таких как awk, может быть неэффективным при работе с более сложными данными, особенно когда поля могут содержать разделители. Альтернативные инструменты, такие как mlr, предоставляют более мощные возможности для разбора CSV-документов. Ваша конечная цель должна заключаться в том, чтобы использовать подходящие инструменты в зависимости от сложности вашего конкретного сценария обработки данных.

Оцените материал
Добавить комментарий

Капча загружается...