Як я можу зробити наступне для файлу CSV за допомогою sed
або awk
?
- Видалити стовпчик
- Скопіювати стовпчик
- Переміщення стовпця
У мене великий стіл із понад 200 рядків, і я не такий знайомий sed
.
Як я можу зробити наступне для файлу CSV за допомогою sed
або awk
?
У мене великий стіл із понад 200 рядків, і я не такий знайомий sed
.
Відповіді:
Окрім того, як вирізати та переупорядкувати поля (висвітлено в інших відповідях), існує проблема вигадливих полів 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
'
Це залежить від того, чи ваш файл CSV використовує коми лише для роздільників, або якщо у вас є божевілля, наприклад:
поле одне, "поле, два", поле три
Це передбачає, що ви використовуєте простий файл CSV:
Ви можете позбутися однієї колонки багатьма способами; Я використовував колонку 2 як приклад. Найпростіший спосіб - це, мабуть, використання cut
, який дозволяє вказати роздільник -d
і поля, які ви бажаєте надрукувати -f
; це говорить про те, що воно розділяється на коми і поле 1 виводу, і поля 3 до кінця:
$ cut -d, -f1,3- /path/to/your/file
Якщо вам насправді потрібно скористатися sed
, ви можете написати регулярний вираз, який відповідає першим n-1
полям, другому n
полю та іншим, і пропустити виведення n
th (ось n
2, тому перша група відповідає 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
рішення випливає природно з інших, але воно починає надмірно довго
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
може також форматувати вихід.
Дано файл з обмеженим пробілом у такому форматі:
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
, але, здається, виводиться простір, навіть якщо роздільник поля є ,
(роздільник поля просто керує тим, як він обробляє введення)
Це буде працювати для видалення
awk '{$2="";$0=$0;$1=$1}1'
Вхідні дані
a b c d
Вихід
a c d