Как заставить awk игнорировать полевой разделитель внутри двойных кавычек?
Мне нужно удалить 2 столбца в файле разделенных запятыми.
Рассмотрим следующую строку в файле csv:
"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4
Теперь результат я хочу в конце:
"[email protected],www.example.com",field4
"[email protected]",field4
Я использовал следующую команду:
awk 'BEGIN{FS=OFS=","}{print $1,$4}'
Но встроенная запятая, которая находится внутри кавычек, создает проблему. Ниже приводится результат, который я получаю:
"[email protected],field3
"[email protected]",field4
Теперь мой вопрос: как сделать awk игнорировать ",", которые находятся внутри двойных кавычек?
Ответы
Ответ 1
Из руководства GNU awk (http://www.gnu.org/software/gawk/manual/gawk.html#Splitting-By-Content):
$ awk -vFPAT='([^,]*)|("[^"]+")' -vOFS=, '{print $1,$4}' file
"[email protected],www.example.com",field4
"[email protected]",field4
и см. Каков наиболее надежный способ эффективного анализа CSV с использованием awk? для более широкого анализа CSV файлов, которые включают в себя новые строки и т.д. в пределах полей.
Ответ 2
Это не решение bash/awk, но я рекомендую CSVKit, который может быть установлен pip install csvkit
. Он предоставляет набор инструментов командной строки для работы с CSV, в том числе csvcut
, который делает именно то, что вы просите:
csvcut --columns=1,4 <<EOF
"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4
EOF
Вывод:
"[email protected],www.example.com",field4
[email protected],field4
Он удаляет ненужные кавычки, которые, я полагаю, не должны быть проблемой.
Прочитайте документы CSVKit здесь, в RTD. ThoughtBot имеет приятный маленький пост в блоге, представляющий этот инструмент, в котором я узнал о CSVKit.
Ответ 3
В вашем примере входного файла это первое поле и только первое поле, которое цитируется. Если это вообще верно, тогда рассмотрите следующее как метод для удаления второго и третьего столбцов:
$ awk -F, '{for (i=1;i<=NF;i++){printf "%s%s",(i>1)?",":"",$i; if ($i ~ /"$/)i=i+2};print""}' file
"[email protected],www.example.com",field4
"[email protected]",field4
Как упоминалось в комментариях, awk не изначально понимает цитируемые разделители. Это решение работает вокруг этого, ища первое поле, которое заканчивается цитатой. Затем он пропускает два следующих поля.
Подробности
-
for (i=1;i<=NF;i++)
Это запустит for
по каждому полю i
.
-
printf "%s%s",(i>1)?",":"",$i
Отправляет поле i
. Если это не первое поле, этому поле предшествует запятая.
-
if ($i ~ /"$/)i=i+2
Если текущее поле заканчивается двойной кавычкой, это увеличивает счетчик полей на 2. Это то, как мы пропускаем поля 2 и 3.
-
print""
После завершения цикла for
это печатает новую строку.
Ответ 4
Этот awk должен работать независимо от того, где указано поле, и работает также с экранированными кавычками.
awk '{while(match($0,/"[^"]+",|([^,]+(,|$))/,a)){
$0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
print b[1] b[4];x=0}' file
Ввод
"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4
field1,"[email protected],www.example.com",field3,field4
Выход
"[email protected],www.example.com",field4
"[email protected]",field4
field1,field4
Он работает даже на
field1,"field,2","but this field has ""escaped"\" quotes",field4
То, что могучая переменная FPAT завершается с ошибкой!
Объяснение
while(match($0,/"[^"]+",|([^,]+(,|$))/,a))
Запускает цикл while, который продолжается до тех пор, пока совпадение имеет успех (т.е. есть поле).
Соответствие соответствует первому появлению регулярного выражения, которое случайно совпадает с полями и сохраняет его в массиве a
$0=substr($0,RSTART+RLENGTH);b[++x]=a[0]
Устанавливает $0
для начала в конце совпадающего поля и добавляет соответствующее поле в соответствующее положение массива в b
.
print b[1] b[4];x=0}
Распечатывает нужные поля с b
и устанавливает x обратно к нулю для следующей строки.
Дефекты
Ошибка, если в поле содержатся как экранированные кавычки, так и запятая
Изменить
Обновлено для поддержки пустых полей
awk '{while(match($0,/("[^"]+",|[^,]*,|([^,]+$))/,a)){
$0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
print b[1] b[4];x=0}' file