Использование awk substr в файле фиксированной ширины - PullRequest
2 голосов
/ 27 февраля 2020

У меня есть файл фиксированной ширины, в котором я хотел бы разбить строки по значению, определенному первыми четырьмя символами, на файлы с именем file_1985.dat & file_1986.dat. Возьмите этот минимальный файл:

$ cat foo.dat
1985tiny dancer
1986largechicken
1985hey  jude

Я хотел бы закончить с этим:

$ cat file_1985.dat
1985tiny dancer
1985hey  jude

и это:

$ cat file_1986.dat
1986largechicken

Я уверен, что мне нужно сделать что-то вроде этого:

awk -F, '{if(???)print > "file_1985.dat";else print > "file_1986.dat"}' foo.dat

, где ??? предполагает некоторое использование substr. Кто-нибудь может посоветовать здесь?

Ответы [ 3 ]

2 голосов
/ 27 февраля 2020

Используйте substr($0, 1, 4), чтобы получить 4 символа, которые вы хотите go в имени файла (подстрока, начинающаяся с символа 1 длины 4):

awk '{ 
  out = "file_" substr($0, 1, 4) ".dat" # set filename
  if (out != prev) close(prev)          # close previous file
  print >> out                          # write to file
  prev = out                            # remember filename to check on next line
}' foo.dat

Имя файла out получено в результате конкатенации строковых литералов с результатом substr. Эта переменная используется для определения файла, в котором вывод print заканчивается.

>> открывает файл в «режиме добавления», что означает, что если вы повторно откроете тот же файл, вы не теряйте предыдущее содержимое.

Тестирование:

$ awk '{ out = "file_" substr($0, 1, 4) ".dat"; if (out != prev) close(prev); print >> out; prev = out  }' foo.dat
$ cat file_1985.dat 
1985tiny dancer
1985hey  jude
$ cat file_1986.dat 
1986largechicken

Использование close - это предосторожность, предотвращающая открытие слишком большого количества файлов, но если ваш ввод не слишком большой, тогда вы можете упростить до:

awk '{ print > ("file_" substr($0, 1, 4) ".dat") }' foo.dat

Что касается производительности, вы можете попробовать отсортировать входные данные, чтобы избежать повторного открытия и закрытия одних и тех же файлов (хотя сортировка сама по себе займет время):

sort -s -k1.1,1.4 foo.dat | awk '{ out = "file_" substr($0, 1, 4) ".dat"; if (out != prev) close(prev); print > out; prev = out  }'

Здесь я также изменил >> на >, поскольку awk будет открывать каждый файл только один раз.

Вы также можете избежать повторения одной и той же конкатенации строк один раз на строку, кэшируя результаты:

{
  ss = substr($0, 1, 4)
  if (!(ss in outs)) {
    outs[ss] = "file_" ss ".dat"
  }
  out = outs[ss]
  if (out != prev) close(prev)
  print >> out
  prev = out
}

Поместите его в скрипт типа script.awk и запустите как awk -f script.awk foo.dat.

0 голосов
/ 27 февраля 2020

Если вы используете GNU awk или у вас будет менее 12 выходных файлов, то:

awk '{print > ("file_"substr($0,1,4)".dat")}' foo.dat

в противном случае, чтобы избежать ошибки «слишком много открытых файлов»:

awk '{out="file_"substr($0,1,4)".dat"; print >> out; close(out)}' foo.dat
0 голосов
/ 27 февраля 2020
gawk -v FIELDWIDTHS="4 200" '{ print $2 > "file_" $1 ".dat" }' foo.dat

с man-страницы: если для переменной FIELDWIDTHS задан разделенный пробелами список чисел, то ожидается, что каждое поле будет иметь фиксированную ширину, и gawk разделяет запись, используя заданную ширину. Каждой ширине поля может дополнительно предшествовать разделенное двоеточиями значение, указывающее количество символов, которые следует пропустить перед началом поля. Значение FS игнорируется. Присвоение нового значения FS или FPAT отменяет использование FIELDWIDTHS.

...