Перестановка стовпців за допомогою awk


13

Я намагаюся перемістити 7-й стовпчик мого файлу CSV до кінця, використовуючи

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

де $ file - це .csv файл у каталозі. Однак вихід є

awk:                          ^ syntax error

Хтось знає, як виправити цю помилку?


7
Показуючи awk помилки, вам потрібно показати всю річ. ^Вказує на певну частину команди , де була виявлена помилка.
terdon

Відповіді:


11

Для -Fпараметра потрібен аргумент: -F,наприклад.

Кінець awkсценарію повинен бути розділений (пробіл) з рештою параметрів.

Якщо роздільник поля є, ,і ви хочете зберегти його, і якщо кількість стовпців є постійним і меншим або рівним 11, спробуйте це:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"

8
@anuribs це дуже мало програм. Стандартний спосіб такий command file > newfile && mv newfile file. Тим НЕ менше, нова версія GNU awkдля підтримки цього: gawk -i inplace '{blah blah}' file.
terdon

1
Альтернативно, замість того, щоб mv newfile fileви могли використовувати cat newfile > file ; rm -f newfile- це зберігає inode та дозволи file.
cas

і, як правило, корисно використовувати mktempсценарії, а не жорстке кодування тимчасових імен файлів. наприкладtf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

8

Коротше рішення було б

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

Я не впевнений, чи ,+буде працювати у всіх awkверсіях, але працює принаймні в GNU awk, також в -cрежимі сумісності.

Пояснення:

  • $(NF+1)=$7: спочатку додаємо 7-е поле до кінця рядка (це може бути $12=$7в цьому випадку)
  • $7="": на наступному кроці 7-е поле стирається (але оточуючі роздільники залишаються)
  • щоб видалити роздільники, нам потрібно відновити весь запис (через $0=$0), розглядаючи декілька коми як роздільник поля (це робиться через -F',+', тут +мається на увазі один чи більше разів), а також переставити поточний запис через $1=$1змусити відновити рядок, використовуючи раніше встановлене вихідне поле роздільник (встановлюється опцією -v OFS=,)
  • після того як всі перетасування зроблені, ми готові надрукувати результат 1

Приклад введення:

1,2,3,4,5,6,7,8,9,10,11

вихід

1,2,3,4,5,6,8,9,10,11,7

Що робити, якщо інші стовпці порожні? Але, так, FS - це регулярний вираз у POSIX (якщо це кілька символів), так це ,+має працювати.
Випадково832

(1) Я розумію, що зробити сьомий стовпчик вхідних даних «зникнути», а не просто встановити його на нуль, є складною частиною цієї проблеми. Але, як говорить Random832, ваше рішення клобує порожні стовпці (наприклад, all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7- розумний підхід. ІМХО, $0 = $0 OFS $7трохи зрозуміліше, лише на пару символів довше, і, здається, робиться те саме. Чи можете ви придумати ситуацію, в якій $0 = $0 OFS $7не так, як ваш код?
G-Man каже: "Відновіть Моніку"

@ Random832 @ G-Man так, деякі крайові випадки, такі як порожні поля, порожні рядки або NF <7, слід обробляти окремо або слід переставляти код. Це лише ідея, а не "повне рішення" для всіх загальних випадків, що має бути зрозумілим. $0=$0 OFS $7ймовірно, ідентичний $(NF+1)=$7, але лише з рештою коду незмінним, не загалом.
jimmij

5

Якщо ви друкуєте з OFS=, так що немає роздільника між полями, ви можете просто зберегти значення $7змінної, встановленої $7на порожнє та надрукувати рядок та змінну безпосередньо. Не потрібно вказувати всі поля:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687

3

Ви, мабуть, маєте на увазі:

awk -F, -v OFS='' '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' "$file"

Ви знаєте, що awkніколи не бачить жодної цитати OFS='', правда? Ви також можете просто набрати OFS=; це точно так само.
Wildcard

1
Так, я це усвідомлюю. Однак мені не подобаються звисаючі завдання.
Michael Vehrs


3

Ви спеціально не сказати , що ви хочете використовувати AWK, і ви ж говорите , що ви хочете використовувати на місці монтажу , як передбачено sed -i, так ось sed -iваріант. Зазвичай awkце краще для роботи зі стовпцями, але це один із випадків, коли я віддаю перевагу sed, оскільки він, природно, обробляє довільну кількість стовпців.

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

Пояснення:

  • -r вибирає розширені регулярні виразки, щоб уникнути великої кількості відхилень
  • перша група - це $ N повторів рядків, що закінчуються комами, іншими словами, стовпці перед тією, яку ми хочемо перемістити, із заключною комою
  • друга група - $ N-й повтор, ми про це забуваємо
  • третя група - це стовпець, який ми хочемо перемістити, без остаточної коми
  • Четверта група складається з усіх стовпців після того, як ми хочемо перейти, без кома раніше
  • ми замінюємо першу групу, останню групу і стовпчик, який ми видобули, вставляючи косу за потребою.

Звичайно, це не буде працювати з файлами, які приховують коми в лапки (або ще гірше, уникнути їх), але awk не впорається з цим без серйозних акробатик. Якщо у вас є ця проблема, вам буде краще з perlмодулем Text:CSVабо pythonмодулем csv.


2

Пара awkваріантів (якщо припустимо, що ваш файл знаходиться всередині змінної $file)

  • Тут ви можете провести цикл для всієї колонки, друкувати за допомогою роздільника поля (OFS) та друкувати термінатор запису (ORS) в кінці рядка.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Тут з використанням регулярного вираження та gensub()функції

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    вбивство 7- го поля та друк його в кінці рядка.

    • $0 - це весь запис
    • $nє n- м записом
    • NF - Кількість полів поточного рядка
    • OFS вихідний поданий роздільник
    • ORS вихідний термінатор запису
    • 1це хитрість сказати awk trueта надрукувати за замовчуванням ( $0).

Оновити ...

Я майже забуваю, що можна зрушити всі стовпчики, наступні за 7- м .

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"

(1) Можливо, OFS $7було б більш надійним, ніж "," $7. (2) Я вважаю, що ", " $7це неправильно, якщо питання вказує на те, що ОП не хоче пробілів після коми. (І якщо вхідні дані мали пробіли після коми, то $7вони вже починалися з пробілу, і ви додавали б додатковий.)
G-Man каже: "Відновити Моніку"

@ G-Man В основному пропонувалися якісь ідеї, деякі варіанти. Дякую, за місце, я погоджуюсь OFS $7, не тільки більш надійний, але ще загальніший ( "поспіх робить сміття" )
Hastur
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.