Объединить столбцы в одном файле с помощью сопоставления заголовков

Другим несовершенным способом сделать это в Bash было бы использование ловушки DEBUG, которая срабатывает при каждой команде. С установленным extdebugобработчик trap может предотвратить запуск основной команды, поэтому вы не получите ошибки «команда не найдена».

$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
    local re='^[ (]*-?[0-9]'
    if [[ $BASH_COMMAND =~ $re ]]; then
        echo "$BASH_COMMAND" | bc -l
        return 1
    fi
}
trap debug_calc DEBUG
$../bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789

Ловушка получает полную командную строку перед расширением переменных или шаблонов имен файлов, поэтому *без кавычек работает. (Но использование переменных оболочки в вычислениях не работает.)

Однако скобки без кавычек по-прежнему вызывают синтаксическую ошибку, так что это тоже не идеально.

(Я взял регулярное выражение выше из ответа Стефана .)

1
25.02.2020, 15:42
3 ответа
$ perl -lane '$,="\t";
   print(qw/var x y/),next if $. == 1;
   push @A, shift @F;
   $A[$|--+1] += $_ for @F;
   print splice @A;
' file

Результаты:

var x   y
a   2   2
b   2   2
c   1   1

Успение (с):

  • Количество полей нечетное.
  • Поле #2 и далее являются числовыми.

Пояснение(краткое описание):

  • Заголовок печатается в явном виде, как показано в OP.

  • Массив @Aзаново заполняется для каждой строки путем его очистки во время печати с помощью функции splice.

  • Массив @Fхранит поля входной записи $_, индексированные нулями.

  • Первый элемент (, а не нулевой элемент )массива @A, смещается из начала массива @F. Обратите внимание, что массив @Fгенерируется perlкаждый раз, когда считывается новая запись. Это похоже на поля $1, $2, $3,..., $NFв awk.

  • Остальная часть массива @Aпредставляет собой двоичный (0|1)+1=>(1|2)индекс элемента, в котором накапливаются соответствующие суммы из массива @F.

Надеюсь, понятно.

0
28.04.2021, 23:22

Использование довольно многословных имен переменных и промежуточных переменных, чтобы помочь вам понять, что происходит (, вместо того, чтобы комментировать каждую строку или добавлять объяснение позже):

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==1 {
    for (inFldNr=2; inFldNr<=NF; inFldNr++) {
        fldName = $inFldNr
        if ( !(fldName in fldName2outFldNr) ) {
            outFldNr2name[++numOutFlds] = fldName
            fldName2outFldNr[fldName] = numOutFlds
        }
        outFldNr = fldName2outFldNr[fldName]
        out2inFldNrs[outFldNr,++numInFlds[outFldNr]] = inFldNr
    }

    printf "%s%s", $1, OFS
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        outFldName = outFldNr2name[outFldNr]
        printf "%s%s", outFldName, (outFldNr<numOutFlds ? OFS : ORS)
    }
    next
}
{
    printf "%s%s", $1, OFS
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        sum = 0
        for (inFldIdx=1; inFldIdx<=numInFlds[outFldNr]; inFldIdx++) {
            inFldNr = out2inFldNrs[outFldNr,inFldIdx]
            sum += $inFldNr
        }
        printf "%s%s", sum, (outFldNr<numOutFlds ? OFS : ORS)
    }
}

.

$ awk -f tst.awk file
var     x       y
a       2       2
b       2       2
c       1       1
0
28.04.2021, 23:22

Использование GNUdatamash:

<file datamash -W transpose | 
  datamash -W --headers -s -g1 sum 2-4 |
  datamash --output-delimiter=' ' transpose
  • Транспонировать файл, используя пробелы в качестве разделителя(-W)
  • Группировка по первому полю (-g1), сортировка перед группировкой(-s)и суммирование значений по полям 2 -4. Первая строка ввода обрабатывается как заголовок столбца и печатается в выводе(--headers)
  • Транспонируйте результат и используйте пробел в качестве разделителя вместо табуляции.

Выход:

GroupBy(var) x y
sum(a) 2 2
sum(b) 2 2
sum(c) 1 1

Pretty -распечатать результат с помощьюsed:

<file datamash -W transpose | 
  datamash -W --headers -s -g1 sum 2-4 |
  datamash --output-delimiter=' ' transpose | 
  sed 's/^[^(]*(\([^)]*\))/\1/'

Выход:

var x y
a 2 2
b 2 2
c 1 1

Вы можете использовать cut, чтобы удалить первые девять столбцов из вашего входного файла и добавить их обратно в результат с помощьюpaste:

paste -d ' ' <(cut -d' ' -f-9 file) <(cut -d' ' -f10- file | datamash... )

Дополнительная строка заголовка может быть напечатана с помощью head -n1и пропущена с помощьюtail -n+2:

{
  head -n1 file 
  paste -d ' ' <(tail -n+2 file | cut -d' ' -f-9) <(tail -n+2 file | cut -d' ' -f10- | datamash... )
} 
0
28.04.2021, 23:22

Теги

Похожие вопросы