Як маніпулювати файлом CSV за допомогою sed або awk?


23

Як я можу зробити наступне для файлу CSV за допомогою sedабо awk?

  • Видалити стовпчик
  • Скопіювати стовпчик
  • Переміщення стовпця

У мене великий стіл із понад 200 рядків, і я не такий знайомий sed.


1
Крос розміщено на AskUbuntu
enzotib

@enzotib Ви можете опублікувати посилання?
n0pe

@MaxMackie askubuntu.com/questions/88142/… . Я не можу одержати мода там у цю годину, тому я позначив його проханням перенести, якщо вони бажають; у ньому вже є прийнята відповідь, тому я не впевнений, чи будуть вони
Майкл Мрозек

@MichaelMrozek, хммм, що зазвичай відбувається в таких ситуаціях? Ми просто зберігаємо дублікати?
n0pe

1
Якщо вам не потрібно запускати систему, в якій доступні лише основні інструменти, див. Чи є надійний інструмент командного рядка для обробки файлів CSV?
Жил 'ТАК - перестань бути злим'

Відповіді:


7

Окрім того, як вирізати та переупорядкувати поля (висвітлено в інших відповідях), існує проблема вигадливих полів CSV.

Якщо ваші дані відносяться до цієї «химерної» категорії, про це можна подбати про трохи попередньої та після фільтрації. Фільтри , зазначені нижче , вимагають символи \x01, \x02, \x03, \x04щоб не з'являтися в будь-якому місці в ваших даних.

Ось фільтри, загорнені навколо простого awkпольового відвалу.

Примітка: у полі п'ятому є невірний / неповний макет "котируемого поля", але він є доброякісним в кінці ряду (залежно від аналізатора CSV). Але, звичайно, це призведе до проблемних неочікуваних результатів, якби його замінити з поточного положення в кінці ряду .

Оновлення; 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
'

як би ви видалили n-й стовпчик на основі цього фільтра?
користувач121196

@ user121196 - Як було сказано у вступному реченні, ця відповідь показує спосіб зробити дані CSV більш послідовними .. Наприклад. шляхом тимчасової заміни вбудованої цитати комою з нейтральним символом лексеми ..., а потім поверненням її назад до коми після переміщення / вирізання / видалення. Знову, як уже згадувалося, крок переміщення / вирізання / видалення замінюється простим дамп-полем-дамп .
Пітер.О

1
це не вдається для цього випадку: "15111 N. Hayden Rd., Ste 160,", ""
user121196

@ user121196: Дякую, що вказали на це. Я оновив відповідь виправленням.
Пітер.О

15

Це залежить від того, чи ваш файл CSV використовує коми лише для роздільників, або якщо у вас є божевілля, наприклад:

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

Це передбачає, що ви використовуєте простий файл CSV:

Видалення стовпця

Ви можете позбутися однієї колонки багатьма способами; Я використовував колонку 2 як приклад. Найпростіший спосіб - це, мабуть, використання cut, який дозволяє вказати роздільник -dі поля, які ви бажаєте надрукувати -f; це говорить про те, що воно розділяється на коми і поле 1 виводу, і поля 3 до кінця:

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

Якщо вам насправді потрібно скористатися sed , ви можете написати регулярний вираз, який відповідає першим n-1полям, другому nполю та іншим, і пропустити виведення nth (ось n2, тому перша група відповідає 1часу :) \{1\}:

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

Існує цілий ряд способів зробити це awk, жоден з них не є вишуканим. Можна скористатися 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 способі for циклу це було б щось на зразок (знову ігноруючи кінцеву кому):

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

The substrчином:

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

(tcdyl придумав кращий метод в своїй відповіді )

Переміщення стовпця

Я думаю, що sedрішення випливає природно з інших, але воно починає надмірно довго


Це завантажена відповідь! +1 :)
jaypal singh


12

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


Оскільки це CSV, вам також знадобиться BEGIN { FS=","; OFS=","; }.

1
Я думаю, навіть FS = OFS = "," спрацює.

5

Дано файл з обмеженим пробілом у такому форматі:

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


Я не знаю багато про що awk, але, здається, виводиться простір, навіть якщо роздільник поля є ,(роздільник поля просто керує тим, як він обробляє введення)
Michael Mrozek

@MichaelMrozek: так, це змінна OFS awk, яка керує роздільником вихідного поля.
enzotib

Так, і, як я згадую у своїй відповіді, ви можете пройти опцію -F, щоб пробудити, щоб змінити роздільник (наприклад, -F,)
tcdyl

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.