Другим несовершенным способом сделать это в 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
Ловушка получает полную командную строку перед расширением переменных или шаблонов имен файлов, поэтому *
без кавычек работает. (Но использование переменных оболочки в вычислениях не работает.)
Однако скобки без кавычек по-прежнему вызывают синтаксическую ошибку, так что это тоже не идеально.
(Я взял регулярное выражение выше из ответа Стефана .)
$ 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
Успение (с):
Пояснение(краткое описание):
Заголовок печатается в явном виде, как показано в OP.
Массив @A
заново заполняется для каждой строки путем его очистки во время печати с помощью функции splice
.
Массив @F
хранит поля входной записи $_
, индексированные нулями.
Первый элемент (, а не нулевой элемент )массива @A
, смещается из начала массива @F
. Обратите внимание, что массив @F
генерируется perl
каждый раз, когда считывается новая запись. Это похоже на поля $1, $2, $3,..., $NF
в awk
.
Остальная часть массива @A
представляет собой двоичный (0|1)+1=>(1|2)
индекс элемента, в котором накапливаются соответствующие суммы из массива @F
.
Надеюсь, понятно.
Использование довольно многословных имен переменных и промежуточных переменных, чтобы помочь вам понять, что происходит (, вместо того, чтобы комментировать каждую строку или добавлять объяснение позже):
$ 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
Использование 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... )
}