Сделайте некоторые вычисления в текстовом файле в оболочке - PullRequest
0 голосов
/ 09 марта 2020

У меня есть текстовый файл:

$cat ifile.txt
this is a text file
assign x to 9 and y to 10.0702
define f(x)=x+y

Я хотел бы отключить исходную строку и разделить значение x на 2 и умножить значение y на 2

Мой желаемый вывод

$cat ofile.txt
this is a text file
#assign x to 9 and y to 10.0702    
assign x to 5 and y to 20.1404
define f(x)=x+y

Здесь 5 is calculate from 9/2 and rounded to the next integer и 20.14 is calculated from 10.07x2 and not rounded

Я думаю о следующем пути, но не могу написать сценарий.

if [ line contains "assign x to" ]; then new_x_value=[next word]/2
if [ line contains "and y to" ]; then new_y_value=[next word]x2

if [ line contains "assign x to" ]; 
   then disable it and add a line "assign x to new_x_value and y to new_y_value"

Ответы [ 3 ]

1 голос
/ 09 марта 2020

Один в awk:

awk '
/assign/ {                                          # when assign met in record
    for(i=1;i<=NF-1;i++)                            # iterate from the beginning
        if($i=="to" && $(i-1)=="x")                 # if to x
            $(i+1)=((v=$(i+1)/2)>(u=int(v))?u+1:u)  # ceil of division
        else if($i=="to" && $(i-1)=="y")            # if to y
            $(i+1)*=2                               # multiply by 2
}1' file                                            # output

Выход:

this is a text file
assign x to 5 and y to 20.1404
define f(x)=x+y

Проверка работоспособности расчета потолка, оставленная в качестве домашней работы ...

1 голос
/ 09 марта 2020
awk '{if(match($0,/^assign/)){b=$0;split($0,a," ");a[8]=a[8]/2;a[4]=a[4]/2; for (x in a) {c = a[x] " " c; $0 = "#" b "\n" c } } { print }}'

Демо:

:>awk  ' { if(match ($0, /^assign/)) {b=$0;split($0,a," ");a[8]=a[8]/2; a[4]=a[4]/2; for (x in a) {c = a[x] " " c; $0 = "#" b "\n" c } } { print }}' <ifile
this is a text file
#assign x to 9 and y to 10.0702
to x assign 5.0351 to y and 4.5
define f(x)=x+y
:>

Объяснение:

awk  ' { 
if(match ($0, /^assign/))  <--- $0 is whole input record. ^ is start of line. 
                                 We are checking if record is starting with "assign"

    {b=$0; <-- Assign input value to variable b 
     split($0,a," "); <-- Create a array by splitting input record with space as separator 
     a[8]=a[8]/2; a[4]=a[4]/2; <--  Divide value stored in 8 and 4 index
     for (x in a)  <-- Loop for getting all values of array 
          {c = a[x] " " c; <-- Create a variable by concatenating values of a   
     $0 = "#" b "\n" c <-- Update value of current record. "\n" new line operator 
    } } 
{ print }}' 
1 голос
/ 09 марта 2020

Не могли бы вы попробовать следующее:

#!/bin/bash

pat="(assign x to )([[:digit:]]+)( and y to )([[:digit:].]+)"
while IFS= read -r line; do
    if [[ $line =~ $pat ]]; then
        echo "#$line"
        x2=$(echo "(${BASH_REMATCH[2]} + 1) / 2" | bc)
        y2=$(echo "${BASH_REMATCH[4]} * 2" | bc)
        echo "${BASH_REMATCH[1]}$x2${BASH_REMATCH[3]}$y2"
    else
        echo "$line"
    fi
done < ifile.txt > ofile.txt

Вывод:

this is a text file
#assign x to 9 and y to 10.0702
assign x to 5 and y to 20.1404
define f(x)=x+y
  • Регулярному выражению (assign x to )([[:digit:]]+)( and y to )([[:digit:].]+) соответствует буквенная строка, за которой следуют цифры, а затем буквенная строка, за которой следуют цифры, включая десятичную точку.
  • Команда bc (${BASH_REMATCH[2]} + 1) / 2 рассчитывает значение ceiling ввода, деленное на 2.
  • Следующая команда bc ${BASH_REMATCH[4]} * 2 умножает входные данные на 2.

Я выбрал bash только потому, что он поддерживает back reference в регулярных выражениях и его легче анализировать и повторно использовать входные параметры, чем awk , Как часто указывалось, bash не подходит для обработки больших файлов по причине производительности. Если вы планируете использовать большие / несколько файлов, рекомендуется использовать другие языки, такие как perl.

С perl вы можете сказать:

perl -pe 's|(assign x to )([0-9]+)( and y to )([0-9.]+)|
    "#$&\n" . $1 . int(($2 + 1) / 2) . $3 . $4 * 2|ge' ifile.txt > ofile.txt

[РЕДАКТИРОВАТЬ ]

Если ваш ifile.txt выглядит следующим образом:

this is a text file
assign x to   9 and y to   10.0702   45
define f(x)=x+y
  • Перед числами находится более одного пробела.
  • Существует еще одно значение в конце (после пробелов).

Затем попробуйте следующее:

pat="(assign x to +)([[:digit:]]+)( and y to +)([[:digit:].]+)( +)([[:digit:].]+)"
while IFS= read -r line; do
    if [[ $line =~ $pat ]]; then
        echo "#$line"
        x2=$(echo "(${BASH_REMATCH[2]} + 1) / 2" | bc)
        y2=$(echo "${BASH_REMATCH[4]} * 2" | bc)
        y3=$(echo "${BASH_REMATCH[6]} * 2" | bc)
        echo "${BASH_REMATCH[1]}$x2${BASH_REMATCH[3]}$y2${BASH_REMATCH[5]}$y3"
    else
        echo "$line"
    fi
done < ifile.txt > ofile.txt

Результат:

this is a text file
#assign x to   9 and y to   10.0702   45
assign x to   5 and y to   20.1404   90
define f(x)=x+y

Знак плюс после пробела является regex quantifier и определяет количество повторений. В этом случае он соответствует одному или нескольким пробелам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...