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[@]}"
$ 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
Руководство по 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
Если вам нужна возможность печатать любое поле в любом порядке, игнорируя (не выводить )несуществующие -поля, вы можете использовать:
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
Как уже отмечалось , выражения $(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
.
Обратите внимание, что ничего не печатается, если скрипту передается пустой список или если список не содержит ни одного из значений из первой строки входного файла.