Перестановка столбцов с помощью awk

12

Я пытаюсь переместить 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, вам нужно показать все это. ^Указывает на определенную часть команды , где была обнаружена ошибка.
Тердон

Ответы:

10

-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.
Тердон
1
альтернативно, вместо того, чтобы mv newfile fileвы могли использовать cat newfile > file ; rm -f newfile- это сохраняет индекс и права доступа file.
КАС
и это, как правило, хорошая идея, 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
jimmij
источник
Что делать, если другие столбцы пусты? Но, да, FS - это регулярное выражение в POSIX (если оно состоит из нескольких символов), поэтому ,+должно работать.
Random832
(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, но только с остальной частью кода без изменений, но не в целом.
Джимми
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
Да, я это понимаю. Однако мне не нравятся висячие задания.
Майкл Вёрс
@anuribs См. stackoverflow.com/questions/16529716/…
Бармар
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.

Law29
источник
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и напечатать default ( $0).

Обновить ...

Я почти забыла, что можно сдвинуть все столбцы после 7- го .

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"
Hastur
источник
(1) Возможно, OFS $7будет более надежным, чем "," $7. (2) Я считаю, что ", " $7это неправильно, поскольку вопрос указывает, что ОП не хочет пробелов после запятых. (И если бы во входных данных были пробелы после запятых, то $7они уже начинались бы с пробела, и вы бы добавили дополнительный.)
G-Man говорит: «Восстановите Монику»
@ G-Man В основном предлагалось несколько идей, несколько вариантов. Спасибо, за место, с которым я согласен OFS $7, не только более надежное, но и более общее ( «спешка тратит впустую» )
Hastur