Извлечение столбцов из файла, разделенного табуляцией - PullRequest
2 голосов
/ 14 мая 2019

У меня есть файл (data.rdb) в следующем формате:

col1    col2    col3    col4    col5    col6    col7
aaa1    bbb1    ccc1    ddd1    eee1    fff1    ggg1
aaa2    bbb2    ccc2    ddd2    eee2    fff2    ggg2
aaa3    bbb3    ccc3    ddd3    eee3    fff3    ggg3

Некоторые свойства данных:

  • все столбцы разделены табуляцией
  • столбцы не имеют одинаковую ширину
  • ячейки могут иметь разную длину
  • файл будет иметь гораздо больше столбцов, чем представлено, и несколько сотен строк
  • предоставленные мною имена столбцов являются общими, настоящие имена могут быть любыми словами, без табуляции, пробелов и специальных символов.

Мне нужно извлечь некоторые столбцы по имени, используя bash, например, col1, col3 и col6, где столбцы для выбора берутся из переменной оболочки, определенной как COLUMN_LIST=$@, где $@ - параметры, передаваемые моему сценарию оболочки.Число и имя параметров могут меняться каждый раз, когда я вызываю скрипт.

Скрипт должен быть в bash, не может быть python или подобным.

Есть идеи?Я думал об использовании awk / gawk, но я не знаю, как выбрать по имени столбца.Порядок столбцов может меняться от файла к файлу.

Спасибо Jorge

ОБНОВЛЕНИЕ

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

date    star    jdb texp
2013-11-22  epsInd   2400000.23551544   100.
2013-11-22  epsInd   2400000.23551544   100.
2013-11-22  epsInd   2400000.23551544   100.
2013-11-22  HD217987 2400000.23551544   900.
2013-11-22  TOI-134  2400000.23551544   900.
2013-11-22  tauCet   2400000.23551544   60. 
2013-11-22  BD+01316 2400000.23551544   300.
2013-11-22  BD+01316 2400000.23551544   300.
2013-11-22  BD+01316 2400000.23551544   300.
2013-11-22  BD+01316 2400000.23551544   300.

, в этом случае мне будут интересны столбцы star jdbи texp

ОБНОВЛЕНИЕ 2

Я использовал код @ EdMorton, и вот результат:

date    star    jdb texp    date    star    jdb texp
2013-11-22  epsInd   2400000.23551544   100.    2013-11-22  epsInd   2400000.23551544   100.
2013-11-22  epsInd   2400000.23551544   100.    2013-11-22  epsInd   2400000.23551544   100.
2013-11-22  epsInd   2400000.23551544   100.    2013-11-22  epsInd   2400000.23551544   100.
2013-11-22  HD217987 2400000.23551544   900.    2013-11-22  HD217987 2400000.23551544   900.
2013-11-22  TOI-134  2400000.23551544   900.    2013-11-22  TOI-134  2400000.23551544   900.
2013-11-22  tauCet   2400000.23551544   60.     2013-11-22  tauCet   2400000.23551544   60. 
2013-11-22  BD+01316 2400000.23551544   300.    2013-11-22  BD+01316 2400000.23551544   300.
2013-11-22  BD+01316 2400000.23551544   300.    2013-11-22  BD+01316 2400000.23551544   300.
2013-11-22  BD+01316 2400000.23551544   300.    2013-11-22  BD+01316 2400000.23551544   300.
2013-11-22  BD+01316 2400000.23551544   300.    2013-11-22  BD+01316 2400000.23551544   300.

ОБНОВЛЕНИЕ3

В итоге я использовал версию Edmorton awk - в основном для гибкости вывода - но с модификацией, которую я не хочу выводить неверными столбцами:

BEGIN {
    numCols = split(column_list,cols)
    OFS="\t"
}
{ sub(/\r$/,"") }
NR==1 {
    for (fldNr=1; fldNr<=NF; fldNr++) {
        f[$fldNr] = fldNr
    }
}
{
    for (colNr=1; colNr<=numCols; colNr++) {
        colName = cols[colNr]
        colVal  = (colName in f ? $(f[colName]) : "")
        printf "%s%s", colVal, (colNr<numCols ? OFS : ORS)
    }
}

Основная проблема, которую я получил, состояла в том, что строка заголовка не была разделена табуляцией и, как таковая, разбивка столбцов не работала.Простой способ обнаружить символы табуляции / не-табуляции:

tr $'\t' '#' < data.rdb | head -2

, которые были указаны в одном из моих тестовых файлов:

date    star    jdb texp
2013-11-22#epsInd#2400000.23551544#100.

Ответы [ 3 ]

3 голосов
/ 14 мая 2019

Порядок столбцов может меняться от файла к файлу.

Вы можете использовать этот подход, используя awk, который принимает в качестве входных данных имена столбцов, разделенных пробелами, и сначала преобразует их в номер столбца.обработав первую запись.Как только требуемые номера столбцов будут получены, мы просто печатаем их со следующей строки.

awk -v cols='col1 col3 col6' 'BEGIN {
   FS=OFS="\t"
   n = split(cols, a, " ")
   for (i=1; i <= n; i++)
      c[a[i]]
}
{
   sub(/\r$/, "")
}
NR == 1 {
   for (i=1; i<=NF; i++)
      if ($i in c)
         hdr[i]
}
{
   for (i=1; i<=NF; i++)
      if (i in hdr)
         s = sprintf(s "%s%s", OFS, $i)
   sub(OFS, "", s)
   print s
   s =""
} ' file | column -t

star      jdb               texp
epsInd    2400000.23551544  100.
epsInd    2400000.23551544  100.
epsInd    2400000.23551544  100.
HD217987  2400000.23551544  900.
TOI-134   2400000.23551544  900.
tauCet    2400000.23551544  60.
BD+01316  2400000.23551544  300.
BD+01316  2400000.23551544  300.
BD+01316  2400000.23551544  300.
BD+01316  2400000.23551544  300.

PS: добавлено column -t для форматирования вывода в табличном формате.

1 голос
/ 14 мая 2019

Лучший способ справиться с этим - создать массив (f[] ниже), который отображает строки заголовка столбца (т.е. имена полей) на номера полей при чтении строки заголовка, а затем просто обращается к полям по ихимена с тех пор.

Обновлено для защиты от вызывающей стороны, запрашивающей имя столбца, который не существует, и от окончаний строки DOS:

$ cat tst.awk
BEGIN {
    numCols = split(column_list,cols)
    FS=OFS="\t"
}
{ sub(/\r$/,"") }
NR==1 {
    for (fldNr=1; fldNr<=NF; fldNr++) {
        f[$fldNr] = fldNr
    }
}
{
    for (colNr=1; colNr<=numCols; colNr++) {
        colName = cols[colNr]
        colVal  = (colName in f ? $(f[colName]) : (NR>1 ? "N/A" : colName))
        printf "%s%s", colVal, (colNr<numCols ? OFS : ORS)
    }
}

$ awk -v column_list='col1 col3 col6' -f tst.awk data.rdb
col1    col3    col6
aaa1    ccc1    fff1
aaa2    ccc2    fff2
aaa3    ccc3    fff3

$ awk -v column_list='col1 col3 col6 bob' -f tst.awk data.rdb
col1    col3    col6    bob
aaa1    ccc1    fff1    N/A
aaa2    ccc2    fff2    N/A
aaa3    ccc3    fff3    N/A

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

$ awk -v column_list='col5 col2 col4' -f tst.awk data.rdb
col5    col2    col4
eee1    bbb1    ddd1
eee2    bbb2    ddd2
eee3    bbb3    ddd3
0 голосов
/ 14 мая 2019

Вы можете сделать это с coreutils. Предполагая, что у вас есть файл callef cols, содержащий нужные столбцы, например ::

col2
col3
col6

Вы можете извлечь номера столбцов следующим образом:

head -n1 infile | tr '\t' '\n' | grep -nf cols | cut -d: -f1 | paste -sd,

Выход:

2,3,6

Передайте это cut, например .:

cut -f $(head -n1 infile | tr '\t' '\n' | grep -nf cols | cut -d: -f1 | paste -sd,) infile

Выход:

col2    col3    col6
bbb1    ccc1    fff1
bbb2    ccc2    fff2
bbb3    ccc3    fff3
...