У меня есть файл (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)
}
}
но это не дает мне выхода ... Что я (думаю, я) сделал:
присвойте каждому значению заголовка столбца число
перебрать диапазон
2.1, если итератор = 0 -> напечатать столбец jdb
2.2 если итератор <= номер столбца jdb -> вывести номер столбца iterator - 1
2.3 если итератор> номер столбца jdb -> вывести iterator
номера столбца
Всего 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.
Это немного многословно, но это делает работу:
awk 'NR==1{for(i=1;i<=NF;i++){if ($i=="jdb") break;}} {printf "%s ",$i; for (j=1;j<=NF;j++){if (i!=j){printf j==NF||(j==NF-1&&j+1==i)?"%s
":"%s ", $j}}}' yourfile.txt
Отличное предложение Пера Мортона. Вот скрипт с правильными пробелами, отступами и переводами строки:
NR == 1 {
for (i = 1; i <= NF; i++) {
if ($i == "jdb") {
break
}
}
}
{
printf "%s ", $i
for (j = 1; j <= NF; j++) {
if (i != j) {
printf (j == NF || j == NF - 1 && j + 1 == i ? "%s
" : "%s "), $j
}
}
}
Вы можете вставить это в свой собственный файл (скажем ... script.awk) и затем вызвать его: awk -f script.awk yourfile.txt
Итак, задача двоякая:
Так:
# our testing input file
cat <<EOF >file
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.
EOF
# my copy+paste messed up tabs with spaces, fix it
sed 's/[[:space:]]+/ /g' -i file
# first we need header count.
# I could remove all characters except tabs and use wc -c
# but was lazy, this will not affect performance anyway
hdrcnt=$(
head -n1 file |
tr ' ' '
' |
wc -l
)
# get the column number that has jdb
# I get the first line
# substitute tab with newlines
# and get the line number with "jdb"
num=$(
head -n1 file |
tr ' ' '
' |
grep -n jdb |
cut -d: -f1
)
# ten I generate the awk script
# so it's like '{print $num, $1, $2 ... except $num ... $hdrcnt }'
awkarg='{print $'"$num"', '"$(
seq $hdrcnt |
grep -v "$num" |
sed 's/(.*)/$1, /' |
sed '$s/, //' |
tr -d '
'
)"'}'
# finally run awk
awk -vIFS=' ' -vOFS=' ' "$awkarg" file
В 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);