Переместить столбец в начало по ключевому слову в заголовке

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

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.

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

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

Как я могу переместить столбец с заголовком jdb чтобы быть первым столбцом?

Некоторые ограничения:

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

Спасибо!

ОБНОВИТЬ

это блок awk который я сейчас использую:

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

но это не дает мне выхода ... Что я (думаю, я) сделал:

  1. присвойте каждому значению заголовка столбца число

  2. перебрать диапазон

    2.1, если итератор = 0 -> напечатать столбец jdb

    2.2 если итератор <= номер столбца jdb -> вывести номер столбца iterator - 1

    2.3 если итератор> номер столбца jdb -> вывести iterator номера столбца

bash awk

Всего 4 ответа


Ну, я действительно надеялся на момент "научить человека ловить рыбу", но вы все равно получаете ответы, так что ... вот как настроить чтобы сделать то, что вы сейчас хотите:

$ cat tst.awk
BEGIN { FS=OFS="	" }
NR==1 {
    cols[++numCols] = tgt
    for (fldNr=1; fldNr<=NF; fldNr++) {
        f[$fldNr] = fldNr
        if ($fldNr != tgt) {
            cols[++numCols] = $fldNr
        }
    }
}
{
    for (colNr=1; colNr<=numCols; colNr++) {
        colName = cols[colNr]
        printf "%s%s", $(f[colName]), (colNr<numCols ? OFS : ORS)
    }
}

$ awk -v tgt=jdb -f tst.awk data.rdb
jdb     date    star    texp
2400000.23551544        2013-11-22      epsInd  100.
2400000.23551544        2013-11-22      epsInd  100.
2400000.23551544        2013-11-22      epsInd  100.
2400000.23551544        2013-11-22      HD217987        900.
2400000.23551544        2013-11-22      TOI-134 900.
2400000.23551544        2013-11-22      tauCet  60.
2400000.23551544        2013-11-22      BD+01316        300.
2400000.23551544        2013-11-22      BD+01316        300.
2400000.23551544        2013-11-22      BD+01316        300.
2400000.23551544        2013-11-22      BD+01316        300.

Обратите внимание, насколько простой цикл выполняется один раз для каждой строки ввода, где вы хотите, чтобы эффективность была, потому что вся тяжелая работа по определению порядка вывода выполняется в блоке NR==1 который просто выполняется один раз для всего файла.

В этом конкретном случае, когда вы на самом деле не заботитесь о других именах столбцов, вы можете написать это более кратко и эффективно, как:

$ cat tst.awk
BEGIN { FS=OFS="	" }
NR==1 {
    numOutFlds = 1
    for (inFldNr=1; inFldNr<=NF; inFldNr++) {
        out2inFldNrs[$inFldNr == tgt ? 1 : ++numOutFlds] = inFldNr
    }
}
{
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        inFldNr = out2inFldNrs[outFldNr]
        printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
    }
}

$ awk -v tgt=jdb -f tst.awk data.rdb
jdb     date    star    texp
2400000.23551544        2013-11-22      epsInd  100.
2400000.23551544        2013-11-22      epsInd  100.
2400000.23551544        2013-11-22      epsInd  100.
2400000.23551544        2013-11-22      HD217987        900.
2400000.23551544        2013-11-22      TOI-134 900.
2400000.23551544        2013-11-22      tauCet  60.
2400000.23551544        2013-11-22      BD+01316        300.
2400000.23551544        2013-11-22      BD+01316        300.
2400000.23551544        2013-11-22      BD+01316        300.
2400000.23551544        2013-11-22      BD+01316        300.



В Perl вы можете воспользоваться библиотекой Text :: CSV_XS :

#! /usr/bin/perl
use warnings;
use strict;

use Text::CSV_XS;

open my $fh, '<', shift or die $!;

my $csv = 'Text::CSV_XS'->new({sep_char => "	"});

my $row = $csv->getline($fh);

my ($jdb) = grep $row->[$_] eq 'jdb', 0 .. $#$row;

do {
    unshift @$row, splice @$row, $jdb, 1;
    $csv->say(*STDOUT, $row);
} while $row = $csv->getline($fh);

Есть идеи?

10000