Данные печати Awk на основе имен столбцов

Основная идея

for elem in "${localarrayname[@]}"; do
    localtoarray+=(   "$(printf '<%-*.*s>\n' "$p" "$p" "$elem")"   )
done

Простая переменная

Использование printf для добавления переменного количества пробелов очень просто (звездочка — это количество):

$ printf '<%-*s>'   8   hello    ; echo
<hello   >

Его можно было даже использовать для удлинения или сокращения строки до подсчета:

$ printf '<%-*.*s>'   8 8   "TestingHello"    ; echo
<TestingH>

Фиксированная замыкающая строка

Используя подстановку переменных, мы можем добавить фиксированную строку к переменной:

$ var=hello
$ printf '<%s>\n' "${var/%/"xxx"}"
<helloxxx>

Это можно расширить до массива значений:

$ var=( hello world test )
$ printf '<%s>\n' "${var[@]/%/"xxx"}"
<helloxxx>
<worldxxx>
<testxxx>

Массив значений.

Однако, чтобы добавить в массив переменное количество пробелов, нам нужно использовать цикл.
Мы можем использовать функцию для передачи списка значений (только для печати):

padarray(){ p=$1; shift
            for elem; do
                printf '<%-*.*s>\n' "$p" "$p" "$elem"
            done
          }

array=( foo qux quuux alongerstring )
padarray 15 "${array[@]}"

Он печатает:

$./script
<foo            >
<qux            >
<quuux          >
<alongerstringto>

Косвенный массив.

Чтобы передать аргументы в новый массив, используйте массив (s )имена (косвенные):

padarray(){ local p=$1;
            local -n localarrayname=$2 localtoarray=$3
            localtoarray=()
            for elem in "${localarrayname[@]}"; do
                localtoarray+=(   "$(printf '<%-*.*s>\n' "$p" "$p" "$elem")"   )
            done
          }

array=( foo qux quuux alongerstringtotest )
padarray 12 array paddedarray
printf '%s\n' "${paddedarray[@]}"

Что при выполнении выводит аналогичный результат:

$./script
<foo         >
<qux         >
<quuux       >
<alongerstrin>

Увеличено количество контактных площадок.

Добавление более надежного подсчета дополнительных символов($p):

p=${1//[!0-9]}                    # Select only numeric digits.
(( 0<=p && p<=999 )) && return 2  # Return on error. Count out of range.

Финал

Заканчиваем этим скриптом:

#!/bin/bash

padarray(){ local p=${1//[!0-9]}                       # Select only numeric digits.
            p=${p:-20}                                 # Make the default count = 20.
            local -n localarrayname=$2 localtoarray=$3 # Define indirect (local) array names.
            localtoarray=()                            # Clean the resulting array.
            (( 0<=p && p<=999 )) || return 2           # Return on error. Count out of range.
            for elem in "${localarrayname[@]}"; do
                localtoarray+=(   "$(printf '<%-*.*s>\n' "$p" "$p" "$elem")"   )
            done
          }


array=( bash bish bosh "" foo qux quuux "A Longer String To Test" )
padarray 20 array paddedarray
printf '%s\n' "${paddedarray[@]}"
1
18.05.2020, 17:28
4 ответа
$ cat tst.awk
NR==1 {
    for (i=1; i<=NF; i++) {
        name2nr[$i] = i
    }
    split("InfoId Time object TestField Request1 Request2 Request6", tmp)
    for (i=1; i in tmp; i++) {
        name = tmp[i]
        if (name in name2nr) {
            f[++nf] = name2nr[name]
        }
    }
}
{
    for (i=1; i<=nf; i++) {
        printf "%s%s", $(f[i]), (i<nf ? OFS : ORS)
    }
}

$ awk -f tst.awk file
InfoId Time object Request1 Request2
-2
28.04.2021, 23:13

Руководство по Awk Ссылка говорит:

References to nonexistent fields (i.e., fields after $NF) return the null-string. However, assigning to a nonexistent field (e.g., $(NF+2) = 5) increases the value of NF, creates any intervening fields with the null string as their value, and causes the value of $0 to be recomputed, with the fields being separated by the value of OFS.

Ваш$(f["Request6"])оценивается как $0, так как f["Request6"]возвращает нулевую строку -.

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

$ awk '
    BEGIN {
        cols = "InfoId,Time,object,Request6,Request7,Request8,Request9,Request1,Request2,Request3,Request4,Request5"
        totcols=split(cols, newf, ",")
    } 
    NR == 1 {
        for (i = 1; i <= NF; i++)  f[$i] = i
        } 

        { 
          for (i = 1; i <=totcols; i++) 
            printf "%s ", f[newf[i]] ? $f[newf[i]] : "" 
          print ""
    } ' test | column -t

Тестовый выход:

$ cat test
InfoId Time object Request1 Request2 Request3 Request4 Request5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5
123  time obj2  req1  req2 req3 req4 req5


 $  awk '
        BEGIN {
            cols = "InfoId,Time,object,Request6,Request7,Request8,Request9,Request1,Request2,Request3,Request4,Request5"
            totcols=split(cols, newf, ",")
        } 
        NR == 1 {
            for (i = 1; i <= NF; i++)  f[$i] = i
            } 

            { 
             for (i = 1; i <=totcols; i++) 
                printf "%s ", f[newf[i]] ? $f[newf[i]] : "" 
             print ""
        } ' test | column -t
    InfoId  Time  object  Request1  Request2  Request3  Request4  Request5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
    123     time  obj2    req1      req2      req3      req4      req5
0
28.04.2021, 23:13

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

listOfInputFields="InfoId Request6 Time object TestField Request4 Request66 Request2"

awk -v loif="$listOfInputFields" '
     NR==1 { for (i=1;i<=NF;i++) { names[$i]=i }
             nif=split(loif, tmp)
             for (i=1; i<=nif; i++) if(names[tmp[i]]!="") { out[++outnf]=names[tmp[i]] }
           }
           {
             FieldSep=""
             for (i=1; i<=outnf; i++) {
               printf "%s%s", FieldSep, $(out[i])
               FieldSep=OFS
           }
           printf "%s", ORS
           }' file | column -t

Который будет печатать:

InfoId Time object Request4 Request2
1:II   2:T  3:o    7:R4     5:R2

Для входа fileиз:

InfoId Time object Request1 Request2 Request3 Request4 Request5
1:II   2:T  3:o    4:R1     5:R2     6:R3     7:R4     8:R5

0
28.04.2021, 23:13

Как уже отмечалось , выражения $(f["field-name"])в вашем коде преобразуются в $0(, таким образом расширяясь до содержимого всей строки )всякий раз, когда f["field-name"]оценивается как нулевая строка, т. е. когда field-nameне существует в первой строке входного файла.

Вот альтернативный подход AWK:

BEGIN {
  nwanted = split(list,wanted,",")
}
NR == 1 {
  for ( iwanted = 1; iwanted <= nwanted; iwanted ++ )
    for ( ifield = 1; ifield <= NF; ifield++ )
      if ( wanted[iwanted] == $ifield )
        toprint[++ntoprint] = ifield
}
{
  for ( itoprint = 1; itoprint <= ntoprint; itoprint++ )
    printf( "%s%s", $toprint[itoprint], itoprint == ntoprint ? ORS : OFS )
}

Предполагая, что вы сохранили его как script, вызовите его как:

awk -v list="InfoId,Time,object,..." -f script input_data

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

Основная идея состоит в том, чтобы :в первой строке создать массив из -номеров полей печати(toprint)на основе пересечения заголовков из этой строки и массива (wanted), полученного путем разделения listпеременная.
Затем для каждой строки выведите поля, номера которых находятся в массиве to-print.

Обратите внимание, что ничего не печатается, если скрипту передается пустой список или если список не содержит ни одного из значений из первой строки входного файла.

0
28.04.2021, 23:13

Теги

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