Как управлять файлом CSV с sed или awk?

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

Существует простой путь с отрицанием и обратными ссылками.

grep '[spine]' | grep -Ev '([spine]).*\1'

Первое grep выбирает строки, которые содержат по крайней мере один из einps; второе grep строки отклонений, которые содержат больше чем одного из любого (например, разрешение spinal tap и spend но нет foobar или see).

23
18.12.2011, 02:40
5 ответов

Кроме того, как сократить и перестроить поля (охваченный в других ответах), существует проблема изворотливых полей CSV.

Если Ваши данные попадают в эту "изворотливую" категорию, немного пред, и фильтрация сообщения может заботиться о нем. Фильтры, показанные ниже, требуют символов \x01,\x02,\x03,\x04 не появиться где угодно в Ваших данных.

Вот фильтры, перенес простое awk полевой дамп.

Поле Примечание: пять имеет недопустимое/неполное "заключенное в кавычки поле" расположение, но это мягко в конце строки (в зависимости от синтаксического анализатора CSV). Но, конечно, это вызвало бы проблематичные результаты unexpedted, если бы это должно было быть подкачано далеко от его текущего положения конца строки.

Обновление; user121196 указал на ошибку, когда запятая предшествует запаздывающей кавычке. Вот фиксация.

Данные

cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF

Код

sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
  awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
    sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g' 

Вывод:

field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five

"15111 N. Hayden Rd., Ste 160,"
""

Вот пред фильтр, расширенный с комментариями.
Фильтр сообщения является просто реверсированием \x01.\x02,\x03,\x04

sed -r '
    s/^/,/                # add a leading comma delimiter
    s/\\"/\x01/g          # obfuscate escaped quotation-mark (\")
    s/,"([^"]*)"/,\x02\1\x03/g    # obfuscate quotation-marks
    s/,"/,\x02/           # when no trailing quote on last field  
    :MC                   # obfuscate commas embedded in quotes
    s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
    tMC
    s/^,//                # remove spurious leading delimiter
'
7
27.01.2020, 19:42
  • 1
    , как Вы удалили бы энный столбец на основе этого фильтра? –  user121196 03.12.2012, 12:50
  • 2
    @user121196 - Как упомянуто в его первом предложении, этот ответ показывает способ сделать данные CSV более последовательными.. например, путем вторичной замены встроенной в кавычку запятой нейтральным маркерным символом... и затем возвращения его назад в запятую после перемещения/сокращения/удаления. Снова, как упомянуто, перемещать/сокращать/удалять шаг заменяется простым awk полевым дампом. –  Peter.O 03.12.2012, 13:39
  • 3
    это перестало работать для этого случая: "N. Hayden Rd., 15111, оф. 160","", –  user121196 04.12.2012, 11:22
  • 4
    @user121196: Спасибо за указание на это. Я обновил ответ с фиксацией. –  Peter.O 04.12.2012, 17:30

Это зависит от того, использует ли Ваш файл CSV запятые только для разделителей, или если у Вас есть безумие как:

поле один, "поле, два", поле три

Это предполагает использование простого файла CSV:

Удаление столбца

Можно избавиться от отдельного столбца много путей; я использовал столбец 2 в качестве примера. Самый легкий путь состоит в том, чтобы, вероятно, использовать cut, который позволяет Вам указать разделитель -d и какие поля Вы хотите распечатать -f; это говорит этому разделять на запятых и производить поле 1 и поля 3 через конец:

$ cut -d, -f1,3- /path/to/your/file

Если на самом деле необходимо использовать sed, можно записать регулярное выражение, которое соответствует первому n-1 поля, nполе th, и остальные и пропуск, производящий nth (здесь n 2, таким образом, первая группа подобрана 1 время: \{1\}):

$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file

Существует много способов выполнить в этом awk, ни один из них особенно изящный. Можно использовать a for цикл, но контакт с запаздывающей запятой является болью; игнорирование этого это было бы что-то как:

$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file

Я нахожу легче произвести поле 1 и затем использовать substr осуществить все после поля 2:

$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file

Это является раздражающим для столбцов далее вперед хотя

Дублирование столбца

В sed это - по существу то же выражение как прежде, но Вы также получаете целевой столбец и включаете ту группу многократно в замену:

$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file

В awk для цикла путем это было бы что-то как (снова игнорирование запаздывающей запятой):

$ awk -F, '{
for(i=1; i<=NF; i++) {
    if(i == 2) printf "%s,", $i;
    printf "%s,", $i
}
print NL
}' /path/to/your/file

substr путь:

$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file

(tcdyl придумал лучший метод в его ответе),

Перемещение столбца

Я думаю sed решение следует естественно от других, но оно начинает становиться смехотворно длинным

15
27.01.2020, 19:42

awk Ваш лучший выбор. awk поля печати числом, таким образом...

awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file

Для удаления столбца, не печатают его:

 awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file

Изменить порядок:

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file

Перенаправление к выходному файлу.

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file

awk может отформатировать вывод также.

Формат Awk производится

12
27.01.2020, 19:42
  • 1
    Так как это - CSV, Вам также будет нужно BEGIN { FS=","; OFS=","; }. –   16.12.2011, 04:13
  • 2
    я думаю даже FS = OFS ="", будет работать. –   16.12.2011, 08:36

Учитывая разграниченный пространством файл в следующем формате:

1 2 3 4 5

Можно удалить поле 2 с awk как так:

awk '{ sub($2,""); print}' file

который возвращается

1  3 4 5

Столбец 2 замены со столбцом n в соответствующих случаях.

Копировать столбец 2,

awk '{ col = $2 " " $2; $2 = col; print }' file

который возвращается

1 2 2 3 4 5

Переключить столбец 2 и 3,

awk '{temp = $2; $2 = $3; $3 = temp; print}'

который возвращается

1 3 2 4 5

awk обычно очень хорош в контакте с понятием полей. Если Вы имеете дело с CSV и не разграниченным пространством файлом, можно просто использовать

awk -F,

определить Ваше поле как запятую, вместо пространства (который является значением по умолчанию). Онлайн существует много хороших awk ресурсов, один из которых я перечисляю как источник ниже.

Источник для № 3

5
27.01.2020, 19:42
  • 1
    я не знаю много о awk, но это, кажется, производит разделенный пробелом, даже если разделитель полей , (разделитель полей просто управляет, как он обрабатывает вход) –  Michael Mrozek♦ 16.12.2011, 04:59
  • 2
    @MichaelMrozek: да, это - OFS awk переменная, которая управляет выходным разделителем полей. –  enzotib 16.12.2011, 10:22
  • 3
    Да, и поскольку я упоминаю в своем ответе, можно передать-F опцию awk для изменения разделителя (например,-F) –  tcdyl 16.12.2011, 22:12

Это будет работать для удаления ввода

awk '{$2="";$0=$0;$1=$1}1'

ввода

a b c d

a c d
0
27.01.2020, 19:42

Теги

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